Use CodeEditor in AIEditor (#47297)

## Context

More clean up / housekeeping - to use `CodeEditor` in `AIEditor` and
remove duplicated logic

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## 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.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Joshen Lim authored and GitHub committed 2026-06-26 10:44:16 +08:00
1 parent 5cb81123ae
commit f6afd8b2e4
4 files changed
+38 -52

No files matched your search

+14 -45
View File
@@ -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 = ({
</div>
) : (
<div className="w-full h-full relative">
{/* [Joshen] Refactor: Use CodeEditor.tsx instead, reduce duplicate declaration of Editor */}
<Editor
theme="supabase"
<CodeEditor
language={language}
autofocus={autoFocus}
className={className}
isReadOnly={readOnly}
options={options}
value={currentValue}
options={defaultOptions}
onChange={(value: string | undefined) => {
onInputChange={(value) => {
const newValue = value || ''
setCurrentValue(newValue)
onChange?.(newValue)
}}
onMount={handleEditorOnMount}
className={className}
actions={{ runQuery: { enabled: true, callback: () => executeQueryRef.current?.() } }}
/>
{promptState.isOpen && editorRef.current && (
<ResizableAIWidget
@@ -22,9 +22,20 @@ const DEFAULT_ACTIONS = {
placeholderFill: { enabled: true },
}
export type ValidLanguages =
| 'pgsql'
| 'json'
| 'html'
| 'typescript'
| 'javascript'
| 'css'
| 'csv'
| 'plaintext'
| 'markdown'
interface CodeEditorProps {
id?: string
language: 'pgsql' | 'json' | 'html' | 'typescript' | 'plaintext' | 'markdown'
language: ValidLanguages
autofocus?: boolean
defaultValue?: string
isReadOnly?: boolean
@@ -507,7 +507,7 @@ export const EditorPanel = () => {
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}
/>
</div>
{error !== undefined && (
<div className="shrink-0">
<Admonition
@@ -539,7 +538,6 @@ export const EditorPanel = () => {
/>
</div>
)}
{showWarning && (
<SqlWarningAdmonition
className="border-t"
@@ -557,7 +555,6 @@ export const EditorPanel = () => {
}}
/>
)}
{results !== undefined && results.length > 0 && (
<div
className={cn(
@@ -593,7 +590,6 @@ export const EditorPanel = () => {
</p>
</div>
)}
<div className="relative shrink-0 flex items-center gap-2 justify-end px-5 py-4 w-full border-t">
{(isUpserting || saveStatus !== 'idle') && (
<div
@@ -38,7 +38,17 @@ export const isBinaryFile = (fileName: string): boolean => {
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':