From aef1599d3e32cd20fe68b69cb16a13c73bfe9175 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 22 Sep 2026 19:29:40 +0800 Subject: [PATCH] Add keyboard shortcut for saving notebook (#50707) ### Context Adds a keyboard shortcut for saving a notebook in the explorer image Also shifts the keyboard shortcuts for queue operations into the table editor registry - more contextual to there since queue operations is specifically for the table editor only ## Summary by CodeRabbit - **New Features** - Added a keyboard shortcut (`Mod+S`) for saving notebooks in Explorer. - The save toolbar action now displays its keyboard shortcut. - Restored table editing shortcuts for saving pending edits, toggling the operation queue, and undoing changes (`Mod+S`, `Mod+.`, and `Mod+Z`). --- .../Explorer/ExplorerNotebookTab.tsx | 26 ++++++++++++----- apps/studio/state/shortcuts/registry.ts | 28 +++++-------------- .../state/shortcuts/registry/explorer.ts | 16 +++++++++++ .../state/shortcuts/registry/table-editor.ts | 21 ++++++++++++++ 4 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 apps/studio/state/shortcuts/registry/explorer.ts diff --git a/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx b/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx index 35c710418bc..6ce6f806161 100644 --- a/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx +++ b/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx @@ -68,6 +68,7 @@ import { createMarkdownCellSkeleton, createQueryCellSkeleton } from './utils' import { checkDestructiveQuery } from '@/components/interfaces/SQLEditor/SQLEditor.utils' import { useExplorerDeleteItem } from '@/components/layouts/ExplorerLayout/ExplorerProvider' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' +import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip' import { evictNotebookFromCaches, hasDiscardableChanges, @@ -85,6 +86,8 @@ import { useCurrentNotebook, useNotebooksStateSnapshot, } from '@/state/notebooks/notebooks-state' +import { SHORTCUT_IDS } from '@/state/shortcuts/registry' +import { useShortcut } from '@/state/shortcuts/useShortcut' import { createTabId, useTabsStateSnapshot } from '@/state/tabs' export const ExplorerNotebookTab = () => { @@ -269,6 +272,10 @@ export const ExplorerNotebookTab = () => { persistNotebook() } + useShortcut(SHORTCUT_IDS.EXPLORER_NOTEBOOK_SAVE, handleSaveNotebook, { + enabled: !!content && !isUpdating, + }) + const handleSaveAnyway = () => { setIsSaveConflictOpen(false) persistNotebook() @@ -392,13 +399,18 @@ export const ExplorerNotebookTab = () => { > Analyze - } - tooltip="Save changes" - loading={isUpdating} - onClick={handleSaveNotebook} - /> + + } + loading={isUpdating} + onClick={handleSaveNotebook} + /> + diff --git a/apps/studio/state/shortcuts/registry.ts b/apps/studio/state/shortcuts/registry.ts index 22e108c3cdf..fd5327a5672 100644 --- a/apps/studio/state/shortcuts/registry.ts +++ b/apps/studio/state/shortcuts/registry.ts @@ -5,6 +5,7 @@ import { API_KEYS_SHORTCUT_IDS, apiKeysRegistry } from './registry/api-keys' import { AUTH_NAV_SHORTCUT_IDS, authNavRegistry } from './registry/auth-nav' import { AUTH_USERS_SHORTCUT_IDS, authUsersRegistry } from './registry/auth-users' import { DATABASE_NAV_SHORTCUT_IDS, databaseNavRegistry } from './registry/database-nav' +import { EXPLORER_SHORTCUT_IDS, explorerRegistry } from './registry/explorer' import { FUNCTIONS_DETAIL_SHORTCUT_IDS, functionsDetailRegistry } from './registry/functions-detail' import { FUNCTIONS_DETAIL_NAV_SHORTCUT_IDS, @@ -87,9 +88,6 @@ export const SHORTCUT_IDS = { DATA_TABLE_RESET_COLUMNS: 'data-table.reset-columns', DATA_TABLE_TOGGLE_LIVE: 'data-table.toggle-live', ACTION_BAR_SAVE: 'action-bar.save', - OPERATION_QUEUE_SAVE: 'operation-queue.save', - OPERATION_QUEUE_TOGGLE: 'operation-queue.toggle', - OPERATION_QUEUE_UNDO: 'operation-queue.undo', NAV_HOME: 'nav.home', NAV_TABLE_EDITOR: 'nav.table-editor', NAV_SQL_EDITOR: 'nav.sql-editor', @@ -146,6 +144,9 @@ export const SHORTCUT_IDS = { // Database sub-page navigation chords ...DATABASE_NAV_SHORTCUT_IDS, + // Explorer notebook shortcuts + ...EXPLORER_SHORTCUT_IDS, + // Auth users page shortcuts ...AUTH_USERS_SHORTCUT_IDS, // Auth sub-page navigation chords @@ -326,24 +327,6 @@ export const SHORTCUT_DEFINITIONS: Record = { sequence: ['Mod+Enter'], showInSettings: false, }, - [SHORTCUT_IDS.OPERATION_QUEUE_SAVE]: { - id: SHORTCUT_IDS.OPERATION_QUEUE_SAVE, - label: 'Save pending table edits', - sequence: ['Mod+S'], - showInSettings: false, - }, - [SHORTCUT_IDS.OPERATION_QUEUE_TOGGLE]: { - id: SHORTCUT_IDS.OPERATION_QUEUE_TOGGLE, - label: 'Toggle operation queue panel', - sequence: ['Mod+.'], - showInSettings: false, - }, - [SHORTCUT_IDS.OPERATION_QUEUE_UNDO]: { - id: SHORTCUT_IDS.OPERATION_QUEUE_UNDO, - label: 'Undo latest table edit', - sequence: ['Mod+Z'], - showInSettings: false, - }, [SHORTCUT_IDS.NAV_HOME]: { id: SHORTCUT_IDS.NAV_HOME, label: 'Go to Project Overview', @@ -536,6 +519,9 @@ export const SHORTCUT_DEFINITIONS: Record = { // Database sub-page navigation chord registration ...databaseNavRegistry, + // Explorer notebook shortcut registration + ...explorerRegistry, + // Auth users page shortcut registration ...authUsersRegistry, // Auth sub-page navigation chord registration diff --git a/apps/studio/state/shortcuts/registry/explorer.ts b/apps/studio/state/shortcuts/registry/explorer.ts new file mode 100644 index 00000000000..81a172b1364 --- /dev/null +++ b/apps/studio/state/shortcuts/registry/explorer.ts @@ -0,0 +1,16 @@ +import { RegistryDefinations } from '../types' + +export const EXPLORER_SHORTCUT_IDS = { + EXPLORER_NOTEBOOK_SAVE: 'explorer.notebook-save', +} + +export type ExplorerShortcutId = (typeof EXPLORER_SHORTCUT_IDS)[keyof typeof EXPLORER_SHORTCUT_IDS] + +export const explorerRegistry: RegistryDefinations = { + [EXPLORER_SHORTCUT_IDS.EXPLORER_NOTEBOOK_SAVE]: { + id: EXPLORER_SHORTCUT_IDS.EXPLORER_NOTEBOOK_SAVE, + label: 'Save notebook', + sequence: ['Mod+S'], + showInSettings: false, + }, +} diff --git a/apps/studio/state/shortcuts/registry/table-editor.ts b/apps/studio/state/shortcuts/registry/table-editor.ts index 2c02b97eb52..d17b0f1dba0 100644 --- a/apps/studio/state/shortcuts/registry/table-editor.ts +++ b/apps/studio/state/shortcuts/registry/table-editor.ts @@ -20,6 +20,9 @@ export const TABLE_EDITOR_SHORTCUT_IDS = { TABLE_EDITOR_CLEAR_SORT: 'table-editor.clear-sort', TABLE_EDITOR_REFRESH: 'table-editor.refresh', TABLE_EDITOR_FOCUS_SCHEMA: 'table-editor.focus-schema', + OPERATION_QUEUE_SAVE: 'operation-queue.save', + OPERATION_QUEUE_TOGGLE: 'operation-queue.toggle', + OPERATION_QUEUE_UNDO: 'operation-queue.undo', } export type TableEditorShortcutId = @@ -159,4 +162,22 @@ export const tableEditorRegistry: RegistryDefinations = { showInSettings: false, options: { ignoreInputs: true }, }, + [TABLE_EDITOR_SHORTCUT_IDS.OPERATION_QUEUE_SAVE]: { + id: TABLE_EDITOR_SHORTCUT_IDS.OPERATION_QUEUE_SAVE, + label: 'Save pending table edits', + sequence: ['Mod+S'], + showInSettings: false, + }, + [TABLE_EDITOR_SHORTCUT_IDS.OPERATION_QUEUE_TOGGLE]: { + id: TABLE_EDITOR_SHORTCUT_IDS.OPERATION_QUEUE_TOGGLE, + label: 'Toggle operation queue panel', + sequence: ['Mod+.'], + showInSettings: false, + }, + [TABLE_EDITOR_SHORTCUT_IDS.OPERATION_QUEUE_UNDO]: { + id: TABLE_EDITOR_SHORTCUT_IDS.OPERATION_QUEUE_UNDO, + label: 'Undo latest table edit', + sequence: ['Mod+Z'], + showInSettings: false, + }, }