Add keyboard shortcut for saving notebook (#50707)

### Context

Adds a keyboard shortcut for saving a notebook in the explorer 
<img width="269" height="112" alt="image"
src="https://github.com/user-attachments/assets/c9ab1c4b-fbb3-40ed-8f10-47301e16e6b3"
/>

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## 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`).

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Joshen Lim authored and GitHub committed 2026-09-22 19:29:40 +08:00
1 parent c7880e328f
commit aef1599d3e
4 files changed
+63 -28

No files matched your search

@@ -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
</ExplorerToolbarAction>
<ExplorerToolbarAction
aria-label="Save changes"
icon={<Save size={16} strokeWidth={2} />}
tooltip="Save changes"
loading={isUpdating}
onClick={handleSaveNotebook}
/>
<ShortcutTooltip
side="bottom"
shortcutId={SHORTCUT_IDS.EXPLORER_NOTEBOOK_SAVE}
label="Save changes"
>
<ExplorerToolbarAction
aria-label="Save changes"
icon={<Save size={16} strokeWidth={2} />}
loading={isUpdating}
onClick={handleSaveNotebook}
/>
</ShortcutTooltip>
<ExplorerToolbarActions>
<DropdownMenu>
<DropdownMenuTrigger asChild>
+7 -21
View File
@@ -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<ShortcutId, ShortcutDefinition> = {
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<ShortcutId, ShortcutDefinition> = {
// Database sub-page navigation chord registration
...databaseNavRegistry,
// Explorer notebook shortcut registration
...explorerRegistry,
// Auth users page shortcut registration
...authUsersRegistry,
// Auth sub-page navigation chord registration
@@ -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<ExplorerShortcutId> = {
[EXPLORER_SHORTCUT_IDS.EXPLORER_NOTEBOOK_SAVE]: {
id: EXPLORER_SHORTCUT_IDS.EXPLORER_NOTEBOOK_SAVE,
label: 'Save notebook',
sequence: ['Mod+S'],
showInSettings: false,
},
}
@@ -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<TableEditorShortcutId> = {
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,
},
}