Files
Alaister YoungandAlaister Young ca2b50a0a7 chore(ui-patterns): collapse the admonition shim into ui-patterns/Admonition (#48377)
Follow-up to #48344: collapses the two resolution paths for the
Admonition module into one.

`src/admonition.tsx` was a back-compat shim re-exporting
`src/Admonition/`. Two ways to resolve one module is exactly what
produced the macOS self-import bug fixed in #48344, and the local
typecheck errors that #48374 worked around. This removes the shim and
standardizes on the PascalCase subpath, matching every other export in
the package.

**Changed:**

- Codemodded all 246 `ui-patterns/admonition` imports to
`ui-patterns/Admonition` (240 `.tsx`, 5 `.mdx`, 1 `.ts` across studio,
docs, www, design-system, and lite-studio)
- Pointed the 5 internal `'../admonition'` imports back at the
`'../Admonition'` directory

**Removed:**

- `packages/ui-patterns/src/admonition.tsx`, and its `./admonition`
entry in the exports map (regenerated with `pnpm gen:exports`)

## To test

- `grep -r "ui-patterns/admonition" --include='*.ts*'` → no hits
- `pnpm test:case-hazards` → passes
- `pnpm typecheck` → all 15 tasks green
- `pnpm --filter studio run lint:ratchet` → passes
- `pnpm --filter ui-patterns vitest run src/Admonition` → 11 tests pass

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

## Summary by CodeRabbit

* **Bug Fixes**
* Standardized Admonition component imports across the application and
documentation.
* Improved compatibility with case-sensitive environments by using the
canonical component path.
  * Removed the legacy Admonition import entry point.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-07-29 00:48:56 +08:00

254 lines
8.5 KiB
TypeScript

import { Monaco, OnMount } from '@monaco-editor/react'
import { LOCAL_STORAGE_KEYS } from 'common'
import { noop } from 'lodash'
import { RefObject, useRef } from 'react'
import { Admonition } from 'ui-patterns/Admonition'
import type { IStandaloneCodeEditor } from './SQLEditor.types'
import { useSnippetEditor } from './useSnippetEditor'
import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { getEditorSelectionParts } from '@/components/ui/AIEditor/utils'
import { CodeEditor } from '@/components/ui/CodeEditor/CodeEditor'
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state'
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
import { useIsShortcutEnabled } from '@/state/shortcuts/useIsShortcutEnabled'
import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state'
import { useSqlEditorSaveCoordinator } from '@/state/sql-editor/sql-editor-save-coordinator'
export type MonacoEditorProps = {
id: string
snippetName: string
className?: string
editorRef: RefObject<IStandaloneCodeEditor | null>
monacoRef: RefObject<Monaco | null>
autoFocus?: boolean
executeQuery: () => void
prettifyQuery: () => void
onHasSelection: (value: boolean) => void
onMount?: (editor: IStandaloneCodeEditor) => void
onPrompt?: (value: {
selection: string
beforeSelection: string
afterSelection: string
startLineNumber: number
endLineNumber: number
}) => void
placeholder?: string
}
export const MonacoEditor = ({
id,
snippetName,
editorRef,
monacoRef,
autoFocus = true,
placeholder = '',
className,
executeQuery,
prettifyQuery,
onHasSelection,
onPrompt,
onMount,
}: MonacoEditorProps) => {
const aiSnap = useAiAssistantStateSnapshot()
const { openSidebar, toggleSidebar } = useSidebarManagerSnapshot()
const [intellisenseEnabled] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.SQL_EDITOR_INTELLISENSE,
true
)
const { snippet, disableEdit, handleEditorChange } = useSnippetEditor({ id, snippetName })
// The Monaco save action is registered once on mount, but `snippet` starts
// undefined for a new/deep-linked snippet and is only created on first edit.
// Read it through a ref so Cmd/Ctrl+S sees the latest value, not the stale
// mount-time closure.
const snippetRef = useRef(snippet)
snippetRef.current = snippet
const prettifyQueryRef = useRef(prettifyQuery)
prettifyQueryRef.current = prettifyQuery
const isAIAssistantHotkeyEnabled = useIsShortcutEnabled(SHORTCUT_IDS.AI_ASSISTANT_TOGGLE)
const aiHotkeyEnabledRef = useRef(isAIAssistantHotkeyEnabled)
aiHotkeyEnabledRef.current = isAIAssistantHotkeyEnabled
const { requestSave } = useSqlEditorSaveCoordinator()
const requestSaveRef = useRef(requestSave)
requestSaveRef.current = requestSave
const handleEditorOnMount: OnMount = (editor, monaco) => {
const model = editor.getModel()
if (model !== null) {
monaco.editor.setModelMarkers(model, 'owner', [])
}
// Blur the editor on Escape so users can hop out to the rest of the UI.
// The precondition defers to Monaco's own Escape consumers (suggest widget,
// find widget, parameter hints, snippet/rename mode, inline suggestions) and
// to selection/multi-cursor cancellation, so inline features keep working.
editor.addCommand(
monaco.KeyCode.Escape,
() => {
;(document.activeElement as HTMLElement | null)?.blur()
},
[
'editorTextFocus',
'!editorHasSelection',
'!editorHasMultipleSelections',
'!suggestWidgetVisible',
'!findWidgetVisible',
'!parameterHintsVisible',
'!renameInputVisible',
'!inSnippetMode',
'!accessibilityHelpWidgetVisible',
'!inlineSuggestionVisible',
].join(' && ')
)
editor.addAction({
id: 'save-query',
label: 'Save Query',
keybindings: [monaco.KeyMod.CtrlCmd + monaco.KeyCode.KeyS],
contextMenuGroupId: 'operation',
contextMenuOrder: 0,
run: () => {
const currentSnippet = snippetRef.current
if (currentSnippet) requestSaveRef.current(currentSnippet.snippet.id)
},
})
editor.addAction({
id: 'prettify-query',
label: 'Prettify SQL',
keybindings: [monaco.KeyMod.Alt | monaco.KeyMod.Shift | monaco.KeyCode.KeyF],
contextMenuGroupId: 'operation',
contextMenuOrder: 2,
run: () => {
prettifyQueryRef.current()
},
})
editor.addAction({
id: 'explain-code',
label: 'Explain Code',
contextMenuGroupId: 'operation',
contextMenuOrder: 1,
run: () => {
const selection = editorRef?.current?.getSelection()
if (!selection) return
const selectedValue = editorRef?.current?.getModel()?.getValueInRange(selection)
openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
aiSnap.newChat({
name: 'Explain code section',
sqlSnippets: [selectedValue ?? ''],
initialInput: 'Can you explain this section to me in more detail?',
})
},
})
editor.addAction({
id: 'toggle-ai-assistant',
label: 'Toggle AI Assistant',
keybindings: [monaco.KeyMod.CtrlCmd + monaco.KeyCode.KeyI],
run: () => {
if (aiHotkeyEnabledRef.current) {
toggleSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
}
},
})
if (onPrompt) {
editor.addAction({
id: 'generate-sql',
label: 'Generate SQL',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyK],
run: () => {
const selectionParts = getEditorSelectionParts(editor)
if (selectionParts) onPrompt(selectionParts)
},
})
}
editor.onDidChangeCursorSelection(({ selection }) => {
const noSelection =
selection.startLineNumber === selection.endLineNumber &&
selection.startColumn === selection.endColumn
onHasSelection(!noSelection)
})
onMount?.(editor)
}
return (
<>
{disableEdit && (
<Admonition
type="default"
className="rounded-none border-0 border-b"
title="Read-only snippet"
description="This snippet has been shared to the project and is only editable by the owner who created this snippet. You may duplicate this snippet into a personal copy by right clicking on the snippet and selecting “Duplicate query”."
/>
)}
<CodeEditor
id={id}
language="pgsql"
className={className}
autofocus={autoFocus}
isReadOnly={disableEdit}
defaultValue={snippet?.snippet.content?.unchecked_sql}
editorRef={editorRef}
monacoRef={monacoRef}
actions={{
runQuery: { enabled: true, callback: executeQuery },
formatDocument: { enabled: false, callback: noop },
placeholderFill: { enabled: false },
}}
options={{
placeholder,
lineDecorationsWidth: 0,
fixedOverflowWidgets: false,
lineNumbersMinChars: 5,
scrollBeyondLastLine: true,
suggest: {
showMethods: intellisenseEnabled,
showFunctions: intellisenseEnabled,
showConstructors: intellisenseEnabled,
showDeprecated: intellisenseEnabled,
showFields: intellisenseEnabled,
showVariables: intellisenseEnabled,
showClasses: intellisenseEnabled,
showStructs: intellisenseEnabled,
showInterfaces: intellisenseEnabled,
showModules: intellisenseEnabled,
showProperties: intellisenseEnabled,
showEvents: intellisenseEnabled,
showOperators: intellisenseEnabled,
showUnits: intellisenseEnabled,
showValues: intellisenseEnabled,
showConstants: intellisenseEnabled,
showEnums: intellisenseEnabled,
showEnumMembers: intellisenseEnabled,
showKeywords: intellisenseEnabled,
showWords: intellisenseEnabled,
showColors: intellisenseEnabled,
showFiles: intellisenseEnabled,
showReferences: intellisenseEnabled,
showFolders: intellisenseEnabled,
showTypeParameters: intellisenseEnabled,
showIssues: intellisenseEnabled,
showUsers: intellisenseEnabled,
showSnippets: intellisenseEnabled,
},
}}
onInputChange={handleEditorChange}
onMount={handleEditorOnMount}
/>
</>
)
}