diff --git a/src/index.ts b/src/index.ts index fbfdb73..5c1ae7d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ import { TemplateAction, performAction } from "./actions"; import { loadLegacyTemplates } from "./legacyTemplates"; import { Logger } from "./logger"; import { PromiseGroup } from "./utils/promises"; -import { PluginSettingsRegistry, DefaultNoteTemplateIdSetting, DefaultTodoTemplateIdSetting, DefaultTemplatesConfigSetting } from "./settings"; +import { PluginSettingsRegistry, DefaultNoteTemplateIdSetting, DefaultTodoTemplateIdSetting, DefaultTemplatesConfigSetting, KeyboardShortcutsSetting } from "./settings"; import { LocaleGlobalSetting, DateFormatGlobalSetting, TimeFormatGlobalSetting, ProfileDirGlobalSetting } from "./settings/global"; import { DefaultTemplatesConfig } from "./settings/defaultTemplatesConfig"; import { CommandsPanel } from "./views/commandsPanel"; @@ -265,6 +265,48 @@ joplin.plugins.register({ })); + // Register keyboard-shortcut commands for individual templates (Issue #122) + // Users configure these in Settings > Templates > "Template keyboard shortcuts (JSON)" + const shortcutEntries = await KeyboardShortcutsSetting.get(); + const shortcutMenuItems: { commandName: string; accelerator?: string }[] = []; + + for (let i = 0; i < shortcutEntries.length; i++) { + const entry = shortcutEntries[i]; + const commandName = `templateShortcut_${i + 1}`; + + const actionEnum: TemplateAction = + entry.action === "newNote" + ? TemplateAction.NewNote + : entry.action === "newTodo" + ? TemplateAction.NewTodo + : TemplateAction.InsertText; + + joplinCommands.add(joplin.commands.register({ + name: commandName, + label: entry.label || `Template shortcut ${i + 1}`, + execute: async () => { + if (!entry.templateId) { + await joplin.views.dialogs.showMessageBox( + `Template shortcut ${i + 1}: no templateId configured.`); + return; + } + const template = await getTemplateFromId(entry.templateId); + if (!template) { + await joplin.views.dialogs.showMessageBox( + `Template shortcut ${i + 1}: template not found (ID: ${entry.templateId}).`); + return; + } + await performActionWithParsedTemplate(actionEnum, template); + } + })); + + const menuItem: { commandName: string; accelerator?: string } = { commandName }; + if (entry.accelerator && entry.accelerator.trim() !== "") { + menuItem.accelerator = entry.accelerator.trim(); + } + shortcutMenuItems.push(menuItem); + } + // Create templates menu await joplin.views.menus.create("templates", "Templates", [ { @@ -304,6 +346,10 @@ joplin.plugins.register({ }, ] }, + // Dynamically-added template shortcuts (Issue #122) + ...(shortcutMenuItems.length > 0 + ? [{ label: "Template shortcuts", submenu: shortcutMenuItems }] + : []), { commandName: "showPluginDocumentation" } diff --git a/src/settings/index.ts b/src/settings/index.ts index 9735b75..00784a5 100644 --- a/src/settings/index.ts +++ b/src/settings/index.ts @@ -4,6 +4,8 @@ export { DefaultNoteTemplateIdSetting } from "./defaultNoteTemplateId"; export { DefaultTemplatesConfigSetting } from "./defaultTemplatesConfig"; export { DefaultTodoTemplateIdSetting } from "./defaultTodoTemplateId"; export { TemplatesSourceSetting } from "./templatesSource"; +export { KeyboardShortcutsSetting } from "./keyboardShortcuts"; +export type { KeyboardShortcutEntry } from "./keyboardShortcuts"; // Export registry export { PluginSettingsRegistry } from "./registry"; diff --git a/src/settings/keyboardShortcuts.ts b/src/settings/keyboardShortcuts.ts new file mode 100644 index 0000000..c02d1a9 --- /dev/null +++ b/src/settings/keyboardShortcuts.ts @@ -0,0 +1,69 @@ +import joplin from "api"; +import { SettingItemType } from "api/types"; +import { PluginSetting } from "./base"; + +export interface KeyboardShortcutEntry { + /** The Joplin Note ID of the template to use */ + templateId: string; + /** The action to perform: "newNote", "newTodo", or "insertText" */ + action: "newNote" | "newTodo" | "insertText"; + /** Human-readable label shown in the menu */ + label: string; + /** Optional accelerator string, e.g. "Ctrl+Alt+1". Leave empty to have no shortcut key. */ + accelerator: string; +} + +export const KEYBOARD_SHORTCUT_SLOTS = 10; + +/** + * Stores an array of keyboard shortcut entries serialised as a JSON string. + * Each entry maps a template (by ID) + action to an accelerator. + * + * Example value (displayed in Joplin settings as a multi-line text area): + * [ + * { "templateId": "abc123", "action": "newNote", "label": "Daily note", "accelerator": "Ctrl+Alt+1" }, + * { "templateId": "def456", "action": "insertText", "label": "Meeting notes", "accelerator": "Ctrl+Alt+2" } + * ] + */ +export const KeyboardShortcutsSetting: PluginSetting = + class { + static id = "templateKeyboardShortcuts"; + static manifest = { + public: true, + type: SettingItemType.String, + value: "[]", + label: "Template keyboard shortcuts (JSON)", + description: + "Define custom keyboard shortcuts for templates. " + + "Provide a JSON array where each item has: " + + '"templateId" (note ID of the template), ' + + '"action" ("newNote", "newTodo", or "insertText"), ' + + '"label" (menu label), and ' + + '"accelerator" (e.g. "Ctrl+Alt+1" — leave empty for no shortcut). ' + + "You can add up to " + + KEYBOARD_SHORTCUT_SLOTS + + " entries. " + + "Restart Joplin after saving.", + section: "templatesPlugin", + }; + + static async get(): Promise { + const raw: string = await joplin.settings.value( + KeyboardShortcutsSetting.id, + ); + try { + const parsed = JSON.parse(raw || "[]"); + if (!Array.isArray(parsed)) return []; + return parsed.slice(0, KEYBOARD_SHORTCUT_SLOTS); + } catch { + return []; + } + } + + static async set(newValue: KeyboardShortcutEntry[]): Promise { + await joplin.settings.setValue( + KeyboardShortcutsSetting.id, + JSON.stringify(newValue), + ); + } + }; diff --git a/src/settings/registry.ts b/src/settings/registry.ts index f465be6..13e8d68 100644 --- a/src/settings/registry.ts +++ b/src/settings/registry.ts @@ -6,6 +6,7 @@ import { DefaultNoteTemplateIdSetting } from "./defaultNoteTemplateId"; import { DefaultTemplatesConfigSetting } from "./defaultTemplatesConfig"; import { DefaultTodoTemplateIdSetting } from "./defaultTodoTemplateId"; import { TemplatesSourceSetting } from "./templatesSource"; +import { KeyboardShortcutsSetting } from "./keyboardShortcuts"; import { PluginSetting } from "./base"; @@ -16,6 +17,7 @@ export class PluginSettingsRegistry { DefaultTemplatesConfigSetting, DefaultTodoTemplateIdSetting, TemplatesSourceSetting, + KeyboardShortcutsSetting, ]; public static async registerSettings(): Promise {