mirror of
https://github.com/supabase/supabase.git
synced 2026-06-09 11:38:49 +08:00
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:
@@ -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 &&
|
||||
|
||||
@@ -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'
|
||||
: ''
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
/>{' '}
|
||||
|
||||
Reference in New Issue
Block a user