From 5e2809dd5f238ec5f347a87633b99a5020b36b7f Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 23 Feb 2025 15:03:46 +0100 Subject: [PATCH 01/13] use constants to shorten variable access --- jsEngine/settings/Settings.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index b2a50c6..75f466f 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -20,15 +20,17 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { } display(): void { - this.containerEl.empty(); + const containerEl = this.containerEl; + const settings = this.plugin.settings; + containerEl.empty(); - if (!this.plugin.settings) { + if (!settings) { return; } // this.containerEl.createEl('p', { text: 'Currently Empty, but there will be stuff here later.' }); - new Setting(this.containerEl).setName('Startup scripts').addButton(button => { + new Setting(containerEl).setName('Startup scripts').addButton(button => { button.setButtonText('Manage').onClick(() => { new StartupScriptsModal(this.plugin).open(); }); From 50ce01a836756b5ab8c42c6b76282c315982e40f Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 23 Feb 2025 18:55:53 +0100 Subject: [PATCH 02/13] remove old startup-scripts modal --- jsEngine/settings/Settings.ts | 9 --- jsEngine/settings/StartupScriptModal.ts | 37 ---------- jsEngine/settings/StartupScripts.svelte | 91 ------------------------- 3 files changed, 137 deletions(-) delete mode 100644 jsEngine/settings/StartupScriptModal.ts delete mode 100644 jsEngine/settings/StartupScripts.svelte diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 75f466f..61f64a6 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -1,5 +1,4 @@ import type JsEnginePlugin from 'jsEngine/main'; -import { StartupScriptsModal } from 'jsEngine/settings/StartupScriptModal'; import type { App } from 'obsidian'; import { PluginSettingTab, Setting } from 'obsidian'; @@ -27,13 +26,5 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { if (!settings) { return; } - - // this.containerEl.createEl('p', { text: 'Currently Empty, but there will be stuff here later.' }); - - new Setting(containerEl).setName('Startup scripts').addButton(button => { - button.setButtonText('Manage').onClick(() => { - new StartupScriptsModal(this.plugin).open(); - }); - }); } } diff --git a/jsEngine/settings/StartupScriptModal.ts b/jsEngine/settings/StartupScriptModal.ts deleted file mode 100644 index 6e9357d..0000000 --- a/jsEngine/settings/StartupScriptModal.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type JsEnginePlugin from 'jsEngine/main'; -import StartupScripts from 'jsEngine/settings/StartupScripts.svelte'; -import { Modal } from 'obsidian'; -import { mount, unmount } from 'svelte'; - -export class StartupScriptsModal extends Modal { - plugin: JsEnginePlugin; - component?: ReturnType; - - constructor(plugin: JsEnginePlugin) { - super(plugin.app); - this.plugin = plugin; - } - - onOpen(): void { - this.contentEl.empty(); - - this.component = mount(StartupScripts, { - target: this.contentEl, - props: { - modal: this, - startupScripts: this.plugin.settings.startupScripts ?? [], - }, - }); - } - - onClose(): void { - if (this.component) { - unmount(this.component); - } - } - - save(startupScripts: string[]): void { - this.plugin.settings.startupScripts = startupScripts; - void this.plugin.saveSettings(); - } -} diff --git a/jsEngine/settings/StartupScripts.svelte b/jsEngine/settings/StartupScripts.svelte deleted file mode 100644 index fc067d5..0000000 --- a/jsEngine/settings/StartupScripts.svelte +++ /dev/null @@ -1,91 +0,0 @@ - - -
-
- {#each startupScripts as script, i} -
- {script} - -
- {/each} -
- - - - - - - -
From c7f40ad1a7b62fe3383e4a2111702cafbab4aa93 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 23 Feb 2025 15:23:54 +0100 Subject: [PATCH 03/13] add definition of obsidian internal function `openWithDefaultApp` --- jsEngine/obsidian-ex.d.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jsEngine/obsidian-ex.d.ts b/jsEngine/obsidian-ex.d.ts index 907e788..efa5e52 100644 --- a/jsEngine/obsidian-ex.d.ts +++ b/jsEngine/obsidian-ex.d.ts @@ -8,6 +8,12 @@ declare module 'obsidian' { plugins: Record; getPlugin: (plugin: string) => Plugin; }; + + /** + * Open a file or folder with the systems default app for it. + * @param path a normalized path to open + */ + openWithDefaultApp(path: string): void; } interface MenuItem { From 0cb65a71b399501aaf7b7c76ec580527def6bb76 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Wed, 26 Feb 2025 15:39:35 +0100 Subject: [PATCH 04/13] add setting `startupScriptsDirectory` --- jsEngine/settings/Settings.ts | 36 +++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 61f64a6..3d8cc13 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -1,12 +1,14 @@ import type JsEnginePlugin from 'jsEngine/main'; import type { App } from 'obsidian'; -import { PluginSettingTab, Setting } from 'obsidian'; +import { normalizePath, PluginSettingTab, Setting } from 'obsidian'; export interface JsEnginePluginSettings { - startupScripts?: string[]; + startupScriptsDirectory: string | undefined; + startupScripts: string[]; } export const JS_ENGINE_DEFAULT_SETTINGS: JsEnginePluginSettings = { + startupScriptsDirectory: undefined, startupScripts: [], }; @@ -26,5 +28,35 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { if (!settings) { return; } + + new Setting(containerEl) + .setName('JS snippets (loaded on startup)') + .setHeading() + .addExtraButton(el => { + el.setTooltip('Open snippets folder') + .setIcon('folder-open') + .onClick(async () => await this.openStartupScriptsDirectory()); + }); + + new Setting(containerEl) + .setName('Custom JS Snippets Folder') + .setDesc('The folder to search for JavaScript files to load') + .addText(el => { + el.setPlaceholder('Folder') + .setValue(settings.startupScriptsDirectory ?? '') + .onChange(async val => { + settings.startupScriptsDirectory = val ? normalizePath(val) : undefined; + await this.plugin.saveSettings(); + }); + }); + } + + async openStartupScriptsDirectory(): Promise { + const vault = this.app.vault; + const directory = this.plugin.settings.startupScriptsDirectory ?? '/'; + if (!(await vault.adapter.exists(directory))) { + await vault.createFolder(directory); + } + this.app.openWithDefaultApp(directory); } } From 862d1533b63ce758cd074fd213b174bd1c4e82ae Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Wed, 26 Feb 2025 16:03:08 +0100 Subject: [PATCH 05/13] startupScript settings inspired by css-snippets --- jsEngine/settings/Settings.ts | 40 ++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 3d8cc13..582110e 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -1,6 +1,6 @@ import type JsEnginePlugin from 'jsEngine/main'; import type { App } from 'obsidian'; -import { normalizePath, PluginSettingTab, Setting } from 'obsidian'; +import { normalizePath, PluginSettingTab, Setting, TFile, TFolder } from 'obsidian'; export interface JsEnginePluginSettings { startupScriptsDirectory: string | undefined; @@ -32,6 +32,11 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { new Setting(containerEl) .setName('JS snippets (loaded on startup)') .setHeading() + .addExtraButton(el => { + el.setTooltip('Reload snippets') + .setIcon('refresh-cw') + .onClick(() => this.display()); + }) .addExtraButton(el => { el.setTooltip('Open snippets folder') .setIcon('folder-open') @@ -49,6 +54,23 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }); }); + + const startupScriptsDirectory = this.app.vault.getFolderByPath(settings.startupScriptsDirectory ?? '/'); + let startupScripts: TFile[] = []; + if (startupScriptsDirectory != null) { + startupScripts = this.listJSfilesInDirectory(startupScriptsDirectory); + } + if (startupScripts.length == 0) { + new Setting(containerEl).setName('No JS snippets found').setDesc(`JS snippets are stored in "vault/${settings.startupScriptsDirectory ?? ''}"`); + } + for (const file of startupScripts) { + new Setting(containerEl) + .setName(file.basename) + .setDesc(`Apply JS snippet from "vault/${file.path}"`) + .addToggle(el => { + el.setValue(settings.startupScripts.contains(file.path)).onChange(async val => this.toggleStartupScript(file, val)); + }); + } } async openStartupScriptsDirectory(): Promise { @@ -59,4 +81,20 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { } this.app.openWithDefaultApp(directory); } + + listJSfilesInDirectory(directory: TFolder): TFile[] { + const files = directory.children.filter(el => el instanceof TFile); + const folders = directory.children.filter(el => el instanceof TFolder); + return files.filter(f => f.extension == 'js').concat(folders.flatMap(dir => this.listJSfilesInDirectory(dir))); + } + + async toggleStartupScript(file: TFile, enable: boolean): Promise { + const settings = this.plugin.settings; + if (enable) { + settings.startupScripts.push(file.path); + } else { + settings.startupScripts.remove(file.path); + } + await this.plugin.saveSettings(); + } } From 654c3edc58ff78cfcf11d3a810c8fed23d20bf39 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Wed, 26 Feb 2025 16:49:21 +0100 Subject: [PATCH 06/13] display preexisting scripts as separate list --- jsEngine/settings/Settings.ts | 35 ++++++++++++++++++++++++++++++++++- styles.css | 9 +++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 582110e..dc98820 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -22,6 +22,7 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { display(): void { const containerEl = this.containerEl; + const vault = this.app.vault; const settings = this.plugin.settings; containerEl.empty(); @@ -55,7 +56,7 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { }); }); - const startupScriptsDirectory = this.app.vault.getFolderByPath(settings.startupScriptsDirectory ?? '/'); + const startupScriptsDirectory = vault.getFolderByPath(settings.startupScriptsDirectory ?? '/'); let startupScripts: TFile[] = []; if (startupScriptsDirectory != null) { startupScripts = this.listJSfilesInDirectory(startupScriptsDirectory); @@ -71,6 +72,23 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { el.setValue(settings.startupScripts.contains(file.path)).onChange(async val => this.toggleStartupScript(file, val)); }); } + + const oldScripts = settings.startupScripts + .map(file => vault.getFileByPath(file)!) + .filter(file => !file.parent?.path.startsWith(settings.startupScriptsDirectory ?? '')); + if (oldScripts.length > 0) { + this.containerEl.createEl('div', { cls: 'callout js-engine-settings-warning', text: 'These scripts are not in the Snippets Folder' }); + } + for (const file of oldScripts) { + new Setting(containerEl) + .setName(file.basename) + .setDesc(`Apply JS snippet from "vault/${file.path}"`) + .addExtraButton(el => { + el.setTooltip('Move to current Snippets Folder') + .setIcon('archive-restore') + .onClick(async () => await this.moveStartupScriptToNewDirectory(file)); + }); + } } async openStartupScriptsDirectory(): Promise { @@ -97,4 +115,19 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { } await this.plugin.saveSettings(); } + + async moveStartupScriptToNewDirectory(script: TFile): Promise { + const settings = this.plugin.settings; + const vault = this.app.vault; + const startupScriptsDirectory = settings.startupScriptsDirectory ?? '/'; + const newPath = startupScriptsDirectory.concat('/', script.name); + if ((await vault.adapter.exists(startupScriptsDirectory)) == false) { + await vault.createFolder(startupScriptsDirectory); + } + settings.startupScripts.remove(script.path); + await this.app.vault.rename(script, newPath); + settings.startupScripts.push(newPath); + await this.plugin.saveSettings(); + this.display(); + } } diff --git a/styles.css b/styles.css index 545aebb..22e9978 100644 --- a/styles.css +++ b/styles.css @@ -175,3 +175,12 @@ If your plugin does not need CSS, delete this file. left: 0; right: 0; } + +.js-engine-settings-warning { + --callout-color: var(--callout-warning); + color: rgb(var(--callout-color)); + margin-bottom: 0; + padding-block: 0.25em; + border-bottom-left-radius: unset; + border-bottom-right-radius: unset; +} From 3775bd7e75b3d70976673f2d16cfa9054af1aae7 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Thu, 27 Feb 2025 19:58:14 +0100 Subject: [PATCH 07/13] use `focusout` event to handle new folder --- jsEngine/settings/Settings.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index dc98820..3390516 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -50,9 +50,11 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { .addText(el => { el.setPlaceholder('Folder') .setValue(settings.startupScriptsDirectory ?? '') - .onChange(async val => { - settings.startupScriptsDirectory = val ? normalizePath(val) : undefined; - await this.plugin.saveSettings(); + .inputEl.addEventListener('focusout', ev => { + const target = ev.currentTarget as HTMLInputElement; + settings.startupScriptsDirectory = target.value ? normalizePath(target.value) : undefined; + void this.plugin.saveSettings(); + this.display(); }); }); From 5ab8d03bf4dc521619332805d030330beac47a15 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 1 Jun 2025 21:52:11 +0200 Subject: [PATCH 08/13] execute the startupscript when it gets enabled --- jsEngine/settings/Settings.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 3390516..181454b 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -112,6 +112,7 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { const settings = this.plugin.settings; if (enable) { settings.startupScripts.push(file.path); + void this.plugin.api.internal.executeFileSimple(file.path); } else { settings.startupScripts.remove(file.path); } From c813ae2dcd682c409f8881cf8349d3ddda67e308 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Thu, 27 Feb 2025 19:59:40 +0100 Subject: [PATCH 09/13] make old startupscripts also togglable --- jsEngine/settings/Settings.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 181454b..88a5450 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -89,6 +89,9 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { el.setTooltip('Move to current Snippets Folder') .setIcon('archive-restore') .onClick(async () => await this.moveStartupScriptToNewDirectory(file)); + }) + .addToggle(el => { + el.setValue(settings.startupScripts.contains(file.path)).onChange(async val => this.toggleStartupScript(file, val)); }); } } From 314b255252d1b632503a2ef8bc12af24060988ba Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 1 Jun 2025 22:13:15 +0200 Subject: [PATCH 10/13] remove no longer needed nullish check --- jsEngine/api/Internal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsEngine/api/Internal.ts b/jsEngine/api/Internal.ts index 6ac8f63..d5d4ff8 100644 --- a/jsEngine/api/Internal.ts +++ b/jsEngine/api/Internal.ts @@ -264,7 +264,7 @@ export class InternalAPI { * Runs all startup scripts defined in the plugins settings. */ public async executeStartupScripts(): Promise { - for (const script of this.apiInstance.plugin.settings.startupScripts ?? []) { + for (const script of this.apiInstance.plugin.settings.startupScripts) { await this.executeFileSimple(script); } } From 978336a6a3ce1d5be8a99c95259be987b8df1d98 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 1 Jun 2025 23:01:52 +0200 Subject: [PATCH 11/13] use default value object when setting is unset by user --- jsEngine/settings/Settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 88a5450..1f24bc1 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -52,7 +52,7 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { .setValue(settings.startupScriptsDirectory ?? '') .inputEl.addEventListener('focusout', ev => { const target = ev.currentTarget as HTMLInputElement; - settings.startupScriptsDirectory = target.value ? normalizePath(target.value) : undefined; + settings.startupScriptsDirectory = target.value ? normalizePath(target.value) : JS_ENGINE_DEFAULT_SETTINGS.startupScriptsDirectory; void this.plugin.saveSettings(); this.display(); }); From 717eb53789e46099448bb7104a6226138250066c Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 1 Jun 2025 23:03:33 +0200 Subject: [PATCH 12/13] move loop into if-scopes to clarify when they have an effect --- jsEngine/settings/Settings.ts | 43 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 1f24bc1..99c10d8 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -65,14 +65,15 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { } if (startupScripts.length == 0) { new Setting(containerEl).setName('No JS snippets found').setDesc(`JS snippets are stored in "vault/${settings.startupScriptsDirectory ?? ''}"`); - } - for (const file of startupScripts) { - new Setting(containerEl) - .setName(file.basename) - .setDesc(`Apply JS snippet from "vault/${file.path}"`) - .addToggle(el => { - el.setValue(settings.startupScripts.contains(file.path)).onChange(async val => this.toggleStartupScript(file, val)); - }); + } else { + for (const file of startupScripts) { + new Setting(containerEl) + .setName(file.basename) + .setDesc(`Apply JS snippet from "vault/${file.path}"`) + .addToggle(el => { + el.setValue(settings.startupScripts.contains(file.path)).onChange(async val => this.toggleStartupScript(file, val)); + }); + } } const oldScripts = settings.startupScripts @@ -80,19 +81,19 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { .filter(file => !file.parent?.path.startsWith(settings.startupScriptsDirectory ?? '')); if (oldScripts.length > 0) { this.containerEl.createEl('div', { cls: 'callout js-engine-settings-warning', text: 'These scripts are not in the Snippets Folder' }); - } - for (const file of oldScripts) { - new Setting(containerEl) - .setName(file.basename) - .setDesc(`Apply JS snippet from "vault/${file.path}"`) - .addExtraButton(el => { - el.setTooltip('Move to current Snippets Folder') - .setIcon('archive-restore') - .onClick(async () => await this.moveStartupScriptToNewDirectory(file)); - }) - .addToggle(el => { - el.setValue(settings.startupScripts.contains(file.path)).onChange(async val => this.toggleStartupScript(file, val)); - }); + for (const file of oldScripts) { + new Setting(containerEl) + .setName(file.basename) + .setDesc(`Apply JS snippet from "vault/${file.path}"`) + .addExtraButton(el => { + el.setTooltip('Move to current Snippets Folder') + .setIcon('archive-restore') + .onClick(async () => await this.moveStartupScriptToNewDirectory(file)); + }) + .addToggle(el => { + el.setValue(settings.startupScripts.contains(file.path)).onChange(async val => this.toggleStartupScript(file, val)); + }); + } } } From ee870bead4f9c9838ad0727c3e110f089bfc8219 Mon Sep 17 00:00:00 2001 From: Jan Wille Date: Sun, 1 Jun 2025 23:03:56 +0200 Subject: [PATCH 13/13] properly search the parent-folder-tree recursivly --- jsEngine/settings/Settings.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/jsEngine/settings/Settings.ts b/jsEngine/settings/Settings.ts index 99c10d8..4f96fa6 100644 --- a/jsEngine/settings/Settings.ts +++ b/jsEngine/settings/Settings.ts @@ -78,7 +78,7 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { const oldScripts = settings.startupScripts .map(file => vault.getFileByPath(file)!) - .filter(file => !file.parent?.path.startsWith(settings.startupScriptsDirectory ?? '')); + .filter(file => !this.isParentDir(file, settings.startupScriptsDirectory ?? '/')); if (oldScripts.length > 0) { this.containerEl.createEl('div', { cls: 'callout js-engine-settings-warning', text: 'These scripts are not in the Snippets Folder' }); for (const file of oldScripts) { @@ -112,6 +112,12 @@ export class JsEnginePluginSettingTab extends PluginSettingTab { return files.filter(f => f.extension == 'js').concat(folders.flatMap(dir => this.listJSfilesInDirectory(dir))); } + isParentDir(pathnode: TFile | TFolder, parent: string): boolean { + if (pathnode.parent == null) return false; + if (pathnode.parent.path == parent) return true; + return this.isParentDir(pathnode.parent, parent); + } + async toggleStartupScript(file: TFile, enable: boolean): Promise { const settings = this.plugin.settings; if (enable) {