Output:
{
  "filePath": "/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx",
  "oldString": "  const shouldStartEditing = (Boolean((openTableId ?? \"\").trim()) || autoEnterEdit) && !readOnly;\n  const [isEditing, setIsEditing] = useState(() => shouldStartEditing);\n  const [keepEditorMounted, setKeepEditorMounted] = useState(() => shouldStartEditing);\n",
  "newString": "  const shouldStartEditing = canEditDocument;\n  const [isEditing, setIsEditing] = useState(() => shouldStartEditing);\n  const [keepEditorMounted, setKeepEditorMounted] = useState(() => shouldStartEditing);\n",
  "originalFile": "\"use client\";\n\nimport dynamic from \"next/dynamic\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent, type KeyboardEvent as ReactKeyboardEvent } from \"react\";\nimport type { BooleanPageOptionKey, DocumentStats, PageOptionsState, PageFont, PageLayoutDensity } from \"@/types/page-options\";\nimport { useEditorBridgeStore } from \"@/store/editor-bridge\";\nimport { usePageLayoutStore } from \"@/store/page-layout\";\nimport { useCurrentDocumentStore } from \"@/store/current-document\";\nimport type { Json } from \"@/types/supabase\";\nimport type { DocumentSnapshot } from \"@/types/document\";\nimport { useDebouncedCallback } from \"@/hooks/use-debounced-callback\";\nimport { ImagePickerProvider } from \"@/components/media/image-picker-context\";\nimport { useRouter } from \"next/navigation\";\nimport { DocumentAiAgentPanel } from \"./DocumentAiAgentPanel\";\nimport { CONTENT_LOADING_DELAY_MS } from \"@/lib/constants\";\nimport { useCommentsUiStore } from \"@/store/comments-ui\";\nimport { useAppPreferencesStore } from \"@/store/app-preferences\";\nimport { useMoveEmbedPickerStore } from \"@/store/move-embed-picker\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { DocumentToc } from \"@/components/editor/document-toc\";\nimport { DocumentReadView } from \"@/components/editor/document-read-view\";\nimport { extractPageBlocks, type PageSubtreeProjection } from \"@/lib/documents/page-subtree\";\nimport {\n  deleteDocumentCommand,\n  embedDocumentCommand,\n  moveDocumentCommand,\n  renameDocumentCommand,\n} from \"@/lib/documents/tree-command-client\";\nimport { EditorHost } from \"@/components/editor/editor-host\";\n\nconst PageOptionsSidebar = dynamic(\n  () => import(\"@/components/editor/page-options-sidebar\").then((mod) => mod.PageOptionsSidebar),\n  { ssr: false },\n);\n\nconst PageBacklinksPanel = dynamic(\n  () => import(\"@/components/editor/page-backlinks-panel\").then((mod) => mod.PageBacklinksPanel),\n  {\n    ssr: false,\n    loading: () => null,\n  },\n);\n\nconst DocumentHistoryDrawer = dynamic(\n  () => import(\"@/components/editor/document-history-drawer\").then((mod) => mod.DocumentHistoryDrawer),\n  {\n    ssr: false,\n    loading: () => null,\n  },\n);\n\nconst MoveEmbedPickerHost = dynamic(\n  () => import(\"@/components/documents/move-embed-picker-host\").then((mod) => mod.MoveEmbedPickerHost),\n  {\n    ssr: false,\n    loading: () => null,\n  },\n);\n\nconst DocumentCommentsDrawer = dynamic(\n  () => import(\"@/components/editor/document-comments-drawer\").then((mod) => mod.DocumentCommentsDrawer),\n  {\n    ssr: false,\n    loading: () => null,\n  },\n);\n\nexport interface DocumentContentProps {\n  documentId: string;\n  workspaceId: string;\n  title: string | null;\n  updatedAt: string | null;\n  initialContent: unknown;\n  initialContentRevision?: number | null;\n  initialConflictDetectionKey?: string | null;\n  initialPageSubtree?: PageSubtreeProjection | null;\n  initialOptions: PageOptionsState;\n  initialStats: DocumentStats | null;\n  openTableId?: string | null;\n  autoEnterEdit?: boolean;\n  readOnly?: boolean;\n  disableDownload?: boolean;\n  disableCopy?: boolean;\n}\n\nconst defaultOptions: PageOptionsState = {\n  wideLayout: false,\n  smallText: false,\n  showHeadingNumbers: true,\n  showToc: false,\n  showStructure: false,\n  protectEditing: false,\n  showWordCount: true,\n  collapseBacklinks: false,\n  pageFont: \"default\",\n  layoutDensity: \"normal\",\n  hideChildPages: false,\n  showBlockRefCount: false,\n  embedDefaultBlockId: null,\n};\nconst defaultStats: DocumentStats = { wordCount: 0, characterCount: 0, blockCount: 0, todoTotal: 0, todoDone: 0 };\nconst EDITOR_UNMOUNT_GRACE_MS = 1000;\n\nexport function DocumentContent({\n  documentId,\n  workspaceId,\n  title,\n  updatedAt,\n  initialContent,\n  initialContentRevision = null,\n  initialConflictDetectionKey = null,\n  initialPageSubtree = null,\n  initialOptions,\n  initialStats,\n  openTableId,\n  autoEnterEdit = false,\n  readOnly = false,\n  disableDownload = false,\n  disableCopy = false,\n}: DocumentContentProps) {\n  const setCurrentDocument = useCurrentDocumentStore((state) => state.setCurrent);\n  const clearIfMatch = useCurrentDocumentStore((state) => state.clearIfMatch);\n  const canEditDocument = !readOnly;\n  const [options, setOptions] = useState<PageOptionsState>(initialOptions ?? defaultOptions);\n  const [stats, setStats] = useState<DocumentStats>(initialStats ?? defaultStats);\n  const [history, setHistory] = useState<DocumentSnapshot[]>([]);\n  const [historyOpen, setHistoryOpen] = useState(false);\n  const editorBridge = useEditorBridgeStore((state) => state.bridge);\n  const router = useRouter();\n  const showInspector = usePageLayoutStore((state) => state.showInspector);\n  const openCommentsForPage = useCommentsUiStore((s) => s.openForPage);\n  const spellCheck = useAppPreferencesStore((s) => s.spellCheck);\n  const openMoveEmbedPicker = useMoveEmbedPickerStore((s) => s.openPicker);\n  const [pageTitle, setPageTitle] = useState(title ?? \"无标题\");\n  const [content, setContent] = useState<unknown>(initialContent);\n  const [serverContentSnapshot, setServerContentSnapshot] = useState<unknown>(initialContent);\n  const [serverPageSubtreeSnapshot, setServerPageSubtreeSnapshot] = useState<PageSubtreeProjection | null>(\n    initialPageSubtree,\n  );\n  const [serverPageSubtreeTitle, setServerPageSubtreeTitle] = useState<string>(title ?? \"无标题\");\n  const [contentRevision, setContentRevision] = useState<number | null>(initialContentRevision);\n  const [conflictDetectionKey, setConflictDetectionKey] = useState<string | null>(initialConflictDetectionKey);\n  const [contentLoading, setContentLoading] = useState(() => initialContent == null);\n  const [contentError, setContentError] = useState<string | null>(null);\n  const [contentReloadKey, setContentReloadKey] = useState(0);\n  const [showContentLoadingIndicator, setShowContentLoadingIndicator] = useState(false);\n  const shouldStartEditing = (Boolean((openTableId ?? \"\").trim()) || autoEnterEdit) && !readOnly;\n  const [isEditing, setIsEditing] = useState(() => shouldStartEditing);\n  const [keepEditorMounted, setKeepEditorMounted] = useState(() => shouldStartEditing);\n  const contentLoadingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const editorUnmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const pendingOpenTableRef = useRef<string | null>(null);\n  const latestBlocksRef = useRef<Json | null>(null);\n  const pageRootRef = useRef<HTMLDivElement>(null);\n  const readViewRootRef = useRef<HTMLDivElement>(null);\n  const pendingRestoreSnapshotRef = useRef<DocumentSnapshot | null>(null);\n  const lastCopyBlockedAtRef = useRef<number>(0);\n\n  useEffect(() => {\n    setCurrentDocument(documentId, readOnly, disableDownload, disableCopy);\n    return () => {\n      clearIfMatch(documentId);\n    };\n  }, [clearIfMatch, disableCopy, disableDownload, documentId, readOnly, setCurrentDocument]);\n\n  useEffect(() => {\n    if (!disableCopy) return;\n\n    const toElement = (node: Node | null): Element | null => {\n      if (!node) return null;\n      if (node.nodeType === Node.TEXT_NODE) {\n        return node.parentElement;\n      }\n      return node instanceof Element ? node : null;\n    };\n\n    const isEventInsidePage = () => {\n      const root = pageRootRef.current;\n      if (!root) return false;\n      const selection = typeof window !== \"undefined\" ? window.getSelection() : null;\n      const anchor = selection?.anchorNode ?? null;\n      const focus = selection?.focusNode ?? null;\n      const anchorEl = toElement(anchor);\n      const focusEl = toElement(focus);\n      return Boolean((anchorEl && root.contains(anchorEl)) || (focusEl && root.contains(focusEl)));\n    };\n\n    const notifyBlocked = () => {\n      const now = Date.now();\n      if (now - lastCopyBlockedAtRef.current < 1200) return;\n      lastCopyBlockedAtRef.current = now;\n      window.alert(\"该页面已禁止复制\");\n    };\n\n    const onCopy = (event: ClipboardEvent) => {\n      if (!isEventInsidePage()) return;\n      event.preventDefault();\n      event.stopPropagation();\n      notifyBlocked();\n    };\n\n    const onCut = (event: ClipboardEvent) => {\n      if (!isEventInsidePage()) return;\n      event.preventDefault();\n      event.stopPropagation();\n      notifyBlocked();\n    };\n\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (!isEventInsidePage()) return;\n      const key = String(event.key ?? \"\").toLowerCase();\n      const ctrlOrMeta = event.ctrlKey || event.metaKey;\n      if (!ctrlOrMeta) return;\n      if (key === \"c\" || key === \"x\" || key === \"insert\") {\n        event.preventDefault();\n        event.stopPropagation();\n        notifyBlocked();\n      }\n    };\n\n    document.addEventListener(\"copy\", onCopy, true);\n    document.addEventListener(\"cut\", onCut, true);\n    document.addEventListener(\"keydown\", onKeyDown, true);\n    return () => {\n      document.removeEventListener(\"copy\", onCopy, true);\n      document.removeEventListener(\"cut\", onCut, true);\n      document.removeEventListener(\"keydown\", onKeyDown, true);\n    };\n  }, [disableCopy]);\n\n  useEffect(() => {\n    const tableId = (openTableId ?? \"\").trim();\n    if (!tableId) return;\n    if (!editorBridge?.openTableFullScreen) return;\n    if (pendingOpenTableRef.current === tableId) return;\n    pendingOpenTableRef.current = tableId;\n\n    editorBridge.openTableFullScreen(tableId);\n\n    if (typeof window !== \"undefined\") {\n      try {\n        const url = new URL(window.location.href);\n        url.searchParams.delete(\"openTableId\");\n        window.history.replaceState({}, \"\", url.toString());\n      } catch {\n        router.replace(`/documents/${documentId}`);\n      }\n    }\n  }, [documentId, editorBridge, openTableId, router]);\n\n  useEffect(() => {\n    setPageTitle(title ?? \"无标题\");\n  }, [title]);\n\n  useEffect(() => {\n    setServerPageSubtreeTitle(title ?? \"无标题\");\n  }, [title]);\n\n  useEffect(() => {\n    setServerPageSubtreeSnapshot(initialPageSubtree);\n  }, [initialPageSubtree]);\n\n  useEffect(() => {\n    setOptions(initialOptions ?? defaultOptions);\n  }, [initialOptions]);\n\n  useEffect(() => {\n    setStats(initialStats ?? defaultStats);\n  }, [initialStats]);\n\n  useEffect(() => {\n    setContentRevision(initialContentRevision);\n  }, [initialContentRevision]);\n\n  useEffect(() => {\n    setConflictDetectionKey(initialConflictDetectionKey);\n  }, [initialConflictDetectionKey]);\n\n  useEffect(() => {\n    setServerContentSnapshot(initialContent);\n  }, [initialContent]);\n\n  useEffect(() => {\n    const nextBlocks = extractPageBlocks(content);\n    latestBlocksRef.current = nextBlocks.length > 0 ? (nextBlocks as Json) : null;\n  }, [content]);\n\n  useEffect(() => {\n    const shouldForceEdit = Boolean((openTableId ?? \"\").trim()) && canEditDocument;\n    if (shouldForceEdit) {\n      setIsEditing(true);\n      setKeepEditorMounted(true);\n    }\n  }, [canEditDocument, openTableId]);\n\n  useEffect(() => {\n    if (isEditing) {\n      if (editorUnmountTimerRef.current) {\n        clearTimeout(editorUnmountTimerRef.current);\n        editorUnmountTimerRef.current = null;\n      }\n      setKeepEditorMounted(true);\n      return;\n    }\n    if (editorUnmountTimerRef.current) {\n      clearTimeout(editorUnmountTimerRef.current);\n    }\n    editorUnmountTimerRef.current = setTimeout(() => {\n      setKeepEditorMounted(false);\n      editorUnmountTimerRef.current = null;\n    }, EDITOR_UNMOUNT_GRACE_MS);\n    return () => {\n      if (editorUnmountTimerRef.current) {\n        clearTimeout(editorUnmountTimerRef.current);\n        editorUnmountTimerRef.current = null;\n      }\n    };\n  }, [isEditing]);\n\n  useEffect(() => {\n    let canceled = false;\n    const controller = new AbortController();\n\n    const load = async () => {\n      setContentError(null);\n      setContentLoading(initialContent == null);\n      setContent(initialContent);\n      setShowContentLoadingIndicator(false);\n\n      if (initialContent != null) {\n        return;\n      }\n\n      if (contentLoadingTimerRef.current) {\n        clearTimeout(contentLoadingTimerRef.current);\n        contentLoadingTimerRef.current = null;\n      }\n      contentLoadingTimerRef.current = setTimeout(() => {\n        if (!canceled) {\n          setShowContentLoadingIndicator(true);\n        }\n      }, CONTENT_LOADING_DELAY_MS);\n\n      try {\n        const response = await fetch(\n          `/api/documents/content?documentId=${encodeURIComponent(documentId)}&workspaceId=${encodeURIComponent(workspaceId)}`,\n          {\n            method: \"GET\",\n            credentials: \"include\",\n            signal: controller.signal,\n          },\n        );\n        if (!response.ok) {\n          const payload = await response.json().catch(() => ({}));\n          throw new Error(payload?.error ?? \"加载页面内容失败\");\n        }\n        const payload = (await response.json()) as {\n          content?: unknown;\n          revision?: number | null;\n          conflictDetectionKey?: string | null;\n          pageSubtree?: PageSubtreeProjection | null;\n        };\n        if (canceled) return;\n        setContent(payload.content ?? null);\n        setServerContentSnapshot(payload.content ?? null);\n        setContentRevision(\n          typeof payload.revision === \"number\" && Number.isInteger(payload.revision)\n            ? payload.revision\n            : 0,\n        );\n        setConflictDetectionKey(\n          typeof payload.conflictDetectionKey === \"string\" && payload.conflictDetectionKey.trim()\n            ? payload.conflictDetectionKey\n            : `${documentId}:0`,\n        );\n        setServerPageSubtreeSnapshot(payload.pageSubtree ?? null);\n        setServerPageSubtreeTitle(\n          typeof payload.pageSubtree?.rootNode.metadata.title === \"string\" &&\n            payload.pageSubtree.rootNode.metadata.title.trim()\n            ? payload.pageSubtree.rootNode.metadata.title\n            : title ?? \"无标题\",\n        );\n      } catch (error) {\n        if (canceled) return;\n        if ((error as { name?: string })?.name === \"AbortError\") return;\n        setContentError(error instanceof Error ? error.message : \"加载页面内容失败\");\n      } finally {\n        if (!canceled) {\n          setContentLoading(false);\n          setShowContentLoadingIndicator(false);\n        }\n        if (contentLoadingTimerRef.current) {\n          clearTimeout(contentLoadingTimerRef.current);\n          contentLoadingTimerRef.current = null;\n        }\n      }\n    };\n\n    void load();\n\n    return () => {\n      canceled = true;\n      controller.abort();\n      if (contentLoadingTimerRef.current) {\n        clearTimeout(contentLoadingTimerRef.current);\n        contentLoadingTimerRef.current = null;\n      }\n    };\n  }, [documentId, initialContent, contentReloadKey, title, workspaceId]);\n\n  const persistTitle = useCallback(\n    async (nextTitle: string) => {\n      if (readOnly) return;\n      const payload = nextTitle.trim() || \"无标题\";\n      try {\n        await renameDocumentCommand({ documentId, workspaceId, title: payload });\n      } catch (error) {\n        console.error(\"更新页面标题失败\", error);\n      }\n    },\n    [documentId, readOnly, workspaceId],\n  );\n\n  const debouncedPersistTitle = useDebouncedCallback((value: string) => {\n    void persistTitle(value);\n  }, 600);\n\n  const handleTitleChange = (event: ChangeEvent<HTMLInputElement>) => {\n    if (!canEditDocument) return;\n    const value = event.target.value;\n    setPageTitle(value);\n    debouncedPersistTitle(value);\n  };\n\n  const handleTitleBlur = () => {\n    if (!canEditDocument) return;\n    void persistTitle(pageTitle);\n  };\n\n  const handleTitleKeyDown = (event: ReactKeyboardEvent<HTMLInputElement>) => {\n    if (event.key === \"Enter\") {\n      event.preventDefault();\n      event.currentTarget.blur();\n    }\n  };\n\n  const persistOptions = useCallback(\n    async (patch: Partial<PageOptionsState>) => {\n      if (readOnly) return;\n      try {\n        const response = await fetch(\"/api/documents/options\", {\n          method: \"POST\",\n          headers: { \"Content-Type\": \"application/json\" },\n          body: JSON.stringify({ documentId, workspaceId, options: patch }),\n        });\n        if (!response.ok) {\n          const payload = await response.json().catch(() => null);\n          console.error(payload?.error ?? \"更新页面选项失败\");\n        }\n      } catch (error) {\n        console.error(error);\n      }\n    },\n    [documentId, readOnly, workspaceId],\n  );\n\n  const toggleOption = useCallback(\n    (key: BooleanPageOptionKey) => {\n      if (readOnly) return;\n      setOptions((prev) => {\n        const nextValue = !prev[key];\n        const next = { ...prev, [key]: nextValue };\n        void persistOptions({ [key]: nextValue } as Partial<PageOptionsState>);\n        return next;\n      });\n    },\n    [persistOptions, readOnly],\n  );\n\n  const setOptionPatch = useCallback(\n    (patch: Partial<PageOptionsState>) => {\n      if (readOnly) return;\n      setOptions((prev) => {\n        const next = { ...prev, ...patch };\n        void persistOptions(patch);\n        return next;\n      });\n    },\n    [persistOptions, readOnly],\n  );\n\n  const closeToc = useCallback(() => {\n    if (readOnly) return;\n    setOptions((prev) => {\n      if (!prev.showToc) return prev;\n      const next = { ...prev, showToc: false };\n      void persistOptions({ showToc: false });\n      return next;\n    });\n  }, [persistOptions, readOnly]);\n\n  const handleSetPageFont = useCallback(\n    (font: PageFont) => {\n      setOptionPatch({ pageFont: font });\n    },\n    [setOptionPatch],\n  );\n\n  const handleSetLayoutDensity = useCallback(\n    (density: PageLayoutDensity) => {\n      setOptionPatch({ layoutDensity: density });\n    },\n    [setOptionPatch],\n  );\n\n  const handleSetEmbedDefaultToCursor = useCallback(() => {\n    if (!canEditDocument) return;\n    if (!isEditing) {\n      setIsEditing(true);\n      return;\n    }\n    const blockId = editorBridge?.getCursorBlockId?.() ?? null;\n    if (!blockId) {\n      window.alert(\"未找到当前光标所在块，请先在编辑器中点击任意块\");\n      return;\n    }\n    setOptionPatch({ embedDefaultBlockId: blockId });\n    window.alert(\"已设置“嵌入默认位置”\");\n  }, [canEditDocument, editorBridge, isEditing, setOptionPatch]);\n\n  const handleClearEmbedDefault = useCallback(() => {\n    if (readOnly) return;\n    setOptionPatch({ embedDefaultBlockId: null });\n    window.alert(\"已清除“嵌入默认位置”\");\n  }, [readOnly, setOptionPatch]);\n\n  useEffect(() => {\n    const onKeyDown = (event: KeyboardEvent) => {\n      const root = pageRootRef.current;\n      if (root) {\n        const target = event.target;\n        if (target && target instanceof Node && !root.contains(target)) {\n          return;\n        }\n      }\n      const ctrlOrMeta = event.ctrlKey || event.metaKey;\n      if (!ctrlOrMeta) return;\n      if (!event.shiftKey) return;\n      const key = String(event.key ?? \"\").toLowerCase();\n      if (key === \"l\") {\n        event.preventDefault();\n        toggleOption(\"showToc\");\n      } else if (key === \"c\") {\n        event.preventDefault();\n        toggleOption(\"protectEditing\");\n      }\n    };\n    window.addEventListener(\"keydown\", onKeyDown);\n    return () => window.removeEventListener(\"keydown\", onKeyDown);\n  }, [toggleOption]);\n\n  const buildDocumentUrl = useCallback((id: string): string => {\n    if (typeof window === \"undefined\" || !window.location) {\n      return `/documents/${id}`;\n    }\n    return `${window.location.origin}/documents/${id}`;\n  }, []);\n\n  const copyText = useCallback(async (text: string, successMessage: string) => {\n    if (typeof navigator !== \"undefined\" && navigator.clipboard) {\n      try {\n        await navigator.clipboard.writeText(text);\n        window.alert(successMessage);\n        return;\n      } catch {\n      }\n    }\n    window.prompt(\"复制失败，请手动复制内容\", text);\n  }, []);\n\n  const handleCopyPageLink = useCallback(\n    async (includeTitle: boolean) => {\n      const url = buildDocumentUrl(documentId);\n      if (includeTitle) {\n        const text = `${pageTitle || \"无标题\"}\\n${url}`;\n        await copyText(text, \"标题 + 链接已复制\");\n        return;\n      }\n      await copyText(url, \"页面链接已复制\");\n    },\n    [buildDocumentUrl, copyText, documentId, pageTitle],\n  );\n\n  const handleCopyPageReference = useCallback(\n    async (mode: \"inline\" | \"embed\") => {\n      const template = mode === \"inline\" ? `((${documentId}))` : `{{${documentId}}}`;\n      await copyText(template, mode === \"inline\" ? \"行内引用已复制\" : \"嵌入引用已复制\");\n    },\n    [copyText, documentId],\n  );\n\n  const handleUndo = useCallback(() => {\n    if (!isEditing) {\n      setIsEditing(true);\n      return;\n    }\n    editorBridge?.undo?.();\n  }, [editorBridge, isEditing]);\n\n  const handleRedo = useCallback(() => {\n    if (!isEditing) {\n      setIsEditing(true);\n      return;\n    }\n    editorBridge?.redo?.();\n  }, [editorBridge, isEditing]);\n\n  const handleDeletePage = useCallback(async () => {\n    if (readOnly) return;\n    const ok = window.confirm(\"确定删除该页面吗？删除后会进入垃圾桶。\");\n    if (!ok) return;\n    try {\n      await deleteDocumentCommand({ documentId, workspaceId });\n    } catch (error) {\n      const payload = error instanceof Error ? error.message : null;\n      window.alert(payload ?? \"删除失败\");\n      return;\n    }\n    router.push(\"/\");\n    router.refresh();\n  }, [documentId, readOnly, router, workspaceId]);\n\n  const handleOpenMoveEmbed = useCallback(() => {\n    if (readOnly) return;\n    openMoveEmbedPicker({\n      workspaceId,\n      defaultMode: \"move\",\n      modes: [\"move\", \"embed\"],\n      allowRoot: true,\n      excludeIds: [documentId],\n      onPick: async (mode, targetId) => {\n        if (mode === \"move\") {\n          try {\n            await moveDocumentCommand({\n              documentId,\n              parentId: targetId ?? null,\n              position: 999999,\n              workspaceId,\n            });\n          } catch (error) {\n            window.alert(error instanceof Error ? error.message : \"移动失败\");\n            return;\n          }\n          window.alert(\"移动成功\");\n          router.refresh();\n          return;\n        }\n\n        if (!targetId) {\n          window.alert(\"请选择目标页面\");\n          return;\n        }\n\n        try {\n          await embedDocumentCommand({\n            sourceId: documentId,\n            targetId,\n          });\n        } catch (error) {\n          const message = error instanceof Error ? error.message : \"嵌入失败\";\n          window.alert(message);\n          return;\n        }\n\n        window.alert(\"已嵌入到目标页面\");\n      },\n    });\n  }, [documentId, openMoveEmbedPicker, readOnly, router, workspaceId]);\n\n  const handleAddToTemplates = useCallback(async () => {\n    if (readOnly) return;\n    const ok = window.confirm(\"将该页面添加为模板？\");\n    if (!ok) return;\n    const resp = await fetch(\"/api/documents/template\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({ documentId, isTemplate: true }),\n    });\n    if (!resp.ok) {\n      const payload = await resp.json().catch(() => ({}));\n      window.alert(payload?.error ?? \"设置模板失败\");\n      return;\n    }\n    window.alert(\"已添加为模板\");\n    router.refresh();\n  }, [documentId, readOnly, router]);\n\n  const formattedUpdatedAt = useMemo(() => {\n    if (!updatedAt) return \"\";\n    const date = new Date(updatedAt);\n    if (Number.isNaN(date.getTime())) return \"\";\n    return new Intl.DateTimeFormat(\"zh-CN\", {\n      timeZone: \"Asia/Shanghai\",\n      year: \"numeric\",\n      month: \"numeric\",\n      day: \"numeric\",\n      hour: \"2-digit\",\n      minute: \"2-digit\",\n      second: \"2-digit\",\n      hour12: false,\n    }).format(date);\n  }, [updatedAt]);\n\n  const handleExport = useCallback(() => {\n    if (disableDownload) {\n      window.alert(\"该页面已禁止下载\");\n      return;\n    }\n    const latest = history[0];\n    const exportBlocks = latest?.blocks ?? latestBlocksRef.current;\n    if (!exportBlocks) {\n      window.alert(\"暂无可导出的内容\");\n      return;\n    }\n    const payload = JSON.stringify(exportBlocks, null, 2);\n    const blob = new Blob([payload], { type: \"application/json\" });\n    const url = URL.createObjectURL(blob);\n    const anchor = document.createElement(\"a\");\n    anchor.href = url;\n    anchor.download = `${title ?? \"未命名页面\"}-${new Date().toISOString()}.json`;\n    anchor.click();\n    URL.revokeObjectURL(url);\n  }, [disableDownload, history, title]);\n\n  const pageRootClass = cn(\n    \"flex h-full overflow-hidden bg-wolai-bg\",\n    options.pageFont === \"song\" && \"wolai-page-font-song\",\n    options.pageFont === \"kai\" && \"wolai-page-font-kai\",\n    options.layoutDensity === \"compact\" && \"wolai-page-density-compact\",\n    options.layoutDensity === \"spacious\" && \"wolai-page-density-spacious\",\n    options.smallText && \"wolai-small-text\",\n    options.hideChildPages && \"wolai-hide-child-pages\",\n  );\n  const pageSubtree = useMemo(() => {\n    const hasServerPageSubtree = Boolean(serverPageSubtreeSnapshot);\n    const titleUnchanged = pageTitle === serverPageSubtreeTitle;\n    const contentUnchanged = content === serverContentSnapshot;\n\n    if (hasServerPageSubtree && titleUnchanged && contentUnchanged) {\n      return serverPageSubtreeSnapshot;\n    }\n    return null;\n  }, [\n    content,\n    pageTitle,\n    serverContentSnapshot,\n    serverPageSubtreeSnapshot,\n    serverPageSubtreeTitle,\n  ]);\n  const readViewTocEntries = useMemo(\n    () =>\n      (pageSubtree?.outline ?? [])\n        .filter((entry) => typeof entry.anchorBlockId === \"string\" && entry.anchorBlockId.trim())\n        .map(({ anchorBlockId, level, numbering, title: entryTitle }) => ({\n          id: anchorBlockId as string,\n          level,\n          numbering,\n          title: entryTitle,\n        })),\n    [pageSubtree],\n  );\n  const inspectorCanUseEditorBridge = canEditDocument && isEditing;\n\n  const jumpToHeading = useCallback((headingId: string) => {\n    const targetRoot = !isEditing ? readViewRootRef.current : pageRootRef.current;\n    const target = targetRoot?.querySelector<HTMLElement>(`[data-id=\"${headingId}\"]`);\n    if (target) {\n      target.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n    }\n  }, [isEditing]);\n\n  const handleSnapshot = useCallback((payload: { blocks: Json; stats: DocumentStats }) => {\n    setContent(payload.blocks);\n    latestBlocksRef.current = payload.blocks;\n    setHistory((prev) => {\n      const now = Date.now();\n      if (prev.length > 0 && now - prev[0].timestamp < 4000) {\n        return prev;\n      }\n      const snapshot: DocumentSnapshot = {\n        id: `${now}-${Math.random().toString(36).slice(2, 8)}`,\n        timestamp: now,\n        blocks: payload.blocks,\n        stats: payload.stats,\n      };\n      return [snapshot, ...prev].slice(0, 15);\n    });\n  }, []);\n\n  const persistStatsRequest = useCallback((next: DocumentStats) => {\n    void fetch(\"/api/documents/stats\", {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({ documentId, workspaceId, stats: next }),\n    }).catch((error) => console.error(error));\n  }, [documentId, workspaceId]);\n\n  const persistStats = useDebouncedCallback(persistStatsRequest, 1500);\n\n  const handleStatsChange = useCallback(\n    (nextStats: DocumentStats) => {\n      setStats(nextStats);\n      persistStats(nextStats);\n    },\n    [persistStats],\n  );\n\n  const handleRestoreSnapshot = useCallback(\n    (snapshot: DocumentSnapshot) => {\n      if (!isEditing || !editorBridge) {\n        pendingRestoreSnapshotRef.current = snapshot;\n        setIsEditing(true);\n        return;\n      }\n      editorBridge.replaceWithSnapshot(snapshot.blocks);\n      setHistoryOpen(false);\n    },\n    [editorBridge, isEditing],\n  );\n\n  const handleEnterEditMode = useCallback(() => {\n    if (!canEditDocument) return;\n    setIsEditing(true);\n  }, [canEditDocument]);\n\n  const handleExitEditMode = useCallback(() => {\n    setIsEditing(false);\n  }, []);\n\n  useEffect(() => {\n    if (!isEditing) return;\n    if (!editorBridge) return;\n    const pendingSnapshot = pendingRestoreSnapshotRef.current;\n    if (!pendingSnapshot) return;\n    pendingRestoreSnapshotRef.current = null;\n    editorBridge.replaceWithSnapshot(pendingSnapshot.blocks);\n    setHistoryOpen(false);\n  }, [editorBridge, isEditing]);\n  return (\n    <ImagePickerProvider documentId={documentId} workspaceId={workspaceId}>\n      <div className={pageRootClass} ref={pageRootRef}>\n        <div className=\"flex h-full flex-1 flex-col overflow-hidden\">\n          <div className=\"border-b border-wolai-border px-12 pb-6 pt-8\">\n            <div className=\"flex items-start justify-between gap-6\">\n              <div className=\"min-w-0 flex-1\">\n                {isEditing && canEditDocument ? (\n                  <div className=\"relative\">\n                    <input\n                      value={pageTitle}\n                      onChange={handleTitleChange}\n                      onBlur={handleTitleBlur}\n                      onKeyDown={handleTitleKeyDown}\n                      placeholder=\"无标题\"\n                      className=\"w-full border-none bg-transparent text-3xl font-semibold text-wolai-text-primary outline-none focus:ring-0\"\n                      aria-label=\"页面标题\"\n                      disabled={options.protectEditing || readOnly}\n                      spellCheck={spellCheck}\n                    />\n                  </div>\n                ) : (\n                  <h1 className=\"break-words text-3xl font-semibold text-wolai-text-primary\">\n                    {pageTitle || \"无标题\"}\n                  </h1>\n                )}\n                {readOnly ? (\n                  <p className=\"mt-1 text-sm text-gray-500\">该页面为只读共享，无法编辑。</p>\n                ) : options.protectEditing && isEditing ? (\n                  <p className=\"mt-1 text-sm text-[#b91c1c]\">当前页面已开启编辑保护，关闭后方可修改内容。</p>\n                ) : !isEditing && canEditDocument ? (\n                  <p className=\"mt-1 text-sm text-gray-500\">当前为阅读态，编辑器仅在进入编辑后挂载。</p>\n                ) : null}\n                <p className=\"text-sm text-wolai-text-secondary\">最近更新：{formattedUpdatedAt}</p>\n              </div>\n              {canEditDocument && (\n                <div className=\"flex shrink-0 items-center gap-2\">\n                  {isEditing ? (\n                    <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleExitEditMode}>\n                      返回阅读\n                    </Button>\n                  ) : (\n                    <Button type=\"button\" size=\"sm\" onClick={handleEnterEditMode}>\n                      进入编辑\n                    </Button>\n                  )}\n                </div>\n              )}\n            </div>\n          </div>\n          <div className=\"relative flex-1 overflow-y-auto px-12 py-6\">\n            {contentLoading ? (\n              showContentLoadingIndicator ? (\n                <div className=\"flex h-64 items-center justify-center text-sm text-gray-400\">\n                  页面内容加载中...\n                </div>\n              ) : (\n                <div className=\"h-64\" />\n              )\n            ) : contentError ? (\n              <div className=\"flex h-64 flex-col items-center justify-center gap-2 text-sm text-red-600\">\n                <div>{contentError}</div>\n                <button\n                  type=\"button\"\n                  className=\"rounded-md border border-red-200 bg-red-50 px-3 py-1 text-sm text-red-700 hover:bg-red-100\"\n                  onClick={() => {\n                    setContentError(null);\n                    setContentLoading(true);\n                    setContentReloadKey((prev) => prev + 1);\n                  }}\n                >\n                  重试\n                </button>\n              </div>\n            ) : (\n              <div className=\"relative\">\n                {keepEditorMounted && (\n                  <div className={cn(!isEditing && \"pointer-events-none absolute inset-0 opacity-0\")} aria-hidden={!isEditing}>\n                    <EditorHost\n                      documentId={documentId}\n                      workspaceId={workspaceId}\n                      initialContent={content}\n                      initialRevision={contentRevision}\n                      initialConflictDetectionKey={conflictDetectionKey}\n                      pageOptions={options}\n                      readOnly={readOnly}\n                      onStatsChange={handleStatsChange}\n                      onSnapshot={handleSnapshot}\n                      onCloseToc={closeToc}\n                      onPersistedMetaChange={(meta: { revision: number | null; conflictDetectionKey: string | null }) => {\n                        setContentRevision(meta.revision);\n                        setConflictDetectionKey(meta.conflictDetectionKey);\n                      }}\n                    />\n                  </div>\n                )}\n                {!isEditing && (\n                  <div className=\"relative\" ref={readViewRootRef}>\n                    <DocumentReadView\n                      content={content}\n                      documentId={documentId}\n                      options={options}\n                      pageSubtree={pageSubtree}\n                      className=\"mx-auto w-full max-w-[980px]\"\n                    />\n                    <DocumentToc\n                      entries={readViewTocEntries}\n                      visible={options.showToc}\n                      onJump={jumpToHeading}\n                      onClose={closeToc}\n                    />\n                  </div>\n                )}\n              </div>\n            )}\n            <PageBacklinksPanel\n              className=\"mt-10\"\n              workspaceId={workspaceId}\n              documentId={documentId}\n              defaultCollapsed={options.collapseBacklinks}\n            />\n          </div>\n        </div>\n        {showInspector && (\n          <PageOptionsSidebar\n            documentId={documentId}\n            options={options}\n            stats={stats}\n            onToggle={toggleOption}\n            onSetPageFont={handleSetPageFont}\n            onSetLayoutDensity={handleSetLayoutDensity}\n            onSetEmbedDefaultToCursor={inspectorCanUseEditorBridge ? handleSetEmbedDefaultToCursor : undefined}\n            onClearEmbedDefault={handleClearEmbedDefault}\n            onExport={handleExport}\n            onOpenHistory={() => setHistoryOpen(true)}\n            onOpenComments={() => openCommentsForPage({ workspaceId, documentId })}\n            onUndo={canEditDocument ? handleUndo : undefined}\n            onRedo={canEditDocument ? handleRedo : undefined}\n            onDeletePage={canEditDocument ? handleDeletePage : undefined}\n            onOpenMoveEmbedPicker={canEditDocument ? handleOpenMoveEmbed : undefined}\n            onCopyPageLink={handleCopyPageLink}\n            onCopyPageReference={handleCopyPageReference}\n            onAddToTemplates={canEditDocument ? handleAddToTemplates : undefined}\n          />\n        )}\n      </div>\n      <DocumentHistoryDrawer\n        open={historyOpen}\n        onOpenChange={setHistoryOpen}\n        history={history}\n        onRestore={handleRestoreSnapshot}\n      />\n      <MoveEmbedPickerHost />\n      <DocumentCommentsDrawer />\n      <DocumentAiAgentPanel\n        documentId={documentId}\n        getLatestBlocks={() => latestBlocksRef.current}\n        getLatestPageSubtree={() => pageSubtree}\n      />\n    </ImagePickerProvider>\n  );\n}\n",
  "structuredPatch": [
    {
      "oldStart": 1,
      "oldLines": 1011,
      "newStart": 1,
      "newLines": 1011,
      "lines": [
        "-\"use client\";",
        "-",
        "-import dynamic from \"next/dynamic\";",
        "-import { useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent, type KeyboardEvent as ReactKeyboardEvent } from \"react\";",
        "-import type { BooleanPageOptionKey, DocumentStats, PageOptionsState, PageFont, PageLayoutDensity } from \"@/types/page-options\";",
        "-import { useEditorBridgeStore } from \"@/store/editor-bridge\";",
        "-import { usePageLayoutStore } from \"@/store/page-layout\";",
        "-import { useCurrentDocumentStore } from \"@/store/current-document\";",
        "-import type { Json } from \"@/types/supabase\";",
        "-import type { DocumentSnapshot } from \"@/types/document\";",
        "-import { useDebouncedCallback } from \"@/hooks/use-debounced-callback\";",
        "-import { ImagePickerProvider } from \"@/components/media/image-picker-context\";",
        "-import { useRouter } from \"next/navigation\";",
        "-import { DocumentAiAgentPanel } from \"./DocumentAiAgentPanel\";",
        "-import { CONTENT_LOADING_DELAY_MS } from \"@/lib/constants\";",
        "-import { useCommentsUiStore } from \"@/store/comments-ui\";",
        "-import { useAppPreferencesStore } from \"@/store/app-preferences\";",
        "-import { useMoveEmbedPickerStore } from \"@/store/move-embed-picker\";",
        "-import { cn } from \"@/lib/utils\";",
        "-import { Button } from \"@/components/ui/button\";",
        "-import { DocumentToc } from \"@/components/editor/document-toc\";",
        "-import { DocumentReadView } from \"@/components/editor/document-read-view\";",
        "-import { extractPageBlocks, type PageSubtreeProjection } from \"@/lib/documents/page-subtree\";",
        "-import {",
        "-  deleteDocumentCommand,",
        "-  embedDocumentCommand,",
        "-  moveDocumentCommand,",
        "-  renameDocumentCommand,",
        "-} from \"@/lib/documents/tree-command-client\";",
        "-import { EditorHost } from \"@/components/editor/editor-host\";",
        "-",
        "-const PageOptionsSidebar = dynamic(",
        "-  () => import(\"@/components/editor/page-options-sidebar\").then((mod) => mod.PageOptionsSidebar),",
        "-  { ssr: false },",
        "-);",
        "-",
        "-const PageBacklinksPanel = dynamic(",
        "-  () => import(\"@/components/editor/page-backlinks-panel\").then((mod) => mod.PageBacklinksPanel),",
        "-  {",
        "-    ssr: false,",
        "-    loading: () => null,",
        "-  },",
        "-);",
        "-",
        "-const DocumentHistoryDrawer = dynamic(",
        "-  () => import(\"@/components/editor/document-history-drawer\").then((mod) => mod.DocumentHistoryDrawer),",
        "-  {",
        "-    ssr: false,",
        "-    loading: () => null,",
        "-  },",
        "-);",
        "-",
        "-const MoveEmbedPickerHost = dynamic(",
        "-  () => import(\"@/components/documents/move-embed-picker-host\").then((mod) => mod.MoveEmbedPickerHost),",
        "-  {",
        "-    ssr: false,",
        "-    loading: () => null,",
        "-  },",
        "-);",
        "-",
        "-const DocumentCommentsDrawer = dynamic(",
        "-  () => import(\"@/components/editor/document-comments-drawer\").then((mod) => mod.DocumentCommentsDrawer),",
        "-  {",
        "-    ssr: false,",
        "-    loading: () => null,",
        "-  },",
        "-);",
        "-",
        "-export interface DocumentContentProps {",
        "-  documentId: string;",
        "-  workspaceId: string;",
        "-  title: string | null;",
        "-  updatedAt: string | null;",
        "-  initialContent: unknown;",
        "-  initialContentRevision?: number | null;",
        "-  initialConflictDetectionKey?: string | null;",
        "-  initialPageSubtree?: PageSubtreeProjection | null;",
        "-  initialOptions: PageOptionsState;",
        "-  initialStats: DocumentStats | null;",
        "-  openTableId?: string | null;",
        "-  autoEnterEdit?: boolean;",
        "-  readOnly?: boolean;",
        "-  disableDownload?: boolean;",
        "-  disableCopy?: boolean;",
        "-}",
        "-",
        "-const defaultOptions: PageOptionsState = {",
        "-  wideLayout: false,",
        "-  smallText: false,",
        "-  showHeadingNumbers: true,",
        "-  showToc: false,",
        "-  showStructure: false,",
        "-  protectEditing: false,",
        "-  showWordCount: true,",
        "-  collapseBacklinks: false,",
        "-  pageFont: \"default\",",
        "-  layoutDensity: \"normal\",",
        "-  hideChildPages: false,",
        "-  showBlockRefCount: false,",
        "-  embedDefaultBlockId: null,",
        "-};",
        "-const defaultStats: DocumentStats = { wordCount: 0, characterCount: 0, blockCount: 0, todoTotal: 0, todoDone: 0 };",
        "-const EDITOR_UNMOUNT_GRACE_MS = 1000;",
        "-",
        "-export function DocumentContent({",
        "-  documentId,",
        "-  workspaceId,",
        "-  title,",
        "-  updatedAt,",
        "-  initialContent,",
        "-  initialContentRevision = null,",
        "-  initialConflictDetectionKey = null,",
        "-  initialPageSubtree = null,",
        "-  initialOptions,",
        "-  initialStats,",
        "-  openTableId,",
        "-  autoEnterEdit = false,",
        "-  readOnly = false,",
        "-  disableDownload = false,",
        "-  disableCopy = false,",
        "-}: DocumentContentProps) {",
        "-  const setCurrentDocument = useCurrentDocumentStore((state) => state.setCurrent);",
        "-  const clearIfMatch = useCurrentDocumentStore((state) => state.clearIfMatch);",
        "-  const canEditDocument = !readOnly;",
        "-  const [options, setOptions] = useState<PageOptionsState>(initialOptions ?? defaultOptions);",
        "-  const [stats, setStats] = useState<DocumentStats>(initialStats ?? defaultStats);",
        "-  const [history, setHistory] = useState<DocumentSnapshot[]>([]);",
        "-  const [historyOpen, setHistoryOpen] = useState(false);",
        "-  const editorBridge = useEditorBridgeStore((state) => state.bridge);",
        "-  const router = useRouter();",
        "-  const showInspector = usePageLayoutStore((state) => state.showInspector);",
        "-  const openCommentsForPage = useCommentsUiStore((s) => s.openForPage);",
        "-  const spellCheck = useAppPreferencesStore((s) => s.spellCheck);",
        "-  const openMoveEmbedPicker = useMoveEmbedPickerStore((s) => s.openPicker);",
        "-  const [pageTitle, setPageTitle] = useState(title ?? \"无标题\");",
        "-  const [content, setContent] = useState<unknown>(initialContent);",
        "-  const [serverContentSnapshot, setServerContentSnapshot] = useState<unknown>(initialContent);",
        "-  const [serverPageSubtreeSnapshot, setServerPageSubtreeSnapshot] = useState<PageSubtreeProjection | null>(",
        "-    initialPageSubtree,",
        "-  );",
        "-  const [serverPageSubtreeTitle, setServerPageSubtreeTitle] = useState<string>(title ?? \"无标题\");",
        "-  const [contentRevision, setContentRevision] = useState<number | null>(initialContentRevision);",
        "-  const [conflictDetectionKey, setConflictDetectionKey] = useState<string | null>(initialConflictDetectionKey);",
        "-  const [contentLoading, setContentLoading] = useState(() => initialContent == null);",
        "-  const [contentError, setContentError] = useState<string | null>(null);",
        "-  const [contentReloadKey, setContentReloadKey] = useState(0);",
        "-  const [showContentLoadingIndicator, setShowContentLoadingIndicator] = useState(false);",
        "-  const shouldStartEditing = (Boolean((openTableId ?? \"\").trim()) || autoEnterEdit) && !readOnly;",
        "-  const [isEditing, setIsEditing] = useState(() => shouldStartEditing);",
        "-  const [keepEditorMounted, setKeepEditorMounted] = useState(() => shouldStartEditing);",
        "-  const contentLoadingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);",
        "-  const editorUnmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);",
        "-  const pendingOpenTableRef = useRef<string | null>(null);",
        "-  const latestBlocksRef = useRef<Json | null>(null);",
        "-  const pageRootRef = useRef<HTMLDivElement>(null);",
        "-  const readViewRootRef = useRef<HTMLDivElement>(null);",
        "-  const pendingRestoreSnapshotRef = useRef<DocumentSnapshot | null>(null);",
        "-  const lastCopyBlockedAtRef = useRef<number>(0);",
        "-",
        "-  useEffect(() => {",
        "-    setCurrentDocument(documentId, readOnly, disableDownload, disableCopy);",
        "-    return () => {",
        "-      clearIfMatch(documentId);",
        "-    };",
        "-  }, [clearIfMatch, disableCopy, disableDownload, documentId, readOnly, setCurrentDocument]);",
        "-",
        "-  useEffect(() => {",
        "-    if (!disableCopy) return;",
        "-",
        "-    const toElement = (node: Node | null): Element | null => {",
        "-      if (!node) return null;",
        "-      if (node.nodeType === Node.TEXT_NODE) {",
        "-        return node.parentElement;",
        "-      }",
        "-      return node instanceof Element ? node : null;",
        "-    };",
        "-",
        "-    const isEventInsidePage = () => {",
        "-      const root = pageRootRef.current;",
        "-      if (!root) return false;",
        "-      const selection = typeof window !== \"undefined\" ? window.getSelection() : null;",
        "-      const anchor = selection?.anchorNode ?? null;",
        "-      const focus = selection?.focusNode ?? null;",
        "-      const anchorEl = toElement(anchor);",
        "-      const focusEl = toElement(focus);",
        "-      return Boolean((anchorEl && root.contains(anchorEl)) || (focusEl && root.contains(focusEl)));",
        "-    };",
        "-",
        "-    const notifyBlocked = () => {",
        "-      const now = Date.now();",
        "-      if (now - lastCopyBlockedAtRef.current < 1200) return;",
        "-      lastCopyBlockedAtRef.current = now;",
        "-      window.alert(\"该页面已禁止复制\");",
        "-    };",
        "-",
        "-    const onCopy = (event: ClipboardEvent) => {",
        "-      if (!isEventInsidePage()) return;",
        "-      event.preventDefault();",
        "-      event.stopPropagation();",
        "-      notifyBlocked();",
        "-    };",
        "-",
        "-    const onCut = (event: ClipboardEvent) => {",
        "-      if (!isEventInsidePage()) return;",
        "-      event.preventDefault();",
        "-      event.stopPropagation();",
        "-      notifyBlocked();",
        "-    };",
        "-",
        "-    const onKeyDown = (event: KeyboardEvent) => {",
        "-      if (!isEventInsidePage()) return;",
        "-      const key = String(event.key ?? \"\").toLowerCase();",
        "-      const ctrlOrMeta = event.ctrlKey || event.metaKey;",
        "-      if (!ctrlOrMeta) return;",
        "-      if (key === \"c\" || key === \"x\" || key === \"insert\") {",
        "-        event.preventDefault();",
        "-        event.stopPropagation();",
        "-        notifyBlocked();",
        "-      }",
        "-    };",
        "-",
        "-    document.addEventListener(\"copy\", onCopy, true);",
        "-    document.addEventListener(\"cut\", onCut, true);",
        "-    document.addEventListener(\"keydown\", onKeyDown, true);",
        "-    return () => {",
        "-      document.removeEventListener(\"copy\", onCopy, true);",
        "-      document.removeEventListener(\"cut\", onCut, true);",
        "-      document.removeEventListener(\"keydown\", onKeyDown, true);",
        "-    };",
        "-  }, [disableCopy]);",
        "-",
        "-  useEffect(() => {",
        "-    const tableId = (openTableId ?? \"\").trim();",
        "-    if (!tableId) return;",
        "-    if (!editorBridge?.openTableFullScreen) return;",
        "-    if (pendingOpenTableRef.current === tableId) return;",
        "-    pendingOpenTableRef.current = tableId;",
        "-",
        "-    editorBridge.openTableFullScreen(tableId);",
        "-",
        "-    if (typeof window !== \"undefined\") {",
        "-      try {",
        "-        const url = new URL(window.location.href);",
        "-        url.searchParams.delete(\"openTableId\");",
        "-        window.history.replaceState({}, \"\", url.toString());",
        "-      } catch {",
        "-        router.replace(`/documents/${documentId}`);",
        "-      }",
        "-    }",
        "-  }, [documentId, editorBridge, openTableId, router]);",
        "-",
        "-  useEffect(() => {",
        "-    setPageTitle(title ?? \"无标题\");",
        "-  }, [title]);",
        "-",
        "-  useEffect(() => {",
        "-    setServerPageSubtreeTitle(title ?? \"无标题\");",
        "-  }, [title]);",
        "-",
        "-  useEffect(() => {",
        "-    setServerPageSubtreeSnapshot(initialPageSubtree);",
        "-  }, [initialPageSubtree]);",
        "-",
        "-  useEffect(() => {",
        "-    setOptions(initialOptions ?? defaultOptions);",
        "-  }, [initialOptions]);",
        "-",
        "-  useEffect(() => {",
        "-    setStats(initialStats ?? defaultStats);",
        "-  }, [initialStats]);",
        "-",
        "-  useEffect(() => {",
        "-    setContentRevision(initialContentRevision);",
        "-  }, [initialContentRevision]);",
        "-",
        "-  useEffect(() => {",
        "-    setConflictDetectionKey(initialConflictDetectionKey);",
        "-  }, [initialConflictDetectionKey]);",
        "-",
        "-  useEffect(() => {",
        "-    setServerContentSnapshot(initialContent);",
        "-  }, [initialContent]);",
        "-",
        "-  useEffect(() => {",
        "-    const nextBlocks = extractPageBlocks(content);",
        "-    latestBlocksRef.current = nextBlocks.length > 0 ? (nextBlocks as Json) : null;",
        "-  }, [content]);",
        "-",
        "-  useEffect(() => {",
        "-    const shouldForceEdit = Boolean((openTableId ?? \"\").trim()) && canEditDocument;",
        "-    if (shouldForceEdit) {",
        "-      setIsEditing(true);",
        "-      setKeepEditorMounted(true);",
        "-    }",
        "-  }, [canEditDocument, openTableId]);",
        "-",
        "-  useEffect(() => {",
        "-    if (isEditing) {",
        "-      if (editorUnmountTimerRef.current) {",
        "-        clearTimeout(editorUnmountTimerRef.current);",
        "-        editorUnmountTimerRef.current = null;",
        "-      }",
        "-      setKeepEditorMounted(true);",
        "-      return;",
        "-    }",
        "-    if (editorUnmountTimerRef.current) {",
        "-      clearTimeout(editorUnmountTimerRef.current);",
        "-    }",
        "-    editorUnmountTimerRef.current = setTimeout(() => {",
        "-      setKeepEditorMounted(false);",
        "-      editorUnmountTimerRef.current = null;",
        "-    }, EDITOR_UNMOUNT_GRACE_MS);",
        "-    return () => {",
        "-      if (editorUnmountTimerRef.current) {",
        "-        clearTimeout(editorUnmountTimerRef.current);",
        "-        editorUnmountTimerRef.current = null;",
        "-      }",
        "-    };",
        "-  }, [isEditing]);",
        "-",
        "-  useEffect(() => {",
        "-    let canceled = false;",
        "-    const controller = new AbortController();",
        "-",
        "-    const load = async () => {",
        "-      setContentError(null);",
        "-      setContentLoading(initialContent == null);",
        "-      setContent(initialContent);",
        "-      setShowContentLoadingIndicator(false);",
        "-",
        "-      if (initialContent != null) {",
        "-        return;",
        "-      }",
        "-",
        "-      if (contentLoadingTimerRef.current) {",
        "-        clearTimeout(contentLoadingTimerRef.current);",
        "-        contentLoadingTimerRef.current = null;",
        "-      }",
        "-      contentLoadingTimerRef.current = setTimeout(() => {",
        "-        if (!canceled) {",
        "-          setShowContentLoadingIndicator(true);",
        "-        }",
        "-      }, CONTENT_LOADING_DELAY_MS);",
        "-",
        "-      try {",
        "-        const response = await fetch(",
        "-          `/api/documents/content?documentId=${encodeURIComponent(documentId)}&workspaceId=${encodeURIComponent(workspaceId)}`,",
        "-          {",
        "-            method: \"GET\",",
        "-            credentials: \"include\",",
        "-            signal: controller.signal,",
        "-          },",
        "-        );",
        "-        if (!response.ok) {",
        "-          const payload = await response.json().catch(() => ({}));",
        "-          throw new Error(payload?.error ?? \"加载页面内容失败\");",
        "-        }",
        "-        const payload = (await response.json()) as {",
        "-          content?: unknown;",
        "-          revision?: number | null;",
        "-          conflictDetectionKey?: string | null;",
        "-          pageSubtree?: PageSubtreeProjection | null;",
        "-        };",
        "-        if (canceled) return;",
        "-        setContent(payload.content ?? null);",
        "-        setServerContentSnapshot(payload.content ?? null);",
        "-        setContentRevision(",
        "-          typeof payload.revision === \"number\" && Number.isInteger(payload.revision)",
        "-            ? payload.revision",
        "-            : 0,",
        "-        );",
        "-        setConflictDetectionKey(",
        "-          typeof payload.conflictDetectionKey === \"string\" && payload.conflictDetectionKey.trim()",
        "-            ? payload.conflictDetectionKey",
        "-            : `${documentId}:0`,",
        "-        );",
        "-        setServerPageSubtreeSnapshot(payload.pageSubtree ?? null);",
        "-        setServerPageSubtreeTitle(",
        "-          typeof payload.pageSubtree?.rootNode.metadata.title === \"string\" &&",
        "-            payload.pageSubtree.rootNode.metadata.title.trim()",
        "-            ? payload.pageSubtree.rootNode.metadata.title",
        "-            : title ?? \"无标题\",",
        "-        );",
        "-      } catch (error) {",
        "-        if (canceled) return;",
        "-        if ((error as { name?: string })?.name === \"AbortError\") return;",
        "-        setContentError(error instanceof Error ? error.message : \"加载页面内容失败\");",
        "-      } finally {",
        "-        if (!canceled) {",
        "-          setContentLoading(false);",
        "-          setShowContentLoadingIndicator(false);",
        "-        }",
        "-        if (contentLoadingTimerRef.current) {",
        "-          clearTimeout(contentLoadingTimerRef.current);",
        "-          contentLoadingTimerRef.current = null;",
        "-        }",
        "-      }",
        "-    };",
        "-",
        "-    void load();",
        "-",
        "-    return () => {",
        "-      canceled = true;",
        "-      controller.abort();",
        "-      if (contentLoadingTimerRef.current) {",
        "-        clearTimeout(contentLoadingTimerRef.current);",
        "-        contentLoadingTimerRef.current = null;",
        "-      }",
        "-    };",
        "-  }, [documentId, initialContent, contentReloadKey, title, workspaceId]);",
        "-",
        "-  const persistTitle = useCallback(",
        "-    async (nextTitle: string) => {",
        "-      if (readOnly) return;",
        "-      const payload = nextTitle.trim() || \"无标题\";",
        "-      try {",
        "-        await renameDocumentCommand({ documentId, workspaceId, title: payload });",
        "-      } catch (error) {",
        "-        console.error(\"更新页面标题失败\", error);",
        "-      }",
        "-    },",
        "-    [documentId, readOnly, workspaceId],",
        "-  );",
        "-",
        "-  const debouncedPersistTitle = useDebouncedCallback((value: string) => {",
        "-    void persistTitle(value);",
        "-  }, 600);",
        "-",
        "-  const handleTitleChange = (event: ChangeEvent<HTMLInputElement>) => {",
        "-    if (!canEditDocument) return;",
        "-    const value = event.target.value;",
        "-    setPageTitle(value);",
        "-    debouncedPersistTitle(value);",
        "-  };",
        "-",
        "-  const handleTitleBlur = () => {",
        "-    if (!canEditDocument) return;",
        "-    void persistTitle(pageTitle);",
        "-  };",
        "-",
        "-  const handleTitleKeyDown = (event: ReactKeyboardEvent<HTMLInputElement>) => {",
        "-    if (event.key === \"Enter\") {",
        "-      event.preventDefault();",
        "-      event.currentTarget.blur();",
        "-    }",
        "-  };",
        "-",
        "-  const persistOptions = useCallback(",
        "-    async (patch: Partial<PageOptionsState>) => {",
        "-      if (readOnly) return;",
        "-      try {",
        "-        const response = await fetch(\"/api/documents/options\", {",
        "-          method: \"POST\",",
        "-          headers: { \"Content-Type\": \"application/json\" },",
        "-          body: JSON.stringify({ documentId, workspaceId, options: patch }),",
        "-        });",
        "-        if (!response.ok) {",
        "-          const payload = await response.json().catch(() => null);",
        "-          console.error(payload?.error ?? \"更新页面选项失败\");",
        "-        }",
        "-      } catch (error) {",
        "-        console.error(error);",
        "-      }",
        "-    },",
        "-    [documentId, readOnly, workspaceId],",
        "-  );",
        "-",
        "-  const toggleOption = useCallback(",
        "-    (key: BooleanPageOptionKey) => {",
        "-      if (readOnly) return;",
        "-      setOptions((prev) => {",
        "-        const nextValue = !prev[key];",
        "-        const next = { ...prev, [key]: nextValue };",
        "-        void persistOptions({ [key]: nextValue } as Partial<PageOptionsState>);",
        "-        return next;",
        "-      });",
        "-    },",
        "-    [persistOptions, readOnly],",
        "-  );",
        "-",
        "-  const setOptionPatch = useCallback(",
        "-    (patch: Partial<PageOptionsState>) => {",
        "-      if (readOnly) return;",
        "-      setOptions((prev) => {",
        "-        const next = { ...prev, ...patch };",
        "-        void persistOptions(patch);",
        "-        return next;",
        "-      });",
        "-    },",
        "-    [persistOptions, readOnly],",
        "-  );",
        "-",
        "-  const closeToc = useCallback(() => {",
        "-    if (readOnly) return;",
        "-    setOptions((prev) => {",
        "-      if (!prev.showToc) return prev;",
        "-      const next = { ...prev, showToc: false };",
        "-      void persistOptions({ showToc: false });",
        "-      return next;",
        "-    });",
        "-  }, [persistOptions, readOnly]);",
        "-",
        "-  const handleSetPageFont = useCallback(",
        "-    (font: PageFont) => {",
        "-      setOptionPatch({ pageFont: font });",
        "-    },",
        "-    [setOptionPatch],",
        "-  );",
        "-",
        "-  const handleSetLayoutDensity = useCallback(",
        "-    (density: PageLayoutDensity) => {",
        "-      setOptionPatch({ layoutDensity: density });",
        "-    },",
        "-    [setOptionPatch],",
        "-  );",
        "-",
        "-  const handleSetEmbedDefaultToCursor = useCallback(() => {",
        "-    if (!canEditDocument) return;",
        "-    if (!isEditing) {",
        "-      setIsEditing(true);",
        "-      return;",
        "-    }",
        "-    const blockId = editorBridge?.getCursorBlockId?.() ?? null;",
        "-    if (!blockId) {",
        "-      window.alert(\"未找到当前光标所在块，请先在编辑器中点击任意块\");",
        "-      return;",
        "-    }",
        "-    setOptionPatch({ embedDefaultBlockId: blockId });",
        "-    window.alert(\"已设置“嵌入默认位置”\");",
        "-  }, [canEditDocument, editorBridge, isEditing, setOptionPatch]);",
        "-",
        "-  const handleClearEmbedDefault = useCallback(() => {",
        "-    if (readOnly) return;",
        "-    setOptionPatch({ embedDefaultBlockId: null });",
        "-    window.alert(\"已清除“嵌入默认位置”\");",
        "-  }, [readOnly, setOptionPatch]);",
        "-",
        "-  useEffect(() => {",
        "-    const onKeyDown = (event: KeyboardEvent) => {",
        "-      const root = pageRootRef.current;",
        "-      if (root) {",
        "-        const target = event.target;",
        "-        if (target && target instanceof Node && !root.contains(target)) {",
        "-          return;",
        "-        }",
        "-      }",
        "-      const ctrlOrMeta = event.ctrlKey || event.metaKey;",
        "-      if (!ctrlOrMeta) return;",
        "-      if (!event.shiftKey) return;",
        "-      const key = String(event.key ?? \"\").toLowerCase();",
        "-      if (key === \"l\") {",
        "-        event.preventDefault();",
        "-        toggleOption(\"showToc\");",
        "-      } else if (key === \"c\") {",
        "-        event.preventDefault();",
        "-        toggleOption(\"protectEditing\");",
        "-      }",
        "-    };",
        "-    window.addEventListener(\"keydown\", onKeyDown);",
        "-    return () => window.removeEventListener(\"keydown\", onKeyDown);",
        "-  }, [toggleOption]);",
        "-",
        "-  const buildDocumentUrl = useCallback((id: string): string => {",
        "-    if (typeof window === \"undefined\" || !window.location) {",
        "-      return `/documents/${id}`;",
        "-    }",
        "-    return `${window.location.origin}/documents/${id}`;",
        "-  }, []);",
        "-",
        "-  const copyText = useCallback(async (text: string, successMessage: string) => {",
        "-    if (typeof navigator !== \"undefined\" && navigator.clipboard) {",
        "-      try {",
        "-        await navigator.clipboard.writeText(text);",
        "-        window.alert(successMessage);",
        "-        return;",
        "-      } catch {",
        "-      }",
        "-    }",
        "-    window.prompt(\"复制失败，请手动复制内容\", text);",
        "-  }, []);",
        "-",
        "-  const handleCopyPageLink = useCallback(",
        "-    async (includeTitle: boolean) => {",
        "-      const url = buildDocumentUrl(documentId);",
        "-      if (includeTitle) {",
        "-        const text = `${pageTitle || \"无标题\"}\\n${url}`;",
        "-        await copyText(text, \"标题 + 链接已复制\");",
        "-        return;",
        "-      }",
        "-      await copyText(url, \"页面链接已复制\");",
        "-    },",
        "-    [buildDocumentUrl, copyText, documentId, pageTitle],",
        "-  );",
        "-",
        "-  const handleCopyPageReference = useCallback(",
        "-    async (mode: \"inline\" | \"embed\") => {",
        "-      const template = mode === \"inline\" ? `((${documentId}))` : `{{${documentId}}}`;",
        "-      await copyText(template, mode === \"inline\" ? \"行内引用已复制\" : \"嵌入引用已复制\");",
        "-    },",
        "-    [copyText, documentId],",
        "-  );",
        "-",
        "-  const handleUndo = useCallback(() => {",
        "-    if (!isEditing) {",
        "-      setIsEditing(true);",
        "-      return;",
        "-    }",
        "-    editorBridge?.undo?.();",
        "-  }, [editorBridge, isEditing]);",
        "-",
        "-  const handleRedo = useCallback(() => {",
        "-    if (!isEditing) {",
        "-      setIsEditing(true);",
        "-      return;",
        "-    }",
        "-    editorBridge?.redo?.();",
        "-  }, [editorBridge, isEditing]);",
        "-",
        "-  const handleDeletePage = useCallback(async () => {",
        "-    if (readOnly) return;",
        "-    const ok = window.confirm(\"确定删除该页面吗？删除后会进入垃圾桶。\");",
        "-    if (!ok) return;",
        "-    try {",
        "-      await deleteDocumentCommand({ documentId, workspaceId });",
        "-    } catch (error) {",
        "-      const payload = error instanceof Error ? error.message : null;",
        "-      window.alert(payload ?? \"删除失败\");",
        "-      return;",
        "-    }",
        "-    router.push(\"/\");",
        "-    router.refresh();",
        "-  }, [documentId, readOnly, router, workspaceId]);",
        "-",
        "-  const handleOpenMoveEmbed = useCallback(() => {",
        "-    if (readOnly) return;",
        "-    openMoveEmbedPicker({",
        "-      workspaceId,",
        "-      defaultMode: \"move\",",
        "-      modes: [\"move\", \"embed\"],",
        "-      allowRoot: true,",
        "-      excludeIds: [documentId],",
        "-      onPick: async (mode, targetId) => {",
        "-        if (mode === \"move\") {",
        "-          try {",
        "-            await moveDocumentCommand({",
        "-              documentId,",
        "-              parentId: targetId ?? null,",
        "-              position: 999999,",
        "-              workspaceId,",
        "-            });",
        "-          } catch (error) {",
        "-            window.alert(error instanceof Error ? error.message : \"移动失败\");",
        "-            return;",
        "-          }",
        "-          window.alert(\"移动成功\");",
        "-          router.refresh();",
        "-          return;",
        "-        }",
        "-",
        "-        if (!targetId) {",
        "-          window.alert(\"请选择目标页面\");",
        "-          return;",
        "-        }",
        "-",
        "-        try {",
        "-          await embedDocumentCommand({",
        "-            sourceId: documentId,",
        "-            targetId,",
        "-          });",
        "-        } catch (error) {",
        "-          const message = error instanceof Error ? error.message : \"嵌入失败\";",
        "-          window.alert(message);",
        "-          return;",
        "-        }",
        "-",
        "-        window.alert(\"已嵌入到目标页面\");",
        "-      },",
        "-    });",
        "-  }, [documentId, openMoveEmbedPicker, readOnly, router, workspaceId]);",
        "-",
        "-  const handleAddToTemplates = useCallback(async () => {",
        "-    if (readOnly) return;",
        "-    const ok = window.confirm(\"将该页面添加为模板？\");",
        "-    if (!ok) return;",
        "-    const resp = await fetch(\"/api/documents/template\", {",
        "-      method: \"POST\",",
        "-      headers: { \"Content-Type\": \"application/json\" },",
        "-      body: JSON.stringify({ documentId, isTemplate: true }),",
        "-    });",
        "-    if (!resp.ok) {",
        "-      const payload = await resp.json().catch(() => ({}));",
        "-      window.alert(payload?.error ?? \"设置模板失败\");",
        "-      return;",
        "-    }",
        "-    window.alert(\"已添加为模板\");",
        "-    router.refresh();",
        "-  }, [documentId, readOnly, router]);",
        "-",
        "-  const formattedUpdatedAt = useMemo(() => {",
        "-    if (!updatedAt) return \"\";",
        "-    const date = new Date(updatedAt);",
        "-    if (Number.isNaN(date.getTime())) return \"\";",
        "-    return new Intl.DateTimeFormat(\"zh-CN\", {",
        "-      timeZone: \"Asia/Shanghai\",",
        "-      year: \"numeric\",",
        "-      month: \"numeric\",",
        "-      day: \"numeric\",",
        "-      hour: \"2-digit\",",
        "-      minute: \"2-digit\",",
        "-      second: \"2-digit\",",
        "-      hour12: false,",
        "-    }).format(date);",
        "-  }, [updatedAt]);",
        "-",
        "-  const handleExport = useCallback(() => {",
        "-    if (disableDownload) {",
        "-      window.alert(\"该页面已禁止下载\");",
        "-      return;",
        "-    }",
        "-    const latest = history[0];",
        "-    const exportBlocks = latest?.blocks ?? latestBlocksRef.current;",
        "-    if (!exportBlocks) {",
        "-      window.alert(\"暂无可导出的内容\");",
        "-      return;",
        "-    }",
        "-    const payload = JSON.stringify(exportBlocks, null, 2);",
        "-    const blob = new Blob([payload], { type: \"application/json\" });",
        "-    const url = URL.createObjectURL(blob);",
        "-    const anchor = document.createElement(\"a\");",
        "-    anchor.href = url;",
        "-    anchor.download = `${title ?? \"未命名页面\"}-${new Date().toISOString()}.json`;",
        "-    anchor.click();",
        "-    URL.revokeObjectURL(url);",
        "-  }, [disableDownload, history, title]);",
        "-",
        "-  const pageRootClass = cn(",
        "-    \"flex h-full overflow-hidden bg-wolai-bg\",",
        "-    options.pageFont === \"song\" && \"wolai-page-font-song\",",
        "-    options.pageFont === \"kai\" && \"wolai-page-font-kai\",",
        "-    options.layoutDensity === \"compact\" && \"wolai-page-density-compact\",",
        "-    options.layoutDensity === \"spacious\" && \"wolai-page-density-spacious\",",
        "-    options.smallText && \"wolai-small-text\",",
        "-    options.hideChildPages && \"wolai-hide-child-pages\",",
        "-  );",
        "-  const pageSubtree = useMemo(() => {",
        "-    const hasServerPageSubtree = Boolean(serverPageSubtreeSnapshot);",
        "-    const titleUnchanged = pageTitle === serverPageSubtreeTitle;",
        "-    const contentUnchanged = content === serverContentSnapshot;",
        "-",
        "-    if (hasServerPageSubtree && titleUnchanged && contentUnchanged) {",
        "-      return serverPageSubtreeSnapshot;",
        "-    }",
        "-    return null;",
        "-  }, [",
        "-    content,",
        "-    pageTitle,",
        "-    serverContentSnapshot,",
        "-    serverPageSubtreeSnapshot,",
        "-    serverPageSubtreeTitle,",
        "-  ]);",
        "-  const readViewTocEntries = useMemo(",
        "-    () =>",
        "-      (pageSubtree?.outline ?? [])",
        "-        .filter((entry) => typeof entry.anchorBlockId === \"string\" && entry.anchorBlockId.trim())",
        "-        .map(({ anchorBlockId, level, numbering, title: entryTitle }) => ({",
        "-          id: anchorBlockId as string,",
        "-          level,",
        "-          numbering,",
        "-          title: entryTitle,",
        "-        })),",
        "-    [pageSubtree],",
        "-  );",
        "-  const inspectorCanUseEditorBridge = canEditDocument && isEditing;",
        "-",
        "-  const jumpToHeading = useCallback((headingId: string) => {",
        "-    const targetRoot = !isEditing ? readViewRootRef.current : pageRootRef.current;",
        "-    const target = targetRoot?.querySelector<HTMLElement>(`[data-id=\"${headingId}\"]`);",
        "-    if (target) {",
        "-      target.scrollIntoView({ behavior: \"smooth\", block: \"center\" });",
        "-    }",
        "-  }, [isEditing]);",
        "-",
        "-  const handleSnapshot = useCallback((payload: { blocks: Json; stats: DocumentStats }) => {",
        "-    setContent(payload.blocks);",
        "-    latestBlocksRef.current = payload.blocks;",
        "-    setHistory((prev) => {",
        "-      const now = Date.now();",
        "-      if (prev.length > 0 && now - prev[0].timestamp < 4000) {",
        "-        return prev;",
        "-      }",
        "-      const snapshot: DocumentSnapshot = {",
        "-        id: `${now}-${Math.random().toString(36).slice(2, 8)}`,",
        "-        timestamp: now,",
        "-        blocks: payload.blocks,",
        "-        stats: payload.stats,",
        "-      };",
        "-      return [snapshot, ...prev].slice(0, 15);",
        "-    });",
        "-  }, []);",
        "-",
        "-  const persistStatsRequest = useCallback((next: DocumentStats) => {",
        "-    void fetch(\"/api/documents/stats\", {",
        "-      method: \"POST\",",
        "-      headers: { \"Content-Type\": \"application/json\" },",
        "-      body: JSON.stringify({ documentId, workspaceId, stats: next }),",
        "-    }).catch((error) => console.error(error));",
        "-  }, [documentId, workspaceId]);",
        "-",
        "-  const persistStats = useDebouncedCallback(persistStatsRequest, 1500);",
        "-",
        "-  const handleStatsChange = useCallback(",
        "-    (nextStats: DocumentStats) => {",
        "-      setStats(nextStats);",
        "-      persistStats(nextStats);",
        "-    },",
        "-    [persistStats],",
        "-  );",
        "-",
        "-  const handleRestoreSnapshot = useCallback(",
        "-    (snapshot: DocumentSnapshot) => {",
        "-      if (!isEditing || !editorBridge) {",
        "-        pendingRestoreSnapshotRef.current = snapshot;",
        "-        setIsEditing(true);",
        "-        return;",
        "-      }",
        "-      editorBridge.replaceWithSnapshot(snapshot.blocks);",
        "-      setHistoryOpen(false);",
        "-    },",
        "-    [editorBridge, isEditing],",
        "-  );",
        "-",
        "-  const handleEnterEditMode = useCallback(() => {",
        "-    if (!canEditDocument) return;",
        "-    setIsEditing(true);",
        "-  }, [canEditDocument]);",
        "-",
        "-  const handleExitEditMode = useCallback(() => {",
        "-    setIsEditing(false);",
        "-  }, []);",
        "-",
        "-  useEffect(() => {",
        "-    if (!isEditing) return;",
        "-    if (!editorBridge) return;",
        "-    const pendingSnapshot = pendingRestoreSnapshotRef.current;",
        "-    if (!pendingSnapshot) return;",
        "-    pendingRestoreSnapshotRef.current = null;",
        "-    editorBridge.replaceWithSnapshot(pendingSnapshot.blocks);",
        "-    setHistoryOpen(false);",
        "-  }, [editorBridge, isEditing]);",
        "-  return (",
        "-    <ImagePickerProvider documentId={documentId} workspaceId={workspaceId}>",
        "-      <div className={pageRootClass} ref={pageRootRef}>",
        "-        <div className=\"flex h-full flex-1 flex-col overflow-hidden\">",
        "-          <div className=\"border-b border-wolai-border px-12 pb-6 pt-8\">",
        "-            <div className=\"flex items-start justify-between gap-6\">",
        "-              <div className=\"min-w-0 flex-1\">",
        "-                {isEditing && canEditDocument ? (",
        "-                  <div className=\"relative\">",
        "-                    <input",
        "-                      value={pageTitle}",
        "-                      onChange={handleTitleChange}",
        "-                      onBlur={handleTitleBlur}",
        "-                      onKeyDown={handleTitleKeyDown}",
        "-                      placeholder=\"无标题\"",
        "-                      className=\"w-full border-none bg-transparent text-3xl font-semibold text-wolai-text-primary outline-none focus:ring-0\"",
        "-                      aria-label=\"页面标题\"",
        "-                      disabled={options.protectEditing || readOnly}",
        "-                      spellCheck={spellCheck}",
        "-                    />",
        "-                  </div>",
        "-                ) : (",
        "-                  <h1 className=\"break-words text-3xl font-semibold text-wolai-text-primary\">",
        "-                    {pageTitle || \"无标题\"}",
        "-                  </h1>",
        "-                )}",
        "-                {readOnly ? (",
        "-                  <p className=\"mt-1 text-sm text-gray-500\">该页面为只读共享，无法编辑。</p>",
        "-                ) : options.protectEditing && isEditing ? (",
        "-                  <p className=\"mt-1 text-sm text-[#b91c1c]\">当前页面已开启编辑保护，关闭后方可修改内容。</p>",
        "-                ) : !isEditing && canEditDocument ? (",
        "-                  <p className=\"mt-1 text-sm text-gray-500\">当前为阅读态，编辑器仅在进入编辑后挂载。</p>",
        "-                ) : null}",
        "-                <p className=\"text-sm text-wolai-text-secondary\">最近更新：{formattedUpdatedAt}</p>",
        "-              </div>",
        "-              {canEditDocument && (",
        "-                <div className=\"flex shrink-0 items-center gap-2\">",
        "-                  {isEditing ? (",
        "-                    <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleExitEditMode}>",
        "-                      返回阅读",
        "-                    </Button>",
        "-                  ) : (",
        "-                    <Button type=\"button\" size=\"sm\" onClick={handleEnterEditMode}>",
        "-                      进入编辑",
        "-                    </Button>",
        "-                  )}",
        "-                </div>",
        "-              )}",
        "-            </div>",
        "-          </div>",
        "-          <div className=\"relative flex-1 overflow-y-auto px-12 py-6\">",
        "-            {contentLoading ? (",
        "-              showContentLoadingIndicator ? (",
        "-                <div className=\"flex h-64 items-center justify-center text-sm text-gray-400\">",
        "-                  页面内容加载中...",
        "-                </div>",
        "-              ) : (",
        "-                <div className=\"h-64\" />",
        "-              )",
        "-            ) : contentError ? (",
        "-              <div className=\"flex h-64 flex-col items-center justify-center gap-2 text-sm text-red-600\">",
        "-                <div>{contentError}</div>",
        "-                <button",
        "-                  type=\"button\"",
        "-                  className=\"rounded-md border border-red-200 bg-red-50 px-3 py-1 text-sm text-red-700 hover:bg-red-100\"",
        "-                  onClick={() => {",
        "-                    setContentError(null);",
        "-                    setContentLoading(true);",
        "-                    setContentReloadKey((prev) => prev + 1);",
        "-                  }}",
        "-                >",
        "-                  重试",
        "-                </button>",
        "-              </div>",
        "-            ) : (",
        "-              <div className=\"relative\">",
        "-                {keepEditorMounted && (",
        "-                  <div className={cn(!isEditing && \"pointer-events-none absolute inset-0 opacity-0\")} aria-hidden={!isEditing}>",
        "-                    <EditorHost",
        "-                      documentId={documentId}",
        "-                      workspaceId={workspaceId}",
        "-                      initialContent={content}",
        "-                      initialRevision={contentRevision}",
        "-                      initialConflictDetectionKey={conflictDetectionKey}",
        "-                      pageOptions={options}",
        "-                      readOnly={readOnly}",
        "-                      onStatsChange={handleStatsChange}",
        "-                      onSnapshot={handleSnapshot}",
        "-                      onCloseToc={closeToc}",
        "-                      onPersistedMetaChange={(meta: { revision: number | null; conflictDetectionKey: string | null }) => {",
        "-                        setContentRevision(meta.revision);",
        "-                        setConflictDetectionKey(meta.conflictDetectionKey);",
        "-                      }}",
        "-                    />",
        "-                  </div>",
        "-                )}",
        "-                {!isEditing && (",
        "-                  <div className=\"relative\" ref={readViewRootRef}>",
        "-                    <DocumentReadView",
        "-                      content={content}",
        "-                      documentId={documentId}",
        "-                      options={options}",
        "-                      pageSubtree={pageSubtree}",
        "-                      className=\"mx-auto w-full max-w-[980px]\"",
        "-                    />",
        "-                    <DocumentToc",
        "-                      entries={readViewTocEntries}",
        "-                      visible={options.showToc}",
        "-                      onJump={jumpToHeading}",
        "-                      onClose={closeToc}",
        "-                    />",
        "-                  </div>",
        "-                )}",
        "-              </div>",
        "-            )}",
        "-            <PageBacklinksPanel",
        "-              className=\"mt-10\"",
        "-              workspaceId={workspaceId}",
        "-              documentId={documentId}",
        "-              defaultCollapsed={options.collapseBacklinks}",
        "-            />",
        "-          </div>",
        "-        </div>",
        "-        {showInspector && (",
        "-          <PageOptionsSidebar",
        "-            documentId={documentId}",
        "-            options={options}",
        "-            stats={stats}",
        "-            onToggle={toggleOption}",
        "-            onSetPageFont={handleSetPageFont}",
        "-            onSetLayoutDensity={handleSetLayoutDensity}",
        "-            onSetEmbedDefaultToCursor={inspectorCanUseEditorBridge ? handleSetEmbedDefaultToCursor : undefined}",
        "-            onClearEmbedDefault={handleClearEmbedDefault}",
        "-            onExport={handleExport}",
        "-            onOpenHistory={() => setHistoryOpen(true)}",
        "-            onOpenComments={() => openCommentsForPage({ workspaceId, documentId })}",
        "-            onUndo={canEditDocument ? handleUndo : undefined}",
        "-            onRedo={canEditDocument ? handleRedo : undefined}",
        "-            onDeletePage={canEditDocument ? handleDeletePage : undefined}",
        "-            onOpenMoveEmbedPicker={canEditDocument ? handleOpenMoveEmbed : undefined}",
        "-            onCopyPageLink={handleCopyPageLink}",
        "-            onCopyPageReference={handleCopyPageReference}",
        "-            onAddToTemplates={canEditDocument ? handleAddToTemplates : undefined}",
        "-          />",
        "-        )}",
        "-      </div>",
        "-      <DocumentHistoryDrawer",
        "-        open={historyOpen}",
        "-        onOpenChange={setHistoryOpen}",
        "-        history={history}",
        "-        onRestore={handleRestoreSnapshot}",
        "-      />",
        "-      <MoveEmbedPickerHost />",
        "-      <DocumentCommentsDrawer />",
        "-      <DocumentAiAgentPanel",
        "-        documentId={documentId}",
        "-        getLatestBlocks={() => latestBlocksRef.current}",
        "-        getLatestPageSubtree={() => pageSubtree}",
        "-      />",
        "-    </ImagePickerProvider>",
        "-  );",
        "-}",
        "+\"use client\";",
        "+",
        "+import dynamic from \"next/dynamic\";",
        "+import { useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent, type KeyboardEvent as ReactKeyboardEvent } from \"react\";",
        "+import type { BooleanPageOptionKey, DocumentStats, PageOptionsState, PageFont, PageLayoutDensity } from \"@/types/page-options\";",
        "+import { useEditorBridgeStore } from \"@/store/editor-bridge\";",
        "+import { usePageLayoutStore } from \"@/store/page-layout\";",
        "+import { useCurrentDocumentStore } from \"@/store/current-document\";",
        "+import type { Json } from \"@/types/supabase\";",
        "+import type { DocumentSnapshot } from \"@/types/document\";",
        "+import { useDebouncedCallback } from \"@/hooks/use-debounced-callback\";",
        "+import { ImagePickerProvider } from \"@/components/media/image-picker-context\";",
        "+import { useRouter } from \"next/navigation\";",
        "+import { DocumentAiAgentPanel } from \"./DocumentAiAgentPanel\";",
        "+import { CONTENT_LOADING_DELAY_MS } from \"@/lib/constants\";",
        "+import { useCommentsUiStore } from \"@/store/comments-ui\";",
        "+import { useAppPreferencesStore } from \"@/store/app-preferences\";",
        "+import { useMoveEmbedPickerStore } from \"@/store/move-embed-picker\";",
        "+import { cn } from \"@/lib/utils\";",
        "+import { Button } from \"@/components/ui/button\";",
        "+import { DocumentToc } from \"@/components/editor/document-toc\";",
        "+import { DocumentReadView } from \"@/components/editor/document-read-view\";",
        "+import { extractPageBlocks, type PageSubtreeProjection } from \"@/lib/documents/page-subtree\";",
        "+import {",
        "+  deleteDocumentCommand,",
        "+  embedDocumentCommand,",
        "+  moveDocumentCommand,",
        "+  renameDocumentCommand,",
        "+} from \"@/lib/documents/tree-command-client\";",
        "+import { EditorHost } from \"@/components/editor/editor-host\";",
        "+",
        "+const PageOptionsSidebar = dynamic(",
        "+  () => import(\"@/components/editor/page-options-sidebar\").then((mod) => mod.PageOptionsSidebar),",
        "+  { ssr: false },",
        "+);",
        "+",
        "+const PageBacklinksPanel = dynamic(",
        "+  () => import(\"@/components/editor/page-backlinks-panel\").then((mod) => mod.PageBacklinksPanel),",
        "+  {",
        "+    ssr: false,",
        "+    loading: () => null,",
        "+  },",
        "+);",
        "+",
        "+const DocumentHistoryDrawer = dynamic(",
        "+  () => import(\"@/components/editor/document-history-drawer\").then((mod) => mod.DocumentHistoryDrawer),",
        "+  {",
        "+    ssr: false,",
        "+    loading: () => null,",
        "+  },",
        "+);",
        "+",
        "+const MoveEmbedPickerHost = dynamic(",
        "+  () => import(\"@/components/documents/move-embed-picker-host\").then((mod) => mod.MoveEmbedPickerHost),",
        "+  {",
        "+    ssr: false,",
        "+    loading: () => null,",
        "+  },",
        "+);",
        "+",
        "+const DocumentCommentsDrawer = dynamic(",
        "+  () => import(\"@/components/editor/document-comments-drawer\").then((mod) => mod.DocumentCommentsDrawer),",
        "+  {",
        "+    ssr: false,",
        "+    loading: () => null,",
        "+  },",
        "+);",
        "+",
        "+export interface DocumentContentProps {",
        "+  documentId: string;",
        "+  workspaceId: string;",
        "+  title: string | null;",
        "+  updatedAt: string | null;",
        "+  initialContent: unknown;",
        "+  initialContentRevision?: number | null;",
        "+  initialConflictDetectionKey?: string | null;",
        "+  initialPageSubtree?: PageSubtreeProjection | null;",
        "+  initialOptions: PageOptionsState;",
        "+  initialStats: DocumentStats | null;",
        "+  openTableId?: string | null;",
        "+  autoEnterEdit?: boolean;",
        "+  readOnly?: boolean;",
        "+  disableDownload?: boolean;",
        "+  disableCopy?: boolean;",
        "+}",
        "+",
        "+const defaultOptions: PageOptionsState = {",
        "+  wideLayout: false,",
        "+  smallText: false,",
        "+  showHeadingNumbers: true,",
        "+  showToc: false,",
        "+  showStructure: false,",
        "+  protectEditing: false,",
        "+  showWordCount: true,",
        "+  collapseBacklinks: false,",
        "+  pageFont: \"default\",",
        "+  layoutDensity: \"normal\",",
        "+  hideChildPages: false,",
        "+  showBlockRefCount: false,",
        "+  embedDefaultBlockId: null,",
        "+};",
        "+const defaultStats: DocumentStats = { wordCount: 0, characterCount: 0, blockCount: 0, todoTotal: 0, todoDone: 0 };",
        "+const EDITOR_UNMOUNT_GRACE_MS = 1000;",
        "+",
        "+export function DocumentContent({",
        "+  documentId,",
        "+  workspaceId,",
        "+  title,",
        "+  updatedAt,",
        "+  initialContent,",
        "+  initialContentRevision = null,",
        "+  initialConflictDetectionKey = null,",
        "+  initialPageSubtree = null,",
        "+  initialOptions,",
        "+  initialStats,",
        "+  openTableId,",
        "+  autoEnterEdit = false,",
        "+  readOnly = false,",
        "+  disableDownload = false,",
        "+  disableCopy = false,",
        "+}: DocumentContentProps) {",
        "+  const setCurrentDocument = useCurrentDocumentStore((state) => state.setCurrent);",
        "+  const clearIfMatch = useCurrentDocumentStore((state) => state.clearIfMatch);",
        "+  const canEditDocument = !readOnly;",
        "+  const [options, setOptions] = useState<PageOptionsState>(initialOptions ?? defaultOptions);",
        "+  const [stats, setStats] = useState<DocumentStats>(initialStats ?? defaultStats);",
        "+  const [history, setHistory] = useState<DocumentSnapshot[]>([]);",
        "+  const [historyOpen, setHistoryOpen] = useState(false);",
        "+  const editorBridge = useEditorBridgeStore((state) => state.bridge);",
        "+  const router = useRouter();",
        "+  const showInspector = usePageLayoutStore((state) => state.showInspector);",
        "+  const openCommentsForPage = useCommentsUiStore((s) => s.openForPage);",
        "+  const spellCheck = useAppPreferencesStore((s) => s.spellCheck);",
        "+  const openMoveEmbedPicker = useMoveEmbedPickerStore((s) => s.openPicker);",
        "+  const [pageTitle, setPageTitle] = useState(title ?? \"无标题\");",
        "+  const [content, setContent] = useState<unknown>(initialContent);",
        "+  const [serverContentSnapshot, setServerContentSnapshot] = useState<unknown>(initialContent);",
        "+  const [serverPageSubtreeSnapshot, setServerPageSubtreeSnapshot] = useState<PageSubtreeProjection | null>(",
        "+    initialPageSubtree,",
        "+  );",
        "+  const [serverPageSubtreeTitle, setServerPageSubtreeTitle] = useState<string>(title ?? \"无标题\");",
        "+  const [contentRevision, setContentRevision] = useState<number | null>(initialContentRevision);",
        "+  const [conflictDetectionKey, setConflictDetectionKey] = useState<string | null>(initialConflictDetectionKey);",
        "+  const [contentLoading, setContentLoading] = useState(() => initialContent == null);",
        "+  const [contentError, setContentError] = useState<string | null>(null);",
        "+  const [contentReloadKey, setContentReloadKey] = useState(0);",
        "+  const [showContentLoadingIndicator, setShowContentLoadingIndicator] = useState(false);",
        "+  const shouldStartEditing = canEditDocument;",
        "+  const [isEditing, setIsEditing] = useState(() => shouldStartEditing);",
        "+  const [keepEditorMounted, setKeepEditorMounted] = useState(() => shouldStartEditing);",
        "+  const contentLoadingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);",
        "+  const editorUnmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);",
        "+  const pendingOpenTableRef = useRef<string | null>(null);",
        "+  const latestBlocksRef = useRef<Json | null>(null);",
        "+  const pageRootRef = useRef<HTMLDivElement>(null);",
        "+  const readViewRootRef = useRef<HTMLDivElement>(null);",
        "+  const pendingRestoreSnapshotRef = useRef<DocumentSnapshot | null>(null);",
        "+  const lastCopyBlockedAtRef = useRef<number>(0);",
        "+",
        "+  useEffect(() => {",
        "+    setCurrentDocument(documentId, readOnly, disableDownload, disableCopy);",
        "+    return () => {",
        "+      clearIfMatch(documentId);",
        "+    };",
        "+  }, [clearIfMatch, disableCopy, disableDownload, documentId, readOnly, setCurrentDocument]);",
        "+",
        "+  useEffect(() => {",
        "+    if (!disableCopy) return;",
        "+",
        "+    const toElement = (node: Node | null): Element | null => {",
        "+      if (!node) return null;",
        "+      if (node.nodeType === Node.TEXT_NODE) {",
        "+        return node.parentElement;",
        "+      }",
        "+      return node instanceof Element ? node : null;",
        "+    };",
        "+",
        "+    const isEventInsidePage = () => {",
        "+      const root = pageRootRef.current;",
        "+      if (!root) return false;",
        "+      const selection = typeof window !== \"undefined\" ? window.getSelection() : null;",
        "+      const anchor = selection?.anchorNode ?? null;",
        "+      const focus = selection?.focusNode ?? null;",
        "+      const anchorEl = toElement(anchor);",
        "+      const focusEl = toElement(focus);",
        "+      return Boolean((anchorEl && root.contains(anchorEl)) || (focusEl && root.contains(focusEl)));",
        "+    };",
        "+",
        "+    const notifyBlocked = () => {",
        "+      const now = Date.now();",
        "+      if (now - lastCopyBlockedAtRef.current < 1200) return;",
        "+      lastCopyBlockedAtRef.current = now;",
        "+      window.alert(\"该页面已禁止复制\");",
        "+    };",
        "+",
        "+    const onCopy = (event: ClipboardEvent) => {",
        "+      if (!isEventInsidePage()) return;",
        "+      event.preventDefault();",
        "+      event.stopPropagation();",
        "+      notifyBlocked();",
        "+    };",
        "+",
        "+    const onCut = (event: ClipboardEvent) => {",
        "+      if (!isEventInsidePage()) return;",
        "+      event.preventDefault();",
        "+      event.stopPropagation();",
        "+      notifyBlocked();",
        "+    };",
        "+",
        "+    const onKeyDown = (event: KeyboardEvent) => {",
        "+      if (!isEventInsidePage()) return;",
        "+      const key = String(event.key ?? \"\").toLowerCase();",
        "+      const ctrlOrMeta = event.ctrlKey || event.metaKey;",
        "+      if (!ctrlOrMeta) return;",
        "+      if (key === \"c\" || key === \"x\" || key === \"insert\") {",
        "+        event.preventDefault();",
        "+        event.stopPropagation();",
        "+        notifyBlocked();",
        "+      }",
        "+    };",
        "+",
        "+    document.addEventListener(\"copy\", onCopy, true);",
        "+    document.addEventListener(\"cut\", onCut, true);",
        "+    document.addEventListener(\"keydown\", onKeyDown, true);",
        "+    return () => {",
        "+      document.removeEventListener(\"copy\", onCopy, true);",
        "+      document.removeEventListener(\"cut\", onCut, true);",
        "+      document.removeEventListener(\"keydown\", onKeyDown, true);",
        "+    };",
        "+  }, [disableCopy]);",
        "+",
        "+  useEffect(() => {",
        "+    const tableId = (openTableId ?? \"\").trim();",
        "+    if (!tableId) return;",
        "+    if (!editorBridge?.openTableFullScreen) return;",
        "+    if (pendingOpenTableRef.current === tableId) return;",
        "+    pendingOpenTableRef.current = tableId;",
        "+",
        "+    editorBridge.openTableFullScreen(tableId);",
        "+",
        "+    if (typeof window !== \"undefined\") {",
        "+      try {",
        "+        const url = new URL(window.location.href);",
        "+        url.searchParams.delete(\"openTableId\");",
        "+        window.history.replaceState({}, \"\", url.toString());",
        "+      } catch {",
        "+        router.replace(`/documents/${documentId}`);",
        "+      }",
        "+    }",
        "+  }, [documentId, editorBridge, openTableId, router]);",
        "+",
        "+  useEffect(() => {",
        "+    setPageTitle(title ?? \"无标题\");",
        "+  }, [title]);",
        "+",
        "+  useEffect(() => {",
        "+    setServerPageSubtreeTitle(title ?? \"无标题\");",
        "+  }, [title]);",
        "+",
        "+  useEffect(() => {",
        "+    setServerPageSubtreeSnapshot(initialPageSubtree);",
        "+  }, [initialPageSubtree]);",
        "+",
        "+  useEffect(() => {",
        "+    setOptions(initialOptions ?? defaultOptions);",
        "+  }, [initialOptions]);",
        "+",
        "+  useEffect(() => {",
        "+    setStats(initialStats ?? defaultStats);",
        "+  }, [initialStats]);",
        "+",
        "+  useEffect(() => {",
        "+    setContentRevision(initialContentRevision);",
        "+  }, [initialContentRevision]);",
        "+",
        "+  useEffect(() => {",
        "+    setConflictDetectionKey(initialConflictDetectionKey);",
        "+  }, [initialConflictDetectionKey]);",
        "+",
        "+  useEffect(() => {",
        "+    setServerContentSnapshot(initialContent);",
        "+  }, [initialContent]);",
        "+",
        "+  useEffect(() => {",
        "+    const nextBlocks = extractPageBlocks(content);",
        "+    latestBlocksRef.current = nextBlocks.length > 0 ? (nextBlocks as Json) : null;",
        "+  }, [content]);",
        "+",
        "+  useEffect(() => {",
        "+    const shouldForceEdit = Boolean((openTableId ?? \"\").trim()) && canEditDocument;",
        "+    if (shouldForceEdit) {",
        "+      setIsEditing(true);",
        "+      setKeepEditorMounted(true);",
        "+    }",
        "+  }, [canEditDocument, openTableId]);",
        "+",
        "+  useEffect(() => {",
        "+    if (isEditing) {",
        "+      if (editorUnmountTimerRef.current) {",
        "+        clearTimeout(editorUnmountTimerRef.current);",
        "+        editorUnmountTimerRef.current = null;",
        "+      }",
        "+      setKeepEditorMounted(true);",
        "+      return;",
        "+    }",
        "+    if (editorUnmountTimerRef.current) {",
        "+      clearTimeout(editorUnmountTimerRef.current);",
        "+    }",
        "+    editorUnmountTimerRef.current = setTimeout(() => {",
        "+      setKeepEditorMounted(false);",
        "+      editorUnmountTimerRef.current = null;",
        "+    }, EDITOR_UNMOUNT_GRACE_MS);",
        "+    return () => {",
        "+      if (editorUnmountTimerRef.current) {",
        "+        clearTimeout(editorUnmountTimerRef.current);",
        "+        editorUnmountTimerRef.current = null;",
        "+      }",
        "+    };",
        "+  }, [isEditing]);",
        "+",
        "+  useEffect(() => {",
        "+    let canceled = false;",
        "+    const controller = new AbortController();",
        "+",
        "+    const load = async () => {",
        "+      setContentError(null);",
        "+      setContentLoading(initialContent == null);",
        "+      setContent(initialContent);",
        "+      setShowContentLoadingIndicator(false);",
        "+",
        "+      if (initialContent != null) {",
        "+        return;",
        "+      }",
        "+",
        "+      if (contentLoadingTimerRef.current) {",
        "+        clearTimeout(contentLoadingTimerRef.current);",
        "+        contentLoadingTimerRef.current = null;",
        "+      }",
        "+      contentLoadingTimerRef.current = setTimeout(() => {",
        "+        if (!canceled) {",
        "+          setShowContentLoadingIndicator(true);",
        "+        }",
        "+      }, CONTENT_LOADING_DELAY_MS);",
        "+",
        "+      try {",
        "+        const response = await fetch(",
        "+          `/api/documents/content?documentId=${encodeURIComponent(documentId)}&workspaceId=${encodeURIComponent(workspaceId)}`,",
        "+          {",
        "+            method: \"GET\",",
        "+            credentials: \"include\",",
        "+            signal: controller.signal,",
        "+          },",
        "+        );",
        "+        if (!response.ok) {",
        "+          const payload = await response.json().catch(() => ({}));",
        "+          throw new Error(payload?.error ?? \"加载页面内容失败\");",
        "+        }",
        "+        const payload = (await response.json()) as {",
        "+          content?: unknown;",
        "+          revision?: number | null;",
        "+          conflictDetectionKey?: string | null;",
        "+          pageSubtree?: PageSubtreeProjection | null;",
        "+        };",
        "+        if (canceled) return;",
        "+        setContent(payload.content ?? null);",
        "+        setServerContentSnapshot(payload.content ?? null);",
        "+        setContentRevision(",
        "+          typeof payload.revision === \"number\" && Number.isInteger(payload.revision)",
        "+            ? payload.revision",
        "+            : 0,",
        "+        );",
        "+        setConflictDetectionKey(",
        "+          typeof payload.conflictDetectionKey === \"string\" && payload.conflictDetectionKey.trim()",
        "+            ? payload.conflictDetectionKey",
        "+            : `${documentId}:0`,",
        "+        );",
        "+        setServerPageSubtreeSnapshot(payload.pageSubtree ?? null);",
        "+        setServerPageSubtreeTitle(",
        "+          typeof payload.pageSubtree?.rootNode.metadata.title === \"string\" &&",
        "+            payload.pageSubtree.rootNode.metadata.title.trim()",
        "+            ? payload.pageSubtree.rootNode.metadata.title",
        "+            : title ?? \"无标题\",",
        "+        );",
        "+      } catch (error) {",
        "+        if (canceled) return;",
        "+        if ((error as { name?: string })?.name === \"AbortError\") return;",
        "+        setContentError(error instanceof Error ? error.message : \"加载页面内容失败\");",
        "+      } finally {",
        "+        if (!canceled) {",
        "+          setContentLoading(false);",
        "+          setShowContentLoadingIndicator(false);",
        "+        }",
        "+        if (contentLoadingTimerRef.current) {",
        "+          clearTimeout(contentLoadingTimerRef.current);",
        "+          contentLoadingTimerRef.current = null;",
        "+        }",
        "+      }",
        "+    };",
        "+",
        "+    void load();",
        "+",
        "+    return () => {",
        "+      canceled = true;",
        "+      controller.abort();",
        "+      if (contentLoadingTimerRef.current) {",
        "+        clearTimeout(contentLoadingTimerRef.current);",
        "+        contentLoadingTimerRef.current = null;",
        "+      }",
        "+    };",
        "+  }, [documentId, initialContent, contentReloadKey, title, workspaceId]);",
        "+",
        "+  const persistTitle = useCallback(",
        "+    async (nextTitle: string) => {",
        "+      if (readOnly) return;",
        "+      const payload = nextTitle.trim() || \"无标题\";",
        "+      try {",
        "+        await renameDocumentCommand({ documentId, workspaceId, title: payload });",
        "+      } catch (error) {",
        "+        console.error(\"更新页面标题失败\", error);",
        "+      }",
        "+    },",
        "+    [documentId, readOnly, workspaceId],",
        "+  );",
        "+",
        "+  const debouncedPersistTitle = useDebouncedCallback((value: string) => {",
        "+    void persistTitle(value);",
        "+  }, 600);",
        "+",
        "+  const handleTitleChange = (event: ChangeEvent<HTMLInputElement>) => {",
        "+    if (!canEditDocument) return;",
        "+    const value = event.target.value;",
        "+    setPageTitle(value);",
        "+    debouncedPersistTitle(value);",
        "+  };",
        "+",
        "+  const handleTitleBlur = () => {",
        "+    if (!canEditDocument) return;",
        "+    void persistTitle(pageTitle);",
        "+  };",
        "+",
        "+  const handleTitleKeyDown = (event: ReactKeyboardEvent<HTMLInputElement>) => {",
        "+    if (event.key === \"Enter\") {",
        "+      event.preventDefault();",
        "+      event.currentTarget.blur();",
        "+    }",
        "+  };",
        "+",
        "+  const persistOptions = useCallback(",
        "+    async (patch: Partial<PageOptionsState>) => {",
        "+      if (readOnly) return;",
        "+      try {",
        "+        const response = await fetch(\"/api/documents/options\", {",
        "+          method: \"POST\",",
        "+          headers: { \"Content-Type\": \"application/json\" },",
        "+          body: JSON.stringify({ documentId, workspaceId, options: patch }),",
        "+        });",
        "+        if (!response.ok) {",
        "+          const payload = await response.json().catch(() => null);",
        "+          console.error(payload?.error ?? \"更新页面选项失败\");",
        "+        }",
        "+      } catch (error) {",
        "+        console.error(error);",
        "+      }",
        "+    },",
        "+    [documentId, readOnly, workspaceId],",
        "+  );",
        "+",
        "+  const toggleOption = useCallback(",
        "+    (key: BooleanPageOptionKey) => {",
        "+      if (readOnly) return;",
        "+      setOptions((prev) => {",
        "+        const nextValue = !prev[key];",
        "+        const next = { ...prev, [key]: nextValue };",
        "+        void persistOptions({ [key]: nextValue } as Partial<PageOptionsState>);",
        "+        return next;",
        "+      });",
        "+    },",
        "+    [persistOptions, readOnly],",
        "+  );",
        "+",
        "+  const setOptionPatch = useCallback(",
        "+    (patch: Partial<PageOptionsState>) => {",
        "+      if (readOnly) return;",
        "+      setOptions((prev) => {",
        "+        const next = { ...prev, ...patch };",
        "+        void persistOptions(patch);",
        "+        return next;",
        "+      });",
        "+    },",
        "+    [persistOptions, readOnly],",
        "+  );",
        "+",
        "+  const closeToc = useCallback(() => {",
        "+    if (readOnly) return;",
        "+    setOptions((prev) => {",
        "+      if (!prev.showToc) return prev;",
        "+      const next = { ...prev, showToc: false };",
        "+      void persistOptions({ showToc: false });",
        "+      return next;",
        "+    });",
        "+  }, [persistOptions, readOnly]);",
        "+",
        "+  const handleSetPageFont = useCallback(",
        "+    (font: PageFont) => {",
        "+      setOptionPatch({ pageFont: font });",
        "+    },",
        "+    [setOptionPatch],",
        "+  );",
        "+",
        "+  const handleSetLayoutDensity = useCallback(",
        "+    (density: PageLayoutDensity) => {",
        "+      setOptionPatch({ layoutDensity: density });",
        "+    },",
        "+    [setOptionPatch],",
        "+  );",
        "+",
        "+  const handleSetEmbedDefaultToCursor = useCallback(() => {",
        "+    if (!canEditDocument) return;",
        "+    if (!isEditing) {",
        "+      setIsEditing(true);",
        "+      return;",
        "+    }",
        "+    const blockId = editorBridge?.getCursorBlockId?.() ?? null;",
        "+    if (!blockId) {",
        "+      window.alert(\"未找到当前光标所在块，请先在编辑器中点击任意块\");",
        "+      return;",
        "+    }",
        "+    setOptionPatch({ embedDefaultBlockId: blockId });",
        "+    window.alert(\"已设置“嵌入默认位置”\");",
        "+  }, [canEditDocument, editorBridge, isEditing, setOptionPatch]);",
        "+",
        "+  const handleClearEmbedDefault = useCallback(() => {",
        "+    if (readOnly) return;",
        "+    setOptionPatch({ embedDefaultBlockId: null });",
        "+    window.alert(\"已清除“嵌入默认位置”\");",
        "+  }, [readOnly, setOptionPatch]);",
        "+",
        "+  useEffect(() => {",
        "+    const onKeyDown = (event: KeyboardEvent) => {",
        "+      const root = pageRootRef.current;",
        "+      if (root) {",
        "+        const target = event.target;",
        "+        if (target && target instanceof Node && !root.contains(target)) {",
        "+          return;",
        "+        }",
        "+      }",
        "+      const ctrlOrMeta = event.ctrlKey || event.metaKey;",
        "+      if (!ctrlOrMeta) return;",
        "+      if (!event.shiftKey) return;",
        "+      const key = String(event.key ?? \"\").toLowerCase();",
        "+      if (key === \"l\") {",
        "+        event.preventDefault();",
        "+        toggleOption(\"showToc\");",
        "+      } else if (key === \"c\") {",
        "+        event.preventDefault();",
        "+        toggleOption(\"protectEditing\");",
        "+      }",
        "+    };",
        "+    window.addEventListener(\"keydown\", onKeyDown);",
        "+    return () => window.removeEventListener(\"keydown\", onKeyDown);",
        "+  }, [toggleOption]);",
        "+",
        "+  const buildDocumentUrl = useCallback((id: string): string => {",
        "+    if (typeof window === \"undefined\" || !window.location) {",
        "+      return `/documents/${id}`;",
        "+    }",
        "+    return `${window.location.origin}/documents/${id}`;",
        "+  }, []);",
        "+",
        "+  const copyText = useCallback(async (text: string, successMessage: string) => {",
        "+    if (typeof navigator !== \"undefined\" && navigator.clipboard) {",
        "+      try {",
        "+        await navigator.clipboard.writeText(text);",
        "+        window.alert(successMessage);",
        "+        return;",
        "+      } catch {",
        "+      }",
        "+    }",
        "+    window.prompt(\"复制失败，请手动复制内容\", text);",
        "+  }, []);",
        "+",
        "+  const handleCopyPageLink = useCallback(",
        "+    async (includeTitle: boolean) => {",
        "+      const url = buildDocumentUrl(documentId);",
        "+      if (includeTitle) {",
        "+        const text = `${pageTitle || \"无标题\"}\\n${url}`;",
        "+        await copyText(text, \"标题 + 链接已复制\");",
        "+        return;",
        "+      }",
        "+      await copyText(url, \"页面链接已复制\");",
        "+    },",
        "+    [buildDocumentUrl, copyText, documentId, pageTitle],",
        "+  );",
        "+",
        "+  const handleCopyPageReference = useCallback(",
        "+    async (mode: \"inline\" | \"embed\") => {",
        "+      const template = mode === \"inline\" ? `((${documentId}))` : `{{${documentId}}}`;",
        "+      await copyText(template, mode === \"inline\" ? \"行内引用已复制\" : \"嵌入引用已复制\");",
        "+    },",
        "+    [copyText, documentId],",
        "+  );",
        "+",
        "+  const handleUndo = useCallback(() => {",
        "+    if (!isEditing) {",
        "+      setIsEditing(true);",
        "+      return;",
        "+    }",
        "+    editorBridge?.undo?.();",
        "+  }, [editorBridge, isEditing]);",
        "+",
        "+  const handleRedo = useCallback(() => {",
        "+    if (!isEditing) {",
        "+      setIsEditing(true);",
        "+      return;",
        "+    }",
        "+    editorBridge?.redo?.();",
        "+  }, [editorBridge, isEditing]);",
        "+",
        "+  const handleDeletePage = useCallback(async () => {",
        "+    if (readOnly) return;",
        "+    const ok = window.confirm(\"确定删除该页面吗？删除后会进入垃圾桶。\");",
        "+    if (!ok) return;",
        "+    try {",
        "+      await deleteDocumentCommand({ documentId, workspaceId });",
        "+    } catch (error) {",
        "+      const payload = error instanceof Error ? error.message : null;",
        "+      window.alert(payload ?? \"删除失败\");",
        "+      return;",
        "+    }",
        "+    router.push(\"/\");",
        "+    router.refresh();",
        "+  }, [documentId, readOnly, router, workspaceId]);",
        "+",
        "+  const handleOpenMoveEmbed = useCallback(() => {",
        "+    if (readOnly) return;",
        "+    openMoveEmbedPicker({",
        "+      workspaceId,",
        "+      defaultMode: \"move\",",
        "+      modes: [\"move\", \"embed\"],",
        "+      allowRoot: true,",
        "+      excludeIds: [documentId],",
        "+      onPick: async (mode, targetId) => {",
        "+        if (mode === \"move\") {",
        "+          try {",
        "+            await moveDocumentCommand({",
        "+              documentId,",
        "+              parentId: targetId ?? null,",
        "+              position: 999999,",
        "+              workspaceId,",
        "+            });",
        "+          } catch (error) {",
        "+            window.alert(error instanceof Error ? error.message : \"移动失败\");",
        "+            return;",
        "+          }",
        "+          window.alert(\"移动成功\");",
        "+          router.refresh();",
        "+          return;",
        "+        }",
        "+",
        "+        if (!targetId) {",
        "+          window.alert(\"请选择目标页面\");",
        "+          return;",
        "+        }",
        "+",
        "+        try {",
        "+          await embedDocumentCommand({",
        "+            sourceId: documentId,",
        "+            targetId,",
        "+          });",
        "+        } catch (error) {",
        "+          const message = error instanceof Error ? error.message : \"嵌入失败\";",
        "+          window.alert(message);",
        "+          return;",
        "+        }",
        "+",
        "+        window.alert(\"已嵌入到目标页面\");",
        "+      },",
        "+    });",
        "+  }, [documentId, openMoveEmbedPicker, readOnly, router, workspaceId]);",
        "+",
        "+  const handleAddToTemplates = useCallback(async () => {",
        "+    if (readOnly) return;",
        "+    const ok = window.confirm(\"将该页面添加为模板？\");",
        "+    if (!ok) return;",
        "+    const resp = await fetch(\"/api/documents/template\", {",
        "+      method: \"POST\",",
        "+      headers: { \"Content-Type\": \"application/json\" },",
        "+      body: JSON.stringify({ documentId, isTemplate: true }),",
        "+    });",
        "+    if (!resp.ok) {",
        "+      const payload = await resp.json().catch(() => ({}));",
        "+      window.alert(payload?.error ?? \"设置模板失败\");",
        "+      return;",
        "+    }",
        "+    window.alert(\"已添加为模板\");",
        "+    router.refresh();",
        "+  }, [documentId, readOnly, router]);",
        "+",
        "+  const formattedUpdatedAt = useMemo(() => {",
        "+    if (!updatedAt) return \"\";",
        "+    const date = new Date(updatedAt);",
        "+    if (Number.isNaN(date.getTime())) return \"\";",
        "+    return new Intl.DateTimeFormat(\"zh-CN\", {",
        "+      timeZone: \"Asia/Shanghai\",",
        "+      year: \"numeric\",",
        "+      month: \"numeric\",",
        "+      day: \"numeric\",",
        "+      hour: \"2-digit\",",
        "+      minute: \"2-digit\",",
        "+      second: \"2-digit\",",
        "+      hour12: false,",
        "+    }).format(date);",
        "+  }, [updatedAt]);",
        "+",
        "+  const handleExport = useCallback(() => {",
        "+    if (disableDownload) {",
        "+      window.alert(\"该页面已禁止下载\");",
        "+      return;",
        "+    }",
        "+    const latest = history[0];",
        "+    const exportBlocks = latest?.blocks ?? latestBlocksRef.current;",
        "+    if (!exportBlocks) {",
        "+      window.alert(\"暂无可导出的内容\");",
        "+      return;",
        "+    }",
        "+    const payload = JSON.stringify(exportBlocks, null, 2);",
        "+    const blob = new Blob([payload], { type: \"application/json\" });",
        "+    const url = URL.createObjectURL(blob);",
        "+    const anchor = document.createElement(\"a\");",
        "+    anchor.href = url;",
        "+    anchor.download = `${title ?? \"未命名页面\"}-${new Date().toISOString()}.json`;",
        "+    anchor.click();",
        "+    URL.revokeObjectURL(url);",
        "+  }, [disableDownload, history, title]);",
        "+",
        "+  const pageRootClass = cn(",
        "+    \"flex h-full overflow-hidden bg-wolai-bg\",",
        "+    options.pageFont === \"song\" && \"wolai-page-font-song\",",
        "+    options.pageFont === \"kai\" && \"wolai-page-font-kai\",",
        "+    options.layoutDensity === \"compact\" && \"wolai-page-density-compact\",",
        "+    options.layoutDensity === \"spacious\" && \"wolai-page-density-spacious\",",
        "+    options.smallText && \"wolai-small-text\",",
        "+    options.hideChildPages && \"wolai-hide-child-pages\",",
        "+  );",
        "+  const pageSubtree = useMemo(() => {",
        "+    const hasServerPageSubtree = Boolean(serverPageSubtreeSnapshot);",
        "+    const titleUnchanged = pageTitle === serverPageSubtreeTitle;",
        "+    const contentUnchanged = content === serverContentSnapshot;",
        "+",
        "+    if (hasServerPageSubtree && titleUnchanged && contentUnchanged) {",
        "+      return serverPageSubtreeSnapshot;",
        "+    }",
        "+    return null;",
        "+  }, [",
        "+    content,",
        "+    pageTitle,",
        "+    serverContentSnapshot,",
        "+    serverPageSubtreeSnapshot,",
        "+    serverPageSubtreeTitle,",
        "+  ]);",
        "+  const readViewTocEntries = useMemo(",
        "+    () =>",
        "+      (pageSubtree?.outline ?? [])",
        "+        .filter((entry) => typeof entry.anchorBlockId === \"string\" && entry.anchorBlockId.trim())",
        "+        .map(({ anchorBlockId, level, numbering, title: entryTitle }) => ({",
        "+          id: anchorBlockId as string,",
        "+          level,",
        "+          numbering,",
        "+          title: entryTitle,",
        "+        })),",
        "+    [pageSubtree],",
        "+  );",
        "+  const inspectorCanUseEditorBridge = canEditDocument && isEditing;",
        "+",
        "+  const jumpToHeading = useCallback((headingId: string) => {",
        "+    const targetRoot = !isEditing ? readViewRootRef.current : pageRootRef.current;",
        "+    const target = targetRoot?.querySelector<HTMLElement>(`[data-id=\"${headingId}\"]`);",
        "+    if (target) {",
        "+      target.scrollIntoView({ behavior: \"smooth\", block: \"center\" });",
        "+    }",
        "+  }, [isEditing]);",
        "+",
        "+  const handleSnapshot = useCallback((payload: { blocks: Json; stats: DocumentStats }) => {",
        "+    setContent(payload.blocks);",
        "+    latestBlocksRef.current = payload.blocks;",
        "+    setHistory((prev) => {",
        "+      const now = Date.now();",
        "+      if (prev.length > 0 && now - prev[0].timestamp < 4000) {",
        "+        return prev;",
        "+      }",
        "+      const snapshot: DocumentSnapshot = {",
        "+        id: `${now}-${Math.random().toString(36).slice(2, 8)}`,",
        "+        timestamp: now,",
        "+        blocks: payload.blocks,",
        "+        stats: payload.stats,",
        "+      };",
        "+      return [snapshot, ...prev].slice(0, 15);",
        "+    });",
        "+  }, []);",
        "+",
        "+  const persistStatsRequest = useCallback((next: DocumentStats) => {",
        "+    void fetch(\"/api/documents/stats\", {",
        "+      method: \"POST\",",
        "+      headers: { \"Content-Type\": \"application/json\" },",
        "+      body: JSON.stringify({ documentId, workspaceId, stats: next }),",
        "+    }).catch((error) => console.error(error));",
        "+  }, [documentId, workspaceId]);",
        "+",
        "+  const persistStats = useDebouncedCallback(persistStatsRequest, 1500);",
        "+",
        "+  const handleStatsChange = useCallback(",
        "+    (nextStats: DocumentStats) => {",
        "+      setStats(nextStats);",
        "+      persistStats(nextStats);",
        "+    },",
        "+    [persistStats],",
        "+  );",
        "+",
        "+  const handleRestoreSnapshot = useCallback(",
        "+    (snapshot: DocumentSnapshot) => {",
        "+      if (!isEditing || !editorBridge) {",
        "+        pendingRestoreSnapshotRef.current = snapshot;",
        "+        setIsEditing(true);",
        "+        return;",
        "+      }",
        "+      editorBridge.replaceWithSnapshot(snapshot.blocks);",
        "+      setHistoryOpen(false);",
        "+    },",
        "+    [editorBridge, isEditing],",
        "+  );",
        "+",
        "+  const handleEnterEditMode = useCallback(() => {",
        "+    if (!canEditDocument) return;",
        "+    setIsEditing(true);",
        "+  }, [canEditDocument]);",
        "+",
        "+  const handleExitEditMode = useCallback(() => {",
        "+    setIsEditing(false);",
        "+  }, []);",
        "+",
        "+  useEffect(() => {",
        "+    if (!isEditing) return;",
        "+    if (!editorBridge) return;",
        "+    const pendingSnapshot = pendingRestoreSnapshotRef.current;",
        "+    if (!pendingSnapshot) return;",
        "+    pendingRestoreSnapshotRef.current = null;",
        "+    editorBridge.replaceWithSnapshot(pendingSnapshot.blocks);",
        "+    setHistoryOpen(false);",
        "+  }, [editorBridge, isEditing]);",
        "+  return (",
        "+    <ImagePickerProvider documentId={documentId} workspaceId={workspaceId}>",
        "+      <div className={pageRootClass} ref={pageRootRef}>",
        "+        <div className=\"flex h-full flex-1 flex-col overflow-hidden\">",
        "+          <div className=\"border-b border-wolai-border px-12 pb-6 pt-8\">",
        "+            <div className=\"flex items-start justify-between gap-6\">",
        "+              <div className=\"min-w-0 flex-1\">",
        "+                {isEditing && canEditDocument ? (",
        "+                  <div className=\"relative\">",
        "+                    <input",
        "+                      value={pageTitle}",
        "+                      onChange={handleTitleChange}",
        "+                      onBlur={handleTitleBlur}",
        "+                      onKeyDown={handleTitleKeyDown}",
        "+                      placeholder=\"无标题\"",
        "+                      className=\"w-full border-none bg-transparent text-3xl font-semibold text-wolai-text-primary outline-none focus:ring-0\"",
        "+                      aria-label=\"页面标题\"",
        "+                      disabled={options.protectEditing || readOnly}",
        "+                      spellCheck={spellCheck}",
        "+                    />",
        "+                  </div>",
        "+                ) : (",
        "+                  <h1 className=\"break-words text-3xl font-semibold text-wolai-text-primary\">",
        "+                    {pageTitle || \"无标题\"}",
        "+                  </h1>",
        "+                )}",
        "+                {readOnly ? (",
        "+                  <p className=\"mt-1 text-sm text-gray-500\">该页面为只读共享，无法编辑。</p>",
        "+                ) : options.protectEditing && isEditing ? (",
        "+                  <p className=\"mt-1 text-sm text-[#b91c1c]\">当前页面已开启编辑保护，关闭后方可修改内容。</p>",
        "+                ) : !isEditing && canEditDocument ? (",
        "+                  <p className=\"mt-1 text-sm text-gray-500\">当前为阅读态，编辑器仅在进入编辑后挂载。</p>",
        "+                ) : null}",
        "+                <p className=\"text-sm text-wolai-text-secondary\">最近更新：{formattedUpdatedAt}</p>",
        "+              </div>",
        "+              {canEditDocument && (",
        "+                <div className=\"flex shrink-0 items-center gap-2\">",
        "+                  {isEditing ? (",
        "+                    <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleExitEditMode}>",
        "+                      返回阅读",
        "+                    </Button>",
        "+                  ) : (",
        "+                    <Button type=\"button\" size=\"sm\" onClick={handleEnterEditMode}>",
        "+                      进入编辑",
        "+                    </Button>",
        "+                  )}",
        "+                </div>",
        "+              )}",
        "+            </div>",
        "+          </div>",
        "+          <div className=\"relative flex-1 overflow-y-auto px-12 py-6\">",
        "+            {contentLoading ? (",
        "+              showContentLoadingIndicator ? (",
        "+                <div className=\"flex h-64 items-center justify-center text-sm text-gray-400\">",
        "+                  页面内容加载中...",
        "+                </div>",
        "+              ) : (",
        "+                <div className=\"h-64\" />",
        "+              )",
        "+            ) : contentError ? (",
        "+              <div className=\"flex h-64 flex-col items-center justify-center gap-2 text-sm text-red-600\">",
        "+                <div>{contentError}</div>",
        "+                <button",
        "+                  type=\"button\"",
        "+                  className=\"rounded-md border border-red-200 bg-red-50 px-3 py-1 text-sm text-red-700 hover:bg-red-100\"",
        "+                  onClick={() => {",
        "+                    setContentError(null);",
        "+                    setContentLoading(true);",
        "+                    setContentReloadKey((prev) => prev + 1);",
        "+                  }}",
        "+                >",
        "+                  重试",
        "+                </button>",
        "+              </div>",
        "+            ) : (",
        "+              <div className=\"relative\">",
        "+                {keepEditorMounted && (",
        "+                  <div className={cn(!isEditing && \"pointer-events-none absolute inset-0 opacity-0\")} aria-hidden={!isEditing}>",
        "+                    <EditorHost",
        "+                      documentId={documentId}",
        "+                      workspaceId={workspaceId}",
        "+                      initialContent={content}",
        "+                      initialRevision={contentRevision}",
        "+                      initialConflictDetectionKey={conflictDetectionKey}",
        "+                      pageOptions={options}",
        "+                      readOnly={readOnly}",
        "+                      onStatsChange={handleStatsChange}",
        "+                      onSnapshot={handleSnapshot}",
        "+                      onCloseToc={closeToc}",
        "+                      onPersistedMetaChange={(meta: { revision: number | null; conflictDetectionKey: string | null }) => {",
        "+                        setContentRevision(meta.revision);",
        "+                        setConflictDetectionKey(meta.conflictDetectionKey);",
        "+                      }}",
        "+                    />",
        "+                  </div>",
        "+                )}",
        "+                {!isEditing && (",
        "+                  <div className=\"relative\" ref={readViewRootRef}>",
        "+                    <DocumentReadView",
        "+                      content={content}",
        "+                      documentId={documentId}",
        "+                      options={options}",
        "+                      pageSubtree={pageSubtree}",
        "+                      className=\"mx-auto w-full max-w-[980px]\"",
        "+                    />",
        "+                    <DocumentToc",
        "+                      entries={readViewTocEntries}",
        "+                      visible={options.showToc}",
        "+                      onJump={jumpToHeading}",
        "+                      onClose={closeToc}",
        "+                    />",
        "+                  </div>",
        "+                )}",
        "+              </div>",
        "+            )}",
        "+            <PageBacklinksPanel",
        "+              className=\"mt-10\"",
        "+              workspaceId={workspaceId}",
        "+              documentId={documentId}",
        "+              defaultCollapsed={options.collapseBacklinks}",
        "+            />",
        "+          </div>",
        "+        </div>",
        "+        {showInspector && (",
        "+          <PageOptionsSidebar",
        "+            documentId={documentId}",
        "+            options={options}",
        "+            stats={stats}",
        "+            onToggle={toggleOption}",
        "+            onSetPageFont={handleSetPageFont}",
        "+            onSetLayoutDensity={handleSetLayoutDensity}",
        "+            onSetEmbedDefaultToCursor={inspectorCanUseEditorBridge ? handleSetEmbedDefaultToCursor : undefined}",
        "+            onClearEmbedDefault={handleClearEmbedDefault}",
        "+            onExport={handleExport}",
        "+            onOpenHistory={() => setHistoryOpen(true)}",
        "+            onOpenComments={() => openCommentsForPage({ workspaceId, documentId })}",
        "+            onUndo={canEditDocument ? handleUndo : undefined}",
        "+            onRedo={canEditDocument ? handleRedo : undefined}",
        "+            onDeletePage={canEditDocument ? handleDeletePage : undefined}",
        "+            onOpenMoveEmbedPicker={canEditDocument ? handleOpenMoveEmbed : undefined}",
        "+            onCopyPageLink={handleCopyPageLink}",
        "+            onCopyPageReference={handleCopyPageReference}",
        "+            onAddToTemplates={canEditDocument ? handleAddToTemplates : undefined}",
        "+          />",
        "+        )}",
        "+      </div>",
        "+      <DocumentHistoryDrawer",
        "+        open={historyOpen}",
        "+        onOpenChange={setHistoryOpen}",
        "+        history={history}",
        "+        onRestore={handleRestoreSnapshot}",
        "+      />",
        "+      <MoveEmbedPickerHost />",
        "+      <DocumentCommentsDrawer />",
        "+      <DocumentAiAgentPanel",
        "+        documentId={documentId}",
        "+        getLatestBlocks={() => latestBlocksRef.current}",
        "+        getLatestPageSubtree={() => pageSubtree}",
        "+      />",
        "+    </ImagePickerProvider>",
        "+  );",
        "+}"
      ]
    }
  ],
  "userModified": false,
  "replaceAll": false,
  "gitDiff": null
}