diff --git a/frontend/src/components/MonacoEditor.tsx b/frontend/src/components/MonacoEditor.tsx
index 64596346..bad1b679 100644
--- a/frontend/src/components/MonacoEditor.tsx
+++ b/frontend/src/components/MonacoEditor.tsx
@@ -168,7 +168,8 @@ const installOceanBaseOracleNavigationFallback = (editor: any) => {
};
const patchQueryEditorAiInlineRightArrowFallback = (editor: any, monaco: any) => {
- const originalAddCommand = editor?.addCommand?.bind?.(editor);
+ const rawAddCommand = editor?.addCommand;
+ const originalAddCommand = rawAddCommand?.bind?.(editor);
if (!originalAddCommand || !monaco?.KeyCode?.RightArrow) {
return;
}
@@ -180,7 +181,7 @@ const patchQueryEditorAiInlineRightArrowFallback = (editor: any, monaco: any) =>
configurable: true,
});
- editor.addCommand = (keybinding: any, handler: any, context: any) => {
+ const patchedAddCommand = (keybinding: any, handler: any, context: any) => {
if (
keybinding === monaco.KeyCode.RightArrow
&& context === QUERY_EDITOR_AI_INLINE_CONTEXT_KEY
@@ -200,6 +201,32 @@ const patchQueryEditorAiInlineRightArrowFallback = (editor: any, monaco: any) =>
}
return originalAddCommand(keybinding, handler, context);
};
+
+ if (rawAddCommand?.mock) {
+ for (const propertyName of [
+ 'mock',
+ 'mockClear',
+ 'mockReset',
+ 'mockRestore',
+ 'mockImplementation',
+ 'mockImplementationOnce',
+ 'mockName',
+ 'getMockName',
+ ]) {
+ if (!(propertyName in rawAddCommand)) {
+ continue;
+ }
+ Object.defineProperty(patchedAddCommand, propertyName, {
+ configurable: true,
+ get: () => {
+ const value = rawAddCommand[propertyName];
+ return typeof value === 'function' ? value.bind(rawAddCommand) : value;
+ },
+ });
+ }
+ }
+
+ editor.addCommand = patchedAddCommand;
};
const installPrintableInputFallback = (editor: any, monaco: any) => {
@@ -207,8 +234,9 @@ const installPrintableInputFallback = (editor: any, monaco: any) => {
if (!editorDomNode || editor.__gonaviPrintableInputFallbackInstalled) {
return;
}
+ const TextAreaElement = typeof HTMLTextAreaElement === 'undefined' ? null : HTMLTextAreaElement;
const input = editorDomNode.querySelector?.('textarea.inputarea, .inputarea textarea, textarea') as HTMLTextAreaElement | null;
- if (!(input instanceof HTMLTextAreaElement)) {
+ if (!TextAreaElement || !(input instanceof TextAreaElement)) {
return;
}
Object.defineProperty(editor, '__gonaviPrintableInputFallbackInstalled', {
diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx
index 04215b42..d140c5aa 100644
--- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx
+++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx
@@ -4481,6 +4481,39 @@ describe('QueryEditor external SQL save', () => {
).not.toContain('gonavi:find-active-query');
});
+ it('keeps SQL editor search on Cmd+F only and suppresses Monaco Cmd+E find-with-selection', async () => {
+ storeState.shortcutOptions.selectCurrentStatement.mac = { enabled: false, combo: '' };
+ storeState.shortcutOptions.selectCurrentStatement.windows = { enabled: false, combo: '' };
+
+ await act(async () => {
+ create();
+ });
+ (window.dispatchEvent as any).mockClear();
+ (document.execCommand as any).mockClear();
+
+ expect(findEditorAction('gonavi.findInEditor')).toMatchObject({
+ keybindings: [2048 | 70],
+ });
+
+ const suppressMacFindAction = findEditorAction('gonavi.suppressMacFindWithSelection');
+ expect(suppressMacFindAction).toMatchObject({
+ keybindings: [2048 | 69],
+ });
+
+ await act(async () => {
+ suppressMacFindAction.run();
+ await Promise.resolve();
+ });
+
+ expect(
+ (window.dispatchEvent as any).mock.calls.map((call: any[]) => call[0]?.type),
+ ).not.toContain('gonavi:find-active-query');
+ expect(document.execCommand).not.toHaveBeenCalled();
+ });
+
it('intercepts Ctrl/Cmd+D at window level and duplicates the current line below', async () => {
storeState.shortcutOptions.duplicateCurrentLine.mac = { enabled: true, combo: 'Meta+D' };
storeState.shortcutOptions.duplicateCurrentLine.windows = { enabled: true, combo: 'Ctrl+D' };
diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx
index 46331efd..be416242 100644
--- a/frontend/src/components/QueryEditor.tsx
+++ b/frontend/src/components/QueryEditor.tsx
@@ -170,6 +170,8 @@ const QUERY_EDITOR_MONACO_FIND_OPTIONS = {
addExtraSpaceOnTop: true,
} as const;
const QUERY_EDITOR_NATIVE_SELECT_CURRENT_LINE_EVENT = 'gonavi:native-select-current-line';
+const QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO = 'Meta+E';
+const QUERY_EDITOR_MAC_FIND_WITH_SELECTION_GUARD_ACTION_ID = 'gonavi.suppressMacFindWithSelection';
const QUERY_EDITOR_AI_INLINE_DEBOUNCE_MS = 90;
const QUERY_EDITOR_AI_INLINE_CONTEXT_KEY = 'gonaviAiInlineSuggestionVisible';
@@ -1131,6 +1133,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
const monacoRef = useRef(null);
const runQueryActionRef = useRef(null);
const selectCurrentStatementActionRef = useRef(null);
+ const macFindWithSelectionGuardActionRef = useRef(null);
const duplicateCurrentLineActionRef = useRef(null);
const saveQueryActionRef = useRef(null);
const findInEditorActionRef = useRef(null);
@@ -4308,6 +4311,8 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
objectHoverActionRef.current = null;
triggerSqlAiCompletionActionRef.current?.dispose?.();
triggerSqlAiCompletionActionRef.current = null;
+ macFindWithSelectionGuardActionRef.current?.dispose?.();
+ macFindWithSelectionGuardActionRef.current = null;
triggerSqlAiCompletionKeydownDisposableRef.current?.dispose?.();
triggerSqlAiCompletionKeydownDisposableRef.current = null;
triggerAiInlineCompletionRef.current = null;
@@ -4369,6 +4374,30 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
}
}
+ const macFindWithSelectionGuardKeyBinding = activeShortcutPlatform === 'mac'
+ ? comboToMonacoKeyBinding(
+ QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO, monaco.KeyMod, monaco.KeyCode,
+ )
+ : null;
+ if (macFindWithSelectionGuardKeyBinding) {
+ macFindWithSelectionGuardActionRef.current = editor.addAction({
+ id: QUERY_EDITOR_MAC_FIND_WITH_SELECTION_GUARD_ACTION_ID,
+ label: 'GoNavi: Suppress macOS Cmd+E Find with Selection',
+ keybindings: [
+ macFindWithSelectionGuardKeyBinding.keyMod
+ | macFindWithSelectionGuardKeyBinding.keyCode,
+ ],
+ run: () => {
+ if (
+ selectStatementBinding?.enabled
+ && normalizeShortcutCombo(selectStatementBinding.combo) === QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO
+ ) {
+ void handleSelectCurrentStatement();
+ }
+ },
+ });
+ }
+
const duplicateLineBinding = duplicateCurrentLineShortcutBinding;
if (duplicateLineBinding?.enabled && duplicateLineBinding.combo) {
const keyBinding = comboToMonacoKeyBinding(
@@ -6734,21 +6763,51 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
selectCurrentStatementActionRef.current.dispose();
selectCurrentStatementActionRef.current = null;
}
+ if (macFindWithSelectionGuardActionRef.current) {
+ macFindWithSelectionGuardActionRef.current.dispose();
+ macFindWithSelectionGuardActionRef.current = null;
+ }
const editor = editorRef.current;
const monaco = monacoRef.current;
if (!editor || !monaco) return;
const binding = selectCurrentStatementShortcutBinding;
- if (!binding?.enabled || !binding.combo) return;
+ if (binding?.enabled && binding.combo) {
+ const keyBinding = comboToMonacoKeyBinding(binding.combo, monaco.KeyMod, monaco.KeyCode);
+ if (keyBinding) {
+ selectCurrentStatementActionRef.current = editor.addAction({
+ id: 'gonavi.selectCurrentStatement',
+ label: buildQueryEditorMonacoActionLabel('app.shortcuts.action.selectCurrentStatement.label'),
+ keybindings: [keyBinding.keyMod | keyBinding.keyCode],
+ run: handleSelectCurrentStatement,
+ });
+ }
+ }
- const keyBinding = comboToMonacoKeyBinding(binding.combo, monaco.KeyMod, monaco.KeyCode);
- if (keyBinding) {
- selectCurrentStatementActionRef.current = editor.addAction({
- id: 'gonavi.selectCurrentStatement',
- label: buildQueryEditorMonacoActionLabel('app.shortcuts.action.selectCurrentStatement.label'),
- keybindings: [keyBinding.keyMod | keyBinding.keyCode],
- run: handleSelectCurrentStatement,
+ const macFindWithSelectionGuardKeyBinding = activeShortcutPlatform === 'mac'
+ ? comboToMonacoKeyBinding(
+ QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO,
+ monaco.KeyMod,
+ monaco.KeyCode,
+ )
+ : null;
+ if (macFindWithSelectionGuardKeyBinding) {
+ macFindWithSelectionGuardActionRef.current = editor.addAction({
+ id: QUERY_EDITOR_MAC_FIND_WITH_SELECTION_GUARD_ACTION_ID,
+ label: 'GoNavi: Suppress macOS Cmd+E Find with Selection',
+ keybindings: [
+ macFindWithSelectionGuardKeyBinding.keyMod
+ | macFindWithSelectionGuardKeyBinding.keyCode,
+ ],
+ run: () => {
+ if (
+ binding?.enabled
+ && normalizeShortcutCombo(binding.combo) === QUERY_EDITOR_MAC_FIND_WITH_SELECTION_COMBO
+ ) {
+ void handleSelectCurrentStatement();
+ }
+ },
});
}
@@ -6757,8 +6816,12 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
selectCurrentStatementActionRef.current.dispose();
selectCurrentStatementActionRef.current = null;
}
+ if (macFindWithSelectionGuardActionRef.current) {
+ macFindWithSelectionGuardActionRef.current.dispose();
+ macFindWithSelectionGuardActionRef.current = null;
+ }
};
- }, [languagePreference, selectCurrentStatementShortcutBinding, handleSelectCurrentStatement]);
+ }, [activeShortcutPlatform, languagePreference, selectCurrentStatementShortcutBinding, handleSelectCurrentStatement]);
useEffect(() => {
if (duplicateCurrentLineActionRef.current) {