From 60e19de0d1d798a3a82f93bb872a5712959bf89b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 27 Jul 2025 15:34:51 +0300 Subject: [PATCH] feat(command_palette): add keyboard shortcut --- apps/client/src/components/app_context.ts | 1 + .../src/widgets/dialogs/jump_to_note.ts | 40 +++++++++++++------ .../src/assets/translations/en/server.json | 1 + apps/server/src/services/keyboard_actions.ts | 6 +++ .../src/lib/keyboard_actions_interface.ts | 1 + 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index f960a76c4..f5a1a3834 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -262,6 +262,7 @@ export type CommandMappings = { closeThisNoteSplit: CommandData; moveThisNoteSplit: CommandData & { isMovingLeft: boolean }; jumpToNote: CommandData; + commandPalette: CommandData; // Geomap deleteFromMap: { noteId: string }; diff --git a/apps/client/src/widgets/dialogs/jump_to_note.ts b/apps/client/src/widgets/dialogs/jump_to_note.ts index 74a87a5a4..9c93fbf00 100644 --- a/apps/client/src/widgets/dialogs/jump_to_note.ts +++ b/apps/client/src/widgets/dialogs/jump_to_note.ts @@ -56,6 +56,14 @@ export default class JumpToNoteDialog extends BasicWidget { } async jumpToNoteEvent() { + await this.openDialog(); + } + + async commandPaletteEvent() { + await this.openDialog(true); + } + + private async openDialog(commandMode = false) { const dialogPromise = openDialog(this.$widget); if (utils.isMobile()) { dialogPromise.then(($dialog) => { @@ -82,12 +90,12 @@ export default class JumpToNoteDialog extends BasicWidget { } // first open dialog, then refresh since refresh is doing focus which should be visible - this.refresh(); + this.refresh(commandMode); this.lastOpenedTs = Date.now(); } - async refresh() { + async refresh(commandMode = false) { noteAutocompleteService .initNoteAutocomplete(this.$autoComplete, { allowCreatingNotes: true, @@ -115,19 +123,27 @@ export default class JumpToNoteDialog extends BasicWidget { this.modal.hide(); }); - // if you open the Jump To dialog soon after using it previously, it can often mean that you - // actually want to search for the same thing (e.g., you opened the wrong note at first try) - // so we'll keep the content. - // if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead. - if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) { - noteAutocompleteService.showRecentNotes(this.$autoComplete); - } else { + if (commandMode) { + // Start in command mode this.$autoComplete - // hack, the actual search value is stored in
 element next to the search input
-                // this is important because the search input value is replaced with the suggestion note's title
-                .autocomplete("val", this.$autoComplete.next().text())
+                .autocomplete("val", ">")
                 .trigger("focus")
                 .trigger("select");
+        } else {
+            // if you open the Jump To dialog soon after using it previously, it can often mean that you
+            // actually want to search for the same thing (e.g., you opened the wrong note at first try)
+            // so we'll keep the content.
+            // if it's outside of this time limit, then we assume it's a completely new search and show recent notes instead.
+            if (Date.now() - this.lastOpenedTs > KEEP_LAST_SEARCH_FOR_X_SECONDS * 1000) {
+                noteAutocompleteService.showRecentNotes(this.$autoComplete);
+            } else {
+                this.$autoComplete
+                    // hack, the actual search value is stored in 
 element next to the search input
+                    // this is important because the search input value is replaced with the suggestion note's title
+                    .autocomplete("val", this.$autoComplete.next().text())
+                    .trigger("focus")
+                    .trigger("select");
+            }
         }
     }
 
diff --git a/apps/server/src/assets/translations/en/server.json b/apps/server/src/assets/translations/en/server.json
index 3f1553d6b..d0bd2d28a 100644
--- a/apps/server/src/assets/translations/en/server.json
+++ b/apps/server/src/assets/translations/en/server.json
@@ -3,6 +3,7 @@
     "back-in-note-history": "Navigate to previous note in history",
     "forward-in-note-history": "Navigate to next note in history",
     "open-jump-to-note-dialog": "Open \"Jump to note\" dialog",
+    "open-command-palette": "Open command palette",
     "scroll-to-active-note": "Scroll note tree to active note",
     "quick-search": "Activate quick search bar",
     "search-in-subtree": "Search for notes in the active note's subtree",
diff --git a/apps/server/src/services/keyboard_actions.ts b/apps/server/src/services/keyboard_actions.ts
index fe8e7c276..d95e52bfa 100644
--- a/apps/server/src/services/keyboard_actions.ts
+++ b/apps/server/src/services/keyboard_actions.ts
@@ -35,6 +35,12 @@ function getDefaultKeyboardActions() {
             description: t("keyboard_actions.open-jump-to-note-dialog"),
             scope: "window"
         },
+        {
+            actionName: "commandPalette",
+            defaultShortcuts: ["CommandOrControl+Shift+J"],
+            description: t("keyboard_actions.open-command-palette"),
+            scope: "window"
+        },
         {
             actionName: "scrollToActiveNote",
             defaultShortcuts: ["CommandOrControl+."],
diff --git a/packages/commons/src/lib/keyboard_actions_interface.ts b/packages/commons/src/lib/keyboard_actions_interface.ts
index 2fc96665f..0d0068793 100644
--- a/packages/commons/src/lib/keyboard_actions_interface.ts
+++ b/packages/commons/src/lib/keyboard_actions_interface.ts
@@ -2,6 +2,7 @@ const enum KeyboardActionNamesEnum {
     backInNoteHistory,
     forwardInNoteHistory,
     jumpToNote,
+    commandPalette,
     scrollToActiveNote,
     quickSearch,
     searchInSubtree,