From f6afd8b2e424ce3043c321eb3c88f285d2cbc62f Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 26 Jun 2026 10:44:16 +0800 Subject: [PATCH] Use CodeEditor in AIEditor (#47297) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context More clean up / housekeeping - to use `CodeEditor` in `AIEditor` and remove duplicated logic ## Summary by CodeRabbit * **New Features** * Expanded supported editor file types, including CSS, CSV, and JavaScript (with improved syntax highlighting). * The updated editor experience now provides a readily available “run query” action. * **UI Improvements** * Tightened editor panel spacing and adjusted padding for a cleaner layout. * **Bug Fixes** * Improved file-to-language detection so files open with the correct syntax highlighting more consistently. --- apps/studio/components/ui/AIEditor/index.tsx | 59 +++++-------------- .../components/ui/CodeEditor/CodeEditor.tsx | 13 +++- .../components/ui/EditorPanel/EditorPanel.tsx | 6 +- .../FileExplorerAndEditor.utils.ts | 12 +++- 4 files changed, 38 insertions(+), 52 deletions(-) diff --git a/apps/studio/components/ui/AIEditor/index.tsx b/apps/studio/components/ui/AIEditor/index.tsx index 1fa7d3bb507..ce8c6865a71 100644 --- a/apps/studio/components/ui/AIEditor/index.tsx +++ b/apps/studio/components/ui/AIEditor/index.tsx @@ -1,4 +1,4 @@ -import Editor, { Monaco, OnMount } from '@monaco-editor/react' +import { Monaco, OnMount } from '@monaco-editor/react' import { AnimatePresence, motion } from 'framer-motion' import type { editor as monacoEditor } from 'monaco-editor' import { useCallback, useEffect, useRef, useState } from 'react' @@ -6,18 +6,20 @@ import { toast } from 'sonner' import { KeyboardShortcut } from 'ui' import { useSetCommandMenuOpen } from 'ui-patterns' +import { CodeEditor, type ValidLanguages } from '../CodeEditor/CodeEditor' 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 { useLatest } from '@/hooks/misc/useLatest' import { SHORTCUT_IDS } from '@/state/shortcuts/registry' import { useIsShortcutEnabled } from '@/state/shortcuts/useIsShortcutEnabled' import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state' interface AIEditorProps { id?: string - language?: string + language: ValidLanguages value?: string defaultValue?: string aiEndpoint?: string @@ -71,14 +73,9 @@ export const AIEditor = ({ 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 executeQueryRef = useLatest(executeQuery) + const commandMenuHotkeyEnabledRef = useLatest(isCommandMenuHotkeyEnabled) + const setCommandMenuOpenRef = useLatest(setCommandMenuOpen) const [currentValue, setCurrentValue] = useState(value || defaultValue) const [isDiffMode, setIsDiffMode] = useState(false) @@ -237,17 +234,6 @@ export const AIEditor = ({ }) } - if (!!executeQueryRef.current) { - editor.addAction({ - id: 'run-query', - label: 'Run Query', - keybindings: [monaco.KeyMod.CtrlCmd + monaco.KeyCode.Enter], - contextMenuGroupId: 'operation', - contextMenuOrder: 0, - run: () => executeQueryRef.current?.(), - }) - } - refreshCloseAction() // Add AI Assistant toggle keybinding (Cmd+I) @@ -281,11 +267,6 @@ export const AIEditor = ({ setCommandMenuOpenRef.current(true) } }) - - if (autoFocus) { - if (editor.getValue().length === 1) editor.setPosition({ lineNumber: 1, column: 2 }) - editor.focus() - } } const handlePrompt = async ( @@ -325,19 +306,6 @@ export const AIEditor = ({ } } - const defaultOptions: monacoEditor.IStandaloneEditorConstructionOptions = { - tabSize: 2, - fontSize: 13, - readOnly, - minimap: { enabled: false }, - wordWrap: 'on', - lineNumbers: 'on', - folding: false, - padding: { top: 4 }, - lineNumbersMinChars: 3, - ...options, - } - useEffect(() => { setCurrentValue(value || defaultValue) }, [value, defaultValue]) @@ -414,19 +382,20 @@ export const AIEditor = ({ ) : (
- {/* [Joshen] Refactor: Use CodeEditor.tsx instead, reduce duplicate declaration of Editor */} - { + onInputChange={(value) => { const newValue = value || '' setCurrentValue(newValue) onChange?.(newValue) }} onMount={handleEditorOnMount} - className={className} + actions={{ runQuery: { enabled: true, callback: () => executeQueryRef.current?.() } }} /> {promptState.isOpen && editorRef.current && ( { wordWrap: 'on', lineNumbers: 'on', folding: false, - padding: { top: 16 }, + padding: { top: 12 }, lineNumbersMinChars: 3, }} executeQuery={onExecuteSql} @@ -516,7 +516,6 @@ export const EditorPanel = () => { openAIAssistantShortcutEnabled={isAIAssistantHotkeyEnabled} />
- {error !== undefined && (
{ />
)} - {showWarning && ( { }} /> )} - {results !== undefined && results.length > 0 && (
{

)} -
{(isUpserting || saveStatus !== 'idle') && (
{ return binaryExtensions.includes(extension || '') } -export const getLanguageFromFileName = (fileName: string): string => { +type EditorLanguage = + | 'typescript' + | 'javascript' + | 'json' + | 'html' + | 'css' + | 'markdown' + | 'csv' + | 'plaintext' + +export const getLanguageFromFileName = (fileName: string): EditorLanguage => { const extension = fileName.split('.').pop()?.toLowerCase() switch (extension) { case 'ts':