瀏覽代碼

fix(monaco-diff-editor): 移除 Monaco 依赖改用 textarea + pre 简化实现

Rolldown 无法解析 monaco-editor worker 路径导致 vite build 失败。改用 textarea + pre 双栏布局满足差异对比核心需求,删除 monaco-environment.ts,避免 worker 配置复杂性。
Mochocyang 3 月之前
父節點
當前提交
0662fa4e9d
共有 2 個文件被更改,包括 37 次插入 和 88 次删除
  1. 37 56
      src/components/common/monaco-diff-editor.tsx
  2. 0 32
      src/components/common/monaco-environment.ts

+ 37 - 56
src/components/common/monaco-diff-editor.tsx

@@ -1,5 +1,4 @@
-import { useEffect, useRef, lazy, Suspense } from "react"
-import "./monaco-environment"
+import { useEffect, useRef } from "react"
 
 
 interface MonacoDiffEditorProps {
 interface MonacoDiffEditorProps {
   originalValue: string
   originalValue: string
@@ -8,74 +7,56 @@ interface MonacoDiffEditorProps {
   language?: string
   language?: string
 }
 }
 
 
-const MonacoEditor = lazy(() =>
-  import("@monaco-editor/react").then((mod) => ({ default: mod.DiffEditor })),
-)
-
 export function MonacoDiffEditor({
 export function MonacoDiffEditor({
   originalValue,
   originalValue,
   modifiedValue,
   modifiedValue,
   onChange,
   onChange,
-  language = "markdown",
 }: MonacoDiffEditorProps) {
 }: MonacoDiffEditorProps) {
-  const editorRef = useRef<Parameters<NonNullable<import("@monaco-editor/react").DiffEditorProps["onMount"]>>[0] | null>(null)
+  const textareaRef = useRef<HTMLTextAreaElement | null>(null)
   const preventSync = useRef(false)
   const preventSync = useRef(false)
 
 
   useEffect(() => {
   useEffect(() => {
-    const editor = editorRef.current
-    if (!editor) return
-    const modified = editor.getModifiedEditor()
-    const disposable = modified.onDidChangeModelContent(() => {
-      if (preventSync.current) return
-      onChange(modified.getValue())
-    })
-    return () => disposable.dispose()
-  }, [onChange])
-
-  useEffect(() => {
-    const editor = editorRef.current
-    if (!editor) return
-    const model = editor.getModel()
-    if (!model) return
-    const currentModified = model.modified.getValue()
-    if (currentModified !== modifiedValue) {
+    const textarea = textareaRef.current
+    if (!textarea) return
+    if (textarea.value !== modifiedValue) {
       preventSync.current = true
       preventSync.current = true
-      model.modified.setValue(modifiedValue)
+      textarea.value = modifiedValue
       preventSync.current = false
       preventSync.current = false
     }
     }
   }, [modifiedValue])
   }, [modifiedValue])
 
 
+  useEffect(() => {
+    const textarea = textareaRef.current
+    if (!textarea) return
+    const handler = () => {
+      if (preventSync.current) return
+      onChange(textarea.value)
+    }
+    textarea.addEventListener("input", handler)
+    return () => textarea.removeEventListener("input", handler)
+  }, [onChange])
+
   return (
   return (
-    <Suspense
-      fallback={
-        <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
-          正在加载编辑器…
+    <div className="grid h-full grid-cols-2 divide-x overflow-hidden">
+      <div className="flex flex-col overflow-hidden">
+        <div className="border-b bg-muted/40 px-3 py-1 text-xs font-medium text-muted-foreground">
+          原始内容
+        </div>
+        <pre className="flex-1 overflow-auto whitespace-pre-wrap break-words p-3 text-xs leading-relaxed text-muted-foreground">
+          {originalValue || "(该文件尚不存在)"}
+        </pre>
+      </div>
+      <div className="flex flex-col overflow-hidden">
+        <div className="border-b bg-muted/40 px-3 py-1 text-xs font-medium text-green-700 dark:text-green-400">
+          最新内容
         </div>
         </div>
-      }
-    >
-      <MonacoEditor
-        height="100%"
-        language={language}
-        original={originalValue}
-        modified={modifiedValue}
-        onMount={(editor) => {
-          editorRef.current = editor
-          editor.getModifiedEditor().updateOptions({ readOnly: false })
-        }}
-        options={{
-          readOnly: false,
-          renderSideBySide: true,
-          scrollBeyondLastLine: false,
-          minimap: { enabled: false },
-          lineNumbers: "on",
-          wordWrap: "on",
-          automaticLayout: true,
-          originalEditable: false,
-          diffCodeLens: false,
-          folding: false,
-          renderOverviewRuler: true,
-        }}
-      />
-    </Suspense>
+        <textarea
+          ref={textareaRef}
+          aria-label="最新源码"
+          className="flex-1 resize-none overflow-auto whitespace-pre-wrap break-words bg-transparent p-3 font-mono text-xs leading-relaxed outline-none"
+          spellCheck={false}
+        />
+      </div>
+    </div>
   )
   )
 }
 }

+ 0 - 32
src/components/common/monaco-environment.ts

@@ -1,32 +0,0 @@
-self.MonacoEnvironment = {
-  getWorker(_moduleId: string, label: string) {
-    if (label === "json") {
-      return new Worker(
-        new URL("monaco-editor/esm/vs/language/json/json.worker?worker", import.meta.url),
-        { type: "module" },
-      )
-    }
-    if (label === "css" || label === "scss" || label === "less") {
-      return new Worker(
-        new URL("monaco-editor/esm/vs/language/css/css.worker?worker", import.meta.url),
-        { type: "module" },
-      )
-    }
-    if (label === "html" || label === "handlebars" || label === "razor") {
-      return new Worker(
-        new URL("monaco-editor/esm/vs/language/html/html.worker?worker", import.meta.url),
-        { type: "module" },
-      )
-    }
-    if (label === "typescript" || label === "javascript") {
-      return new Worker(
-        new URL("monaco-editor/esm/vs/language/typescript/ts.worker?worker", import.meta.url),
-        { type: "module" },
-      )
-    }
-    return new Worker(
-      new URL("monaco-editor/esm/vs/editor/editor.worker?worker", import.meta.url),
-      { type: "module" },
-    )
-  },
-}