{"version":3,"file":"use-code-editor.cjs","sources":["../../../app/hooks/use-code-editor.ts"],"sourcesContent":["import { EditorState, EditorStateConfig, Extension } from \"@codemirror/state\"\nimport { EditorView, ViewUpdate } from \"@codemirror/view\"\nimport { useEffect, useId, useLayoutEffect, useRef, useCallback } from \"react\"\nimport { useExtensions } from \"./use-extensions\"\nimport { useEvent } from \"./use-event\"\nimport { useLanguageExtensions } from \"./use-language-extensions\"\nimport type { CursorPosition } from \"../code-editor\"\n\ntype Props = {\n  autoFocus?: boolean\n  customExtensions?: Extension[]\n  lineWrapping?: boolean\n  onChange?: (value: string, viewUpdate: ViewUpdate) => void\n  onCursorChange?: (position: CursorPosition) => void\n  onEditorUpdate?: (update: ViewUpdate) => void\n  onFormat?: (formattedCode: string) => void\n  onSelectionChange?: (characterCount: number) => void\n  readonly?: boolean\n  value?: string\n}\n\nexport function useCodeEditor(props: Props) {\n  const { \n    value, \n    customExtensions, \n    autoFocus, \n    onChange, \n    readonly, \n    lineWrapping,\n    onEditorUpdate, \n    onFormat, \n    onCursorChange,\n    onSelectionChange,\n  } = props\n\n  const id = useId()\n\n  const containerRef = useRef<HTMLDivElement>(null)\n  const editorViewRef = useRef<EditorView | null>(null)\n  const isInternalChangeRef = useRef(false)\n\n  const getCursorPosition = useCallback((): CursorPosition | null => {\n    const editor = editorViewRef.current\n    if (!editor) return null\n\n    const { state } = editor\n    const pos = state.selection.main.head\n    const lineInfo = state.doc.lineAt(pos)\n    \n    return {\n      column: pos - lineInfo.from + 1,\n      line: lineInfo.number,\n    }\n  }, [])\n\n  const getSelectedCharacterCount = useCallback((): number => {\n    const editor = editorViewRef.current\n    if (!editor) return 0\n\n    const { state } = editor\n    const { from, to } = state.selection.main\n    \n    return to - from\n  }, [])\n\n  const extensions = useExtensions({\n    readonly,\n    lineWrapping,\n    customExtensions,\n    onChange: (newValue, update) => {\n      isInternalChangeRef.current = true\n      onChange?.(newValue, update)\n    },\n    onCursorChange,\n    onEditorUpdate: onEditorUpdate ?? (() => {}),\n    onFormat,\n    onSelectionChange,\n  })\n\n  const { getLanguageExtensions } = useLanguageExtensions({\n    id,\n  })\n\n  const { blurOnClickOutside } = useEvent(editorViewRef)\n\n  const createEditor = useCallback(async () => {\n    if (!containerRef.current) return\n\n    // 加载完整的 TypeScript 语言功能\n    const languageExtensions = await getLanguageExtensions(value ?? \"\")\n    \n    const config: EditorStateConfig = {\n      doc: value ?? \"\",\n      extensions: [...extensions, ...languageExtensions],\n    }\n    const parent = containerRef.current\n    const state = EditorState.create(config)\n\n    const editorView = new EditorView({\n      parent,\n      state,\n    })\n\n    editorViewRef.current = editorView\n  }, [extensions, getLanguageExtensions, value])\n\n  // init & deinit - 只在挂载时创建一次\n  useLayoutEffect(() => {\n    createEditor()\n\n    return () => {\n      if (editorViewRef.current) {\n        editorViewRef.current.destroy()\n        editorViewRef.current = null\n      }\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n\n  // 当外部 value 改变时，更新编辑器内容\n  useEffect(() => {\n    const editor = editorViewRef.current\n    if (!editor) return\n\n    // 如果这是内部变化触发的更新，跳过\n    if (isInternalChangeRef.current) {\n      isInternalChangeRef.current = false\n      return\n    }\n\n    const currentValue = editor.state.doc.toString()\n    const newValue = value ?? \"\"\n\n    // 只在值真正不同时才更新\n    if (currentValue !== newValue) {\n      editor.dispatch({\n        changes: {\n          from: 0,\n          to: currentValue.length,\n          insert: newValue,\n        },\n      })\n    }\n  }, [value])\n\n  useEffect(() => {\n    if (autoFocus && editorViewRef.current) {\n      editorViewRef.current.focus()\n    }\n  }, [autoFocus])\n\n  useEffect(() => {\n    document.addEventListener(\"click\", blurOnClickOutside)\n\n    return () => {\n      document.removeEventListener(\"click\", blurOnClickOutside)\n    }\n  }, [blurOnClickOutside])\n\n  return {\n    containerRef,\n    editorViewRef,\n    getCursorPosition,\n    getSelectedCharacterCount,\n  }\n}\n"],"names":["useId","useRef","useCallback","useExtensions","useLanguageExtensions","useEvent","EditorState","EditorView","useLayoutEffect","useEffect"],"mappings":"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"}