diff --git a/README.md b/README.md index 672e56b..cd7bdf2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ **Tray** is an [Obsidian](https://obsidian.md/) plugin that can be used to launch the app -on system startup and run it in the background, adding an icon to the system tray that it -can be minimised to and a global hotkey to toggle visibility of the app's windows. +on system startup and run it in the background, adding global hotkeys and a tray menu that +toggle app window visibility and can create quick notes from anywhere in your operating system. ## Configuration +### Window management + | Option | Description | Default | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | Launch on startup | Open Obsidian automatically whenever you log into your computer. | Disabled | @@ -13,7 +15,15 @@ can be minimised to and a global hotkey to toggle visibility of the app's window | Run in background | Hide the app and continue to run it in the background instead of quitting it when pressing the window close button or toggle focus hotkey. | Disabled | | Hide taskbar icon | Hides the window's icon from from the dock/taskbar. This may not work on all Linux-based OSes. | Disabled | | Create tray icon | Add an icon to your system tray/menubar to bring hidden Obsidian windows back into focus on click or force a full quit/relaunch of the app through the right-click menu. _Changing this option requires a restart to take effect._ | Enabled | -| Toggle window focus hotkey | Format: [Electron accelerator](https://www.electronjs.org/docs/latest/api/accelerator) | CmdOrCtrl+Shift+Tab | +| Toggle window focus hotkey | This hotkey is registered globally and will be detected even if Obsidian does not have keyboard focus. Format: [Electron accelerator](https://www.electronjs.org/docs/latest/api/accelerator) | CmdOrCtrl+Shift+Tab | + +### Quick notes + +| Option | Description | Default | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| Quick note location | New quick notes will be placed in this folder. | | +| Quick note date format | New quick notes will use a filename of this pattern. Format: [Moment.js format string](https://momentjs.com/docs/#/displaying/format/) | YYYY-MM-DD | +| Quick note hotkey | This hotkey is registered globally and will be detected even if Obsidian does not have keyboard focus. Format: [Electron accelerator](https://www.electronjs.org/docs/latest/api/accelerator) | Disabled | ## Installation diff --git a/main.js b/main.js index 8354517..4e61a45 100644 --- a/main.js +++ b/main.js @@ -3,6 +3,8 @@ "use strict"; +const DEFAULT_DATE_FORMAT = "YYYY-MM-DD"; + let tray; const obsidian = require("obsidian"), { @@ -61,7 +63,17 @@ const onWindowClose = (event) => { closeBtn.removeEventListener("click", onWindowClose, true); }; -const setHideTaskbarIcon = (plugin) => { +const addQuickNote = (plugin) => { + const { quickNoteLocation, quickNoteDateFormat } = plugin.settings, + format = quickNoteDateFormat || DEFAULT_DATE_FORMAT, + date = obsidian.moment().format(format), + name = obsidian + .normalizePath(`${quickNoteLocation ?? ""}/${date}`) + .replace(/\*|"|\\|<|>|:|\||\?/g, "-"); + plugin.app.fileManager.createAndOpenMarkdownFile(name); + showWindows(); + }, + setHideTaskbarIcon = (plugin) => { const win = getCurrentWindow(); win.setSkipTaskbar(plugin.settings.hideTaskbarIcon); }, @@ -84,6 +96,12 @@ const createTrayIcon = (plugin) => { `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHZSURBVDhPlZKxTxRBFMa/XZcF7nIG7mjxjoRCwomJxgsFdhaASqzQxFDzB1AQKgstLGxIiBQGJBpiCCGx8h+wgYaGgAWNd0dyHofeEYVwt/PmOTMZV9aDIL/s5pvZvPfN9yaL/+HR3eXcypta0m4juFbP5GHuXc9IbunDFc9db/G81/ZzhDMN7g8td47mll4R5BfHwZN4LOaA+fHa259PbUmIYzWkt3e2NZNo3/V9v1vvU6kkstk+tLW3ItUVr/m+c3N8MlkwxYqmBFcbwUQQCNOcyVzDwEAWjuPi5DhAMV/tKOYPX5hCyz8Gz1zX5SmWjBvZfmTSaRBJkGAIoxJHv+pVW2yIGNxOJ8bUVNcFEWLxuG1ia6JercTbttwQTeDwPS0kCMXiXtgk/jQrFUw7ptYSMWApF40yo/ytjHq98fdk3ayVE+cn2CxMb6ruz9qAJKFUKoWza1VJSi/n0+ffgYHdWW2gHuxXymg0gjCB0sjpmiaDnkL3RzDyzLqBUKns2ztQqUR0fk2TwSrGSf1eczqF5vsPZRCQSSAFLk6gqctgQRkc6TWRQLV2YMYQki9OoNkqzFQ9r+WOGuW5CrJbOzyAlPKr6MSGLbkcDwbf35oY/jRkt6cAfgNwowruAMz9AgAAAABJRU5ErkJggg==` ), contextMenu = Menu.buildFromTemplate([ + { + type: "normal", + label: "Add Quick Note", + accelerator: plugin.settings.quickNoteHotkey, + click: () => addQuickNote(plugin), + }, { type: "normal", label: "Open Obsidian", @@ -112,25 +130,36 @@ const createTrayIcon = (plugin) => { tray.on("click", () => toggleWindows(plugin.settings.runInBackground, false)); }; -const registerHotkey = (plugin) => { +const registerHotkeys = (plugin) => { console.log("obsidian-tray: registering hotkey"); try { - const accelerator = plugin.settings.toggleWindowFocusHotkey; - globalShortcut.register(accelerator, () => { - const runInBackground = plugin.settings.runInBackground; - toggleWindows(runInBackground); - }); + const toggleAccelerator = plugin.settings.toggleWindowFocusHotkey, + quicknoteAccelerator = plugin.settings.quickNoteHotkey; + if (toggleAccelerator) { + globalShortcut.register(toggleAccelerator, () => { + const runInBackground = plugin.settings.runInBackground; + toggleWindows(runInBackground); + }); + } + if (quicknoteAccelerator) { + globalShortcut.register(quicknoteAccelerator, () => { + addQuickNote(plugin); + }); + } } catch {} }, - unregisterHotkey = (plugin) => { + unregisterHotkeys = (plugin) => { console.log("obsidian-tray: unregistering hotkey"); try { - const accelerator = plugin.settings.toggleWindowFocusHotkey; - globalShortcut.unregister(accelerator); + const toggle = plugin.settings.toggleWindowFocusHotkey, + quicknote = plugin.settings.quickNoteHotkey; + globalShortcut.unregister(toggle); + globalShortcut.unregister(quicknote); } catch {} }; const OPTIONS = [ + "Window management", { key: "launchOnStartup", desc: "Open Obsidian automatically whenever you log into your computer.", @@ -185,16 +214,26 @@ const OPTIONS = [ }, { key: "toggleWindowFocusHotkey", - desc: ` - Format: - - Electron accelerator - - `, - type: "text", + type: "hotkey", default: "CmdOrCtrl+Shift+Tab", - onBeforeChange: unregisterHotkey, - onChange: registerHotkey, + }, + "Quick notes", + { + key: "quickNoteLocation", + desc: "New quick notes will be placed in this folder.", + type: "text", + placeholder: "Example: notes/quick", + }, + { + key: "quickNoteDateFormat", + desc: "New quick notes will use a filename of this pattern.", + type: "moment", + default: DEFAULT_DATE_FORMAT, + }, + { + key: "quickNoteHotkey", + type: "hotkey", + default: "CmdOrCtrl+Shift+Q", }, ]; @@ -210,6 +249,18 @@ const keyToLabel = (key) => .createRange() .createContextualFragment((html ?? "").replace(/\s+/g, " ")); +const acceleratorFormat = ` + This hotkey is registered globally and will be detected even if Obsidian does + not have keyboard focus. Format: + + Electron accelerator + `, + momentFormat = ` + Format: + + Moment.js format string +
Preview: + `; class SettingsTab extends obsidian.PluginSettingTab { constructor(app, plugin) { super(app, plugin); @@ -218,32 +269,58 @@ class SettingsTab extends obsidian.PluginSettingTab { display() { this.containerEl.empty(); for (const opt of OPTIONS) { - const name = keyToLabel(opt.key), - desc = htmlToFragment(opt.desc), - onChange = async (value) => { + const setting = new obsidian.Setting(this.containerEl); + if (typeof opt === "string") { + setting.setName(opt); + setting.setHeading(); + } else { + if (opt.default) { + opt.placeholder ??= `Example: ${opt.default}`; + } + if (opt.type === "hotkey") { + opt.desc ??= ""; + opt.desc += acceleratorFormat; + opt.onBeforeChange = unregisterHotkeys; + opt.onChange = registerHotkeys; + } + if (opt.type === "moment") { + opt.desc = `${opt.desc ? `${opt.desc}
` : ""}${momentFormat}`; + } + + setting.setName(keyToLabel(opt.key)); + setting.setDesc(htmlToFragment(opt.desc)); + const onChange = async (value) => { await opt.onBeforeChange?.(this.plugin); this.plugin.settings[opt.key] = value; await this.plugin.saveSettings(); await opt.onChange?.(this.plugin); }; - const setting = new obsidian.Setting(this.containerEl) - .setName(name) - .setDesc(desc); - switch (opt.type) { - case "toggle": - setting.addToggle((toggle) => - toggle.setValue(this.plugin.settings[opt.key]).onChange(onChange) - ); - break; - case "text": - default: - setting.addText((text) => + if (opt.type === "toggle") { + setting.addToggle((toggle) => { + toggle + .setValue(this.plugin.settings[opt.key] ?? opt.default) + .onChange(onChange); + }); + } else if (opt.type === "moment") { + setting.addMomentFormat((moment) => { + const sampleEl = setting.descEl.createEl("b"); + sampleEl.className = "u-pop"; + moment + .setPlaceholder(opt.placeholder) + .setDefaultFormat(opt.default ?? "") + .setValue(this.plugin.settings[opt.key] ?? opt.default ?? "") + .setSampleEl(sampleEl) + .onChange(onChange); + }); + } else { + setting.addText((text) => { text - .setPlaceholder(opt.default) - .setValue(this.plugin.settings[opt.key]) - .onChange(onChange) - ); + .setPlaceholder(opt.placeholder) + .setValue(this.plugin.settings[opt.key] ?? opt.default ?? "") + .onChange(onChange); + }); + } } } } @@ -256,7 +333,7 @@ class TrayPlugin extends obsidian.Plugin { this.addSettingTab(new SettingsTab(this.app, this)); const { settings } = this; - registerHotkey(this); + registerHotkeys(this); setHideTaskbarIcon(this); setLaunchOnStartup(this); if (settings.createTrayIcon) createTrayIcon(this); @@ -273,7 +350,7 @@ class TrayPlugin extends obsidian.Plugin { } } onunload() { - unregisterHotkey(this); + unregisterHotkeys(this); cleanupWindowClose(); }