Files
supabase/apps/studio/components/ui/AIEditor/ResizableAIWidget.tsx
Joshen Lim dde45ab06c Joshenlim/fe 4195 explorer queryeditor cmd k completion support (#49248)
## Context

Adds the inline AI completion functionality into Explorer QueryEditor,
similar to what we've got for the existing SQL editor
- Shifts the `ResizableAIWidget` and `InlineWidget` components out of
the SQL Editor folder into `components/ui/AiEditor` to be used by both
SQL Editor and Query Editor
- Consolidates the "proposal" logic that was initially set up for the
Clickhouse Migration functionality with this Inline AI stuff
- Also added the prompt into the proposal header (Refer to the
screenshots below)
- SQL Editor didn't have this - but figured this is useful as context
for the user

<img width="935" height="352" alt="image"
src="https://github.com/user-attachments/assets/3f71539a-dda1-4763-be08-a850bdc8aec6"
/>

Source selected: Database
<img width="922" height="357" alt="image"
src="https://github.com/user-attachments/assets/81e772e6-dcc9-440d-83db-49d0d487dd13"
/>

Source selected: Logs
<img width="920" height="345" alt="image"
src="https://github.com/user-attachments/assets/83f1dae3-cf62-4424-be42-b06e70cb366d"
/>


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

- **New Features**
- Added AI-assisted SQL generation with contextual prompts and
OS-specific guidance.
- Review generated SQL changes in a diff, then accept, reject, or cancel
suggestions.
- Added inline, resizable AI prompt controls with loading and submission
states.
  - Added the Ctrl/Cmd+Shift+K shortcut to run AI SQL generation.

- **Improvements**
  - Added options to disable query execution and run custom actions.
  - Renamed “Recent” to “Recently updated.”
  - Improved editor widget positioning and display behavior.
  - Added clearer error notifications when AI generation fails.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-20 14:05:38 +08:00

88 lines
2.2 KiB
TypeScript

import type { editor as monacoEditor } from 'monaco-editor'
import { useCallback, useEffect, useRef, useState } from 'react'
import { AskAIWidget } from './AskAIWidget'
import { InlineWidget } from './InlineWidget'
interface ResizableAIWidgetProps {
editor: monacoEditor.IStandaloneCodeEditor | monacoEditor.IStandaloneDiffEditor
id: string
value: string
onChange: (value: string) => void
onSubmit: (prompt: string) => void
onAccept?: () => void
onReject?: () => void
onCancel?: () => void
isDiffVisible: boolean
isLoading?: boolean
startLineNumber: number
endLineNumber: number
}
const LINE_HEIGHT = 20 // height of each line in pixels
const MIN_LINES = 3 // minimum number of lines to show
export const ResizableAIWidget = ({
editor,
id,
value,
onChange,
onSubmit,
onAccept,
onReject,
onCancel,
isDiffVisible,
isLoading = false,
startLineNumber,
endLineNumber,
}: ResizableAIWidgetProps) => {
const containerRef = useRef<HTMLDivElement>(null)
const [heightInLines, setHeightInLines] = useState(MIN_LINES)
const updateHeight = useCallback(() => {
if (containerRef.current) {
const height = containerRef.current.offsetHeight
const newHeightInLines = Math.max(MIN_LINES, Math.ceil(height / LINE_HEIGHT))
setHeightInLines(newHeightInLines)
}
}, [])
useEffect(() => {
// Update height on value change
updateHeight()
// Set up resize observer to track height changes
const resizeObserver = new ResizeObserver(updateHeight)
if (containerRef.current) {
resizeObserver.observe(containerRef.current)
}
return () => {
resizeObserver.disconnect()
}
}, [updateHeight])
return (
<InlineWidget
editor={editor}
id={id}
heightInLines={heightInLines}
afterLineNumber={endLineNumber}
beforeLineNumber={Math.max(0, startLineNumber - 1)}
>
<div ref={containerRef}>
<AskAIWidget
value={value}
onChange={onChange}
onSubmit={onSubmit}
onAccept={onAccept}
onReject={onReject}
onCancel={onCancel}
isDiffVisible={isDiffVisible}
isLoading={isLoading}
/>
</div>
</InlineWidget>
)
}