feat(studio): change SQL editor assistant shortcut to Cmd+Shift+K (#45380)

## Summary

Closes
[FE-3109](https://linear.app/supabase/issue/FE-3109/change-sql-editor-assistant-shortcut-to-cmdshiftk).

The SQL editor's "Generate SQL" Monaco action was bound to `Cmd+K`,
which conflicted with the global command menu shortcut while the editor
was focused. This PR moves the assistant shortcut to `Cmd+Shift+K` and
makes `Cmd+K` open the global command menu from inside the editor.

## Changes

- `MonacoEditor.tsx` — rebind `generate-sql` to `Cmd+Shift+K`. Add an
`editor.addCommand` for `Cmd+K` that opens the global command menu
(gated on the user's `COMMAND_MENU_OPEN` shortcut preference). Without
this, Monaco swallows `Cmd+K` as a chord prefix and the global hotkey
never fires inside the editor.
- `SQLEditor.tsx` — update the empty-editor placeholder text from
`CMD+K` to `CMD+SHIFT+K`.

## Notes

- Monaco's standalone defaults bind `Cmd+Shift+K` to "Delete Line";
registering an `editor.addAction` with the same keybinding overrides it.
- The same `Cmd+K` binding still exists in
`apps/studio/components/ui/AIEditor/index.tsx` (used by the inline
editor panel and edge functions). Out of scope for FE-3109 — happy to
file a follow-up.

## Test plan

- [x] Focus the SQL editor, press `Cmd+K` → global command menu opens.
- [x] Focus the SQL editor, press `Cmd+Shift+K` → Generate SQL widget
opens (or "Make an edit" if a diff is already visible).
- [x] Disable the command menu shortcut in Account → Preferences →
Keyboard shortcuts and confirm `Cmd+K` no longer opens the menu from
inside the editor.
- [x] Empty SQL snippet placeholder reads "Hit CMD+SHIFT+K to generate
query…".

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

* **Improvements**
  * Reorganized SQL editor keyboard shortcuts for clearer access
* "Generate SQL" shortcut changed to Ctrl/Cmd + Shift + K (was Ctrl/Cmd
+ K)
  * Command menu can now be opened with Ctrl/Cmd + K when enabled
  * Editor UI shortcut hints updated to reflect the new bindings
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Ali Waseem
2026-04-30 07:09:58 -06:00
committed by GitHub
parent 13df106d89
commit 9cd688528f
3 changed files with 43 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ import { useRouter } from 'next/router'
import { MutableRefObject, useEffect, useRef, useState } from 'react'
import { cn } from 'ui'
import { Admonition } from 'ui-patterns'
import { useSetCommandMenuOpen } from 'ui-patterns/CommandMenu'
import type { IStandaloneCodeEditor } from './SQLEditor.types'
import { createSqlSnippetSkeletonV2 } from './SQLEditor.utils'
@@ -72,6 +73,8 @@ const MonacoEditor = ({
true
)
const isAIAssistantHotkeyEnabled = useIsShortcutEnabled(SHORTCUT_IDS.AI_ASSISTANT_TOGGLE)
const isCommandMenuHotkeyEnabled = useIsShortcutEnabled(SHORTCUT_IDS.COMMAND_MENU_OPEN)
const setCommandMenuOpen = useSetCommandMenuOpen()
// [Joshen] Lodash debounce doesn't seem to be working here, so opting to use useDebounce
const [value, setValue] = useState('')
@@ -93,6 +96,12 @@ const MonacoEditor = ({
const aiHotkeyEnabledRef = useRef(isAIAssistantHotkeyEnabled)
aiHotkeyEnabledRef.current = isAIAssistantHotkeyEnabled
const commandMenuHotkeyEnabledRef = useRef(isCommandMenuHotkeyEnabled)
commandMenuHotkeyEnabledRef.current = isCommandMenuHotkeyEnabled
const setCommandMenuOpenRef = useRef(setCommandMenuOpen)
setCommandMenuOpenRef.current = setCommandMenuOpen
const handleEditorOnMount: OnMount = async (editor, monaco) => {
editorRef.current = editor
monacoRef.current = monaco
@@ -202,7 +211,7 @@ const MonacoEditor = ({
editor.addAction({
id: 'generate-sql',
label: 'Generate SQL',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK],
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyK],
run: () => {
const selectionParts = getEditorSelectionParts(editor)
if (selectionParts) onPrompt(selectionParts)
@@ -210,6 +219,15 @@ const MonacoEditor = ({
})
}
// Monaco claims Cmd+K as a chord prefix, which swallows the global command
// menu shortcut while the editor is focused. Intercept it here and open the
// command menu directly so it works the same inside and outside the editor.
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => {
if (commandMenuHotkeyEnabledRef.current) {
setCommandMenuOpenRef.current(true)
}
})
editor.onDidChangeCursorSelection(({ selection }) => {
const noSelection =
selection.startLineNumber === selection.endLineNumber &&

View File

@@ -937,7 +937,7 @@ export const SQLEditor = () => {
placeholder={
!promptState.isOpen && !editorRef.current?.getValue()
? 'Hit ' +
(os === 'macos' ? 'CMD+K' : `CTRL+K`) +
(os === 'macos' ? 'CMD+SHIFT+K' : `CTRL+SHIFT+K`) +
' to generate query or just start typing'
: ''
}

View File

@@ -4,12 +4,15 @@ import type { editor as monacoEditor } from 'monaco-editor'
import { useCallback, useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
import { KeyboardShortcut } from 'ui'
import { useSetCommandMenuOpen } from 'ui-patterns'
import { DiffEditor } from '../DiffEditor'
import ResizableAIWidget from './ResizableAIWidget'
import { getEditorSelectionParts } from './utils'
import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { constructHeaders } from '@/data/fetchers'
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
import { useIsShortcutEnabled } from '@/state/shortcuts/useIsShortcutEnabled'
import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state'
interface AIEditorProps {
@@ -65,9 +68,18 @@ export const AIEditor = ({
const monacoRef = useRef<Monaco | null>(null)
const closeActionDisposableRef = useRef<{ dispose: () => void } | null>(null)
const isCommandMenuHotkeyEnabled = useIsShortcutEnabled(SHORTCUT_IDS.COMMAND_MENU_OPEN)
const setCommandMenuOpen = useSetCommandMenuOpen()
const executeQueryRef = useRef(executeQuery)
executeQueryRef.current = executeQuery
const commandMenuHotkeyEnabledRef = useRef(isCommandMenuHotkeyEnabled)
commandMenuHotkeyEnabledRef.current = isCommandMenuHotkeyEnabled
const setCommandMenuOpenRef = useRef(setCommandMenuOpen)
setCommandMenuOpenRef.current = setCommandMenuOpen
const [currentValue, setCurrentValue] = useState(value || defaultValue)
const [isDiffMode, setIsDiffMode] = useState(false)
const [isDiffEditorMounted, setIsDiffEditorMounted] = useState(false)
@@ -251,7 +263,7 @@ export const AIEditor = ({
editor.addAction({
id: 'generate-ai',
label: 'Generate with AI',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK],
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyK],
run: () => {
const selectionParts = getEditorSelectionParts(editor)
if (!selectionParts) return
@@ -259,6 +271,15 @@ export const AIEditor = ({
},
})
// Monaco claims Cmd+K as a chord prefix, which swallows the global command
// menu shortcut while the editor is focused. Intercept it here and open the
// command menu directly so it works the same inside and outside the editor.
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => {
if (commandMenuHotkeyEnabledRef.current) {
setCommandMenuOpenRef.current(true)
}
})
if (autoFocus) {
if (editor.getValue().length === 1) editor.setPosition({ lineNumber: 1, column: 2 })
editor.focus()
@@ -435,7 +456,7 @@ export const AIEditor = ({
>
Hit{' '}
<KeyboardShortcut
keys={['Meta', 'k']}
keys={['Meta', 'Shift', 'k']}
variant="inline"
className="text-xs text-foreground-lighter"
/>{' '}