tiptap-markdown-react 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/components/CitationRefView.tsx","../src/createCitationRef.ts","../src/commentAnchor/commentAnchorPlugin.ts","../src/commentAnchor/commentAnchorController.ts","../src/findReplace.ts","../src/icons/index.tsx","../src/labels.ts","../src/i18n/zh.ts","../src/i18n/index.ts","../src/components/FindReplaceBar.tsx","../src/importPlaceholder.ts","../src/i18n/store.ts","../src/components/ImageUploadView.tsx","../src/imageUpload.ts","../src/imageAlign.ts","../src/emoji/emojiCategories.ts","../src/emoji/filterEmoji.ts","../src/emoji/EmojiSuggestion.tsx","../src/characterCount.ts","../src/slashMenu/items.ts","../src/slashMenu/SlashMenuPopup.tsx","../src/slashMenu/SlashMenuExtension.ts","../src/shortcuts/types.ts","../src/shortcuts/ShortcutPanel.tsx","../src/markdownFileDrop.ts","../src/pendingAnchor.ts","../src/selectionKind.ts","../src/markdownPaste.ts","../src/components/ChartView.tsx","../src/chart/createChart.ts","../src/components/CodeBlockView.tsx","../node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs","../node_modules/@floating-ui/core/dist/floating-ui.core.mjs","../node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs","../src/blockDrag/actions.ts","../src/blockDrag/listDrop.ts","../src/components/BlockDragHandle.tsx","../src/components/EditorToc.tsx","../src/hooks/useIsMobile.ts","../src/components/MarkdownWysiwygEditor.tsx","../src/components/MarkdownPreview.tsx","../src/importMenu.ts","../src/insertMarkdown.ts","../src/components/ColorPalette.tsx","../src/imageCaption.ts","../src/components/ImageAlignTools.tsx","../src/linkEditing.ts","../src/components/LinkPopover.tsx","../src/tableSelection.ts","../src/components/TableHandles.tsx","../src/components/MathEditorPopover.tsx","../src/components/EditorToolbar.tsx","../src/resetImageSize.ts","../src/commentAnchor/CommentPopover.tsx","../src/anchorRange.ts","../src/markdownRange.ts","../src/normalizeMarkdown.ts","../src/replaceRange.ts","../src/slashMenu/insertSlashMarkdown.ts"],"sourcesContent":["'use client';\n\nimport {\n NodeViewWrapper,\n type NodeViewProps,\n} from '@tiptap/react';\nimport type { RenderCitation } from '../citationTypes';\n\n/**\n * 脚注圆标 React NodeView。\n * 无 `renderCitation` 时渲默认 pill(有 url 则可点);有插槽时把 defaultDom 交给消费方。\n */\nexport function CitationRefView({ node, extension }: NodeViewProps) {\n const index = String(node.attrs.index ?? '');\n const url = (node.attrs.url as string | null) || null;\n const title = (node.attrs.title as string | null) || '';\n const renderCitation = extension.options.renderCitation as\n | RenderCitation\n | undefined;\n\n const pill = (\n <span\n className=\"citation-ref\"\n data-index={index}\n {...(url ? { 'data-url': url } : {})}\n {...(title ? { 'data-title': title } : {})}\n >\n {!renderCitation && url ? (\n <a\n href={url}\n title={title}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {index}\n </a>\n ) : (\n index\n )}\n </span>\n );\n\n const content = renderCitation\n ? renderCitation({\n index,\n attrs: { index, url, title: title || null },\n defaultDom: pill,\n })\n : pill;\n\n return (\n <NodeViewWrapper\n as=\"span\"\n data-citation-ref=\"\"\n style={{ display: 'inline' }}\n >\n {content}\n </NodeViewWrapper>\n );\n}\n","'use client';\n\nimport { ReactNodeViewRenderer } from '@tiptap/react';\nimport { CitationRef, type CitationRefOptions } from './CitationRef';\nimport type { RenderCitation } from './citationTypes';\nimport { CitationRefView } from './components/CitationRefView';\n\nexport type CitationRefViewOptions = CitationRefOptions & {\n renderCitation?: RenderCitation;\n};\n\n/**\n * 客户端增强版 CitationRef:挂 React NodeView,支持 `renderCitation` 插槽。\n * server / SSR 请继续用纯 {@link CitationRef}(无 React)。\n */\nexport function createCitationRef(\n options: { renderCitation?: RenderCitation } = {},\n) {\n return CitationRef.extend<CitationRefViewOptions>({\n addOptions() {\n return {\n HTMLAttributes: {},\n ...this.parent?.(),\n renderCitation: options.renderCitation,\n };\n },\n addNodeView() {\n return ReactNodeViewRenderer(CitationRefView);\n },\n }).configure({\n renderCitation: options.renderCitation,\n });\n}\n","/**\n * 评论锚定客户端插件:\n * - 装饰:block 左缘 gutter 气泡(showGutter)、active 评论的 mark/block 高亮;\n * - 交互:点击 mark / gutter 通过 transaction meta 上报(宿主经 editor.on('transaction')\n * 接收,规避扩展创建时闭包 stale 的问题);\n * - 守卫:粘贴 / 拖拽的 slice 剥离 commentAnchor mark,防止幽灵高亮。\n *\n * 只读态(MarkdownPreview)不注入本插件、不接评论 —— 评论是编辑会话专属。\n */\n\nimport {\n Fragment,\n Slice,\n type MarkType,\n type Node as PMNode,\n} from '@tiptap/pm/model';\nimport { Extension } from '@tiptap/core';\nimport {\n Plugin,\n PluginKey,\n type EditorState,\n} from '@tiptap/pm/state';\nimport {\n Decoration,\n DecorationSet,\n type EditorView,\n} from '@tiptap/pm/view';\nimport type { CommentClickPayload } from './commentTypes';\n\nexport const COMMENT_ACTIVE_META = 'tmr:comment:active';\nexport const COMMENT_CLICK_META = 'tmr:comment:click';\nexport const COMMENT_GUTTER_META = 'tmr:comment:gutter';\n\nexport interface CommentGutterPayload {\n commentIds: string[];\n pos: number;\n}\n\nexport interface CommentAnchorPluginOptions {\n /** 是否在 block 左缘渲染评论 gutter 气泡,默认 true。 */\n showGutter?: boolean;\n /**\n * 编辑器内点 mark 是否参与交互(默认 true:设 active + 上报 click meta)。\n * 设 false 后点击 mark 完全惰性(不设 active、不上报),适合「只靠侧栏单项\n * 驱动滚动定位」的宿主。\n */\n interactive?: boolean;\n}\n\nexport interface CommentAnchorPluginState {\n activeCommentId: string | null;\n}\n\nexport const commentAnchorPluginKey = new PluginKey<CommentAnchorPluginState>(\n 'tmrCommentAnchor',\n);\n\n/** 从 Slice 中剔除 commentAnchor mark(粘贴/拖拽守卫)。 */\nexport function stripCommentAnchorMarks(\n slice: Slice,\n markType: MarkType,\n): Slice {\n const strip = (fragment: Fragment): Fragment => {\n const children: PMNode[] = [];\n fragment.forEach((child) => {\n if (child.isText && child.marks.some((m) => m.type === markType)) {\n children.push(\n child.mark(child.marks.filter((m) => m.type !== markType)),\n );\n } else if (child.content.size > 0) {\n children.push(child.copy(strip(child.content)));\n } else {\n children.push(child);\n }\n });\n return Fragment.fromArray(children);\n };\n return new Slice(strip(slice.content), slice.openStart, slice.openEnd);\n}\n\nfunction sliceHasCommentMarks(slice: Slice, markType: MarkType): boolean {\n let found = false;\n slice.content.descendants((node) => {\n if (node.isText && node.marks.some((m) => m.type === markType)) {\n found = true;\n return false;\n }\n return true;\n });\n return found;\n}\n\n/** 顶层 block 边界。 */\nfunction blockBounds(doc: PMNode): Array<{ start: number; end: number }> {\n const bounds: Array<{ start: number; end: number }> = [];\n doc.forEach((child, offset) => {\n bounds.push({ start: offset, end: offset + child.nodeSize });\n });\n return bounds;\n}\n\n/** commentId → 文本区间(从 doc 内 commentAnchor mark 收集)。 */\nfunction collectCommentRanges(\n doc: PMNode,\n): Map<string, Array<{ from: number; to: number }>> {\n const byId = new Map<string, Array<{ from: number; to: number }>>();\n doc.descendants((node, pos) => {\n if (!node.isText) return true;\n for (const m of node.marks) {\n if (m.type.name !== 'commentAnchor') continue;\n const ids = (m.attrs.commentIds as string[]) ?? [];\n for (const id of ids) {\n const arr = byId.get(id) ?? [];\n arr.push({ from: pos, to: pos + node.nodeSize });\n byId.set(id, arr);\n }\n }\n return true;\n });\n return byId;\n}\n\nfunction rangesIntersectBlock(\n ranges: Array<{ from: number; to: number }>,\n start: number,\n end: number,\n): boolean {\n return ranges.some((r) => r.from < end && r.to > start);\n}\n\nfunction gutterDom(\n commentIds: string[],\n view: EditorView,\n blockPos: number,\n): HTMLElement {\n const el = document.createElement('span');\n el.className = 'tmr-comment-gutter';\n el.setAttribute('data-comment-ids', commentIds.join(' '));\n el.textContent = String(commentIds.length);\n el.title = `${commentIds.length}`;\n // 不抢编辑器焦点/选区\n el.addEventListener('mousedown', (e) => {\n e.preventDefault();\n e.stopPropagation();\n });\n el.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n view.dispatch(\n view.state.tr\n .setMeta(COMMENT_ACTIVE_META, commentIds[0] ?? null)\n .setMeta(COMMENT_GUTTER_META, {\n commentIds,\n pos: blockPos,\n } satisfies CommentGutterPayload),\n );\n });\n return el;\n}\n\nexport function commentAnchorPlugin(\n options: CommentAnchorPluginOptions = {},\n): Plugin<CommentAnchorPluginState> {\n const showGutter = options.showGutter !== false;\n const interactive = options.interactive !== false;\n\n return new Plugin<CommentAnchorPluginState>({\n key: commentAnchorPluginKey,\n state: {\n init: () => ({ activeCommentId: null }),\n apply: (tr, value) => {\n const active = tr.getMeta(COMMENT_ACTIVE_META);\n return active !== undefined\n ? { activeCommentId: active as string | null }\n : value;\n },\n },\n props: {\n decorations(state: EditorState) {\n const byId = collectCommentRanges(state.doc);\n if (byId.size === 0) return DecorationSet.empty;\n\n const decos: Decoration[] = [];\n const bounds = blockBounds(state.doc);\n\n if (showGutter) {\n for (const b of bounds) {\n const idsInBlock: string[] = [];\n for (const [id, ranges] of byId) {\n if (rangesIntersectBlock(ranges, b.start, b.end)) {\n idsInBlock.push(id);\n }\n }\n if (idsInBlock.length > 0) {\n decos.push(\n Decoration.widget(\n b.start,\n (view) => gutterDom(idsInBlock, view, b.start),\n { side: -1, key: `tmr-gutter:${b.start}` },\n ),\n );\n }\n }\n }\n\n const activeId =\n commentAnchorPluginKey.getState(state)?.activeCommentId ?? null;\n if (activeId) {\n const ranges = byId.get(activeId) ?? [];\n for (const r of ranges) {\n decos.push(\n Decoration.inline(r.from, r.to, { class: 'tmr-comment-active' }),\n );\n }\n for (const b of bounds) {\n if (rangesIntersectBlock(ranges, b.start, b.end)) {\n decos.push(\n Decoration.node(b.start, b.end, {\n class: 'tmr-comment-block-active',\n }),\n );\n }\n }\n }\n\n return DecorationSet.create(state.doc, decos);\n },\n\n handleClick(view: EditorView, pos: number, event: MouseEvent) {\n // 惰性模式:编辑器内点击 mark 不产生任何交互(侧栏单项驱动)。\n if (!interactive) return false;\n const target = event.target as Element | null;\n const el = target?.closest?.(\n 'mark.tmr-comment[data-comment-ids]',\n ) as HTMLElement | null;\n if (!el) return false;\n const commentIds = (el.getAttribute('data-comment-ids') ?? '')\n .split(' ')\n .filter(Boolean);\n if (commentIds.length === 0) return false;\n view.dispatch(\n view.state.tr\n .setMeta(COMMENT_ACTIVE_META, commentIds[0] ?? null)\n .setMeta(COMMENT_CLICK_META, {\n commentIds,\n anchorEl: el,\n pos,\n } satisfies CommentClickPayload),\n );\n return true;\n },\n\n // 粘贴 / 拖拽 / 编辑器内拖动的 slice 统一走这里:剥离评论 mark,\n // 防止复制粘贴产生幽灵高亮。正文内容不变,只是丢掉会话级元数据。\n transformPasted(slice: Slice, view: EditorView) {\n const markType = view.state.schema.marks.commentAnchor;\n if (!markType || !sliceHasCommentMarks(slice, markType)) return slice;\n return stripCommentAnchorMarks(slice, markType);\n },\n },\n });\n}\n\n/**\n * Tiptap Extension 包装:把评论锚定 Plugin 挂进 useEditor / Editor 的 extensions。\n * 只读态(MarkdownPreview)不要注入本扩展 —— 评论是编辑会话专属。\n */\nexport function commentAnchorExtension(\n options: CommentAnchorPluginOptions = {},\n) {\n return Extension.create({\n name: 'tmrCommentAnchor',\n addProseMirrorPlugins() {\n return [commentAnchorPlugin(options)];\n },\n });\n}\n","/**\n * 编辑会话内的评论锚定控制器(纯函数,供宿主 / 组件 handle 复用)。\n *\n * 职责:\n * - applyCommentAnchorsToEditor:comments 装载时把 segments 映射成 mark 铺入 doc;\n * - focusComment / nextComment:active 状态 + 滚动 + 选区;\n * - collectCommentIds / commentRangesById:供宿主查询。\n */\n\nimport type { Editor } from '@tiptap/core';\nimport {\n COMMENT_ACTIVE_META,\n commentAnchorPluginKey,\n} from './commentAnchorPlugin';\nimport {\n mapCommentAnchors,\n mergeCommentIntervals,\n type CommentRange,\n} from './commentMapper';\nimport type { CommentRef } from './commentTypes';\n\n/** 先清后铺,幂等;无评论时清空全部 mark。 */\nexport function applyCommentAnchorsToEditor(\n editor: Editor,\n comments: CommentRef[],\n): void {\n // 只读态不接评论(库契约:评论锚定 = 编辑会话专属)。\n if (!editor.isEditable) return;\n const intervals =\n comments.length > 0\n ? mergeCommentIntervals(\n mapCommentAnchors(\n editor.state.doc,\n comments.map((c) => ({ commentId: c.commentId, segments: c.segments })),\n ).map((r) => ({ commentId: r.commentId, ranges: r.ranges })),\n )\n : [];\n const chain = editor.chain().clearCommentAnchors();\n if (intervals.length > 0) chain.applyCommentAnchors(intervals);\n chain.run();\n}\n\n/** 文档顺序去重的 commentId 列表(gutter / 导航顺序)。 */\nexport function collectCommentIds(editor: Editor): string[] {\n const ids: string[] = [];\n editor.state.doc.descendants((node) => {\n if (!node.isText) return true;\n for (const m of node.marks) {\n if (m.type.name !== 'commentAnchor') continue;\n for (const id of (m.attrs.commentIds as string[]) ?? []) {\n if (!ids.includes(id)) ids.push(id);\n }\n }\n return true;\n });\n return ids;\n}\n\nexport function commentRangesById(\n editor: Editor,\n commentId: string,\n): CommentRange[] {\n const ranges: CommentRange[] = [];\n editor.state.doc.descendants((node, pos) => {\n if (!node.isText) return true;\n for (const m of node.marks) {\n if (m.type.name !== 'commentAnchor') continue;\n const ids = (m.attrs.commentIds as string[]) ?? [];\n if (ids.includes(commentId)) {\n ranges.push({ from: pos, to: pos + node.nodeSize });\n }\n }\n return true;\n });\n return ranges;\n}\n\nfunction setActive(editor: Editor, commentId: string | null): void {\n editor.view.dispatch(\n editor.state.tr.setMeta(COMMENT_ACTIVE_META, commentId),\n );\n}\n\nfunction getActive(editor: Editor): string | null {\n return commentAnchorPluginKey.getState(editor.state)?.activeCommentId ?? null;\n}\n\n/**\n * 聚焦某条评论:设置 active(mark/block 高亮)+ 滚动到第一个 mark + 光标落到\n * 区间起点。找不到(outdated)时清空 active 并返回 false。\n */\nexport function focusComment(editor: Editor, commentId: string): boolean {\n const ranges = commentRangesById(editor, commentId);\n if (ranges.length === 0) {\n setActive(editor, null);\n return false;\n }\n setActive(editor, commentId);\n const markEl = editor.view.dom.querySelector<HTMLElement>(\n `mark.tmr-comment[data-comment-ids~=\"${CSS.escape(commentId)}\"]`,\n );\n markEl?.scrollIntoView?.({ block: 'center', behavior: 'smooth' });\n editor.chain().setTextSelection(ranges[0].from).run();\n return true;\n}\n\n/** 按文档顺序在评论间循环跳转(dir: next / prev)。 */\nexport function nextComment(\n editor: Editor,\n dir: 'next' | 'prev' = 'next',\n): string | null {\n const ids = collectCommentIds(editor);\n if (ids.length === 0) return null;\n const active = getActive(editor);\n const current = active ? ids.indexOf(active) : -1;\n const nextIdx =\n dir === 'next'\n ? (current + 1) % ids.length\n : (current - 1 + ids.length) % ids.length;\n const target = ids[nextIdx];\n focusComment(editor, target);\n return target;\n}\n","/**\n * 查找替换的适配层。\n *\n * 匹配 / 高亮 / 替换全部由官方 `@tiptap/extension-find-and-replace` 承担(命令 + storage +\n * decoration),宿主与 AI 侧可以直接用 `editor.commands.setSearchTerm / replaceAll …`。\n * 这个文件只收拾 Tiptap 3.31.3 上的一个坑,以及库内的默认防抖。\n */\n\nimport type { Editor } from '@tiptap/react';\n\n/** 官方扩展在 `extensionManager` 里的注册名(与官方 UI Components 同款判据)。 */\nexport const FIND_EXTENSION_NAME = 'findAndReplace';\n\n/**\n * 输入法组合中的这次按键要不要放行。\n *\n * 中文 / 日文输入法里 Enter 是「提交候选词」、Escape 是「取消候选」,都不是「执行」:不拦的话,\n * 每次确认候选词都会触发一次跳转(替换框则直接替换一处),而按 Esc 想取消候选反而把浮动条关掉。\n * 判据取两路——`isComposing` 覆盖常规路径,`keyCode === 229` 兜住 Safari 在组合收尾时的 keydown\n * (与 slashMenu/SlashMenuExtension.ts 的 isComposing() 同一套;那边还多一路 ProseMirror 的\n * `view.composing`,React 事件里拿不到,调用方传 `event.nativeEvent` 即可)。\n */\nexport function isComposingKeyEvent(\n event: Pick<KeyboardEvent, 'isComposing' | 'keyCode'>,\n): boolean {\n return event.isComposing || event.keyCode === 229;\n}\n\n/**\n * 查找替换扩展是否已注册。\n *\n * 按 `extensionManager.extensions` 里的注册名查,而不是探 `editor.commands.setSearchTerm`\n * 在不在——命令探测分不清「扩展根本没装」和「命令被别的同名扩展覆盖」,也判不出已销毁的实例。\n * 官方 UI Components 的 `isFindAndReplaceAvailable` 用的是同一条判据。\n */\nexport function hasFindReplaceExtension(\n editor: Editor | null | undefined,\n): boolean {\n if (!editor || editor.isDestroyed) return false;\n return (\n editor.extensionManager?.extensions.some(\n (extension) => extension.name === FIND_EXTENSION_NAME,\n ) ?? false\n );\n}\n\n/**\n * 输入防抖(毫秒)。\n *\n * 注意:注册扩展时必须把官方选项 `searchDebounceMs` 设为 **0**。官方防抖走 `setTimeout`,\n * 真正的 dispatch 发生在异步回调里,下面 `runFindCommand` 的守卫就兜不住那次抛错。\n * 防抖改由调用方(浮动条)自己做,语义一样但全程同步。\n */\nexport const FIND_DEBOUNCE_MS = 120;\n\nconst MISMATCH_RE = /Applying a mismatched transaction/;\n\n/**\n * 跑一条官方查找替换命令,兜掉 Tiptap 3.31.3 的一处事务错配。\n *\n * 成因:官方扩展的 `setSearchTerm` 不走 CommandManager 给的 dispatch,而是自己\n * `editor.state.tr` + `view.dispatch`(见扩展 dist 的 `applySearchTerm`)。CommandManager\n * 在命令结束后仍会派发它「命令开始时创建」的那个 tr。若这次派发触发了 TrailingNode 的\n * appendTransaction(**文末是代码块 / 表格 / 图表**时它会补一个空段落,starter-kit 自带),\n * doc 已经前进,那个旧 tr 的 `before` 就对不上当前 doc → `RangeError: Applying a mismatched\n * transaction`。\n *\n * 此时查询其实已经生效(扩展自己那次 dispatch 成功了、尾段落也补上了),只是紧随其后的\n * 第二次派发炸掉。所以捕获这一种错并重试一次即可收敛——重试时文末空段落已在,TrailingNode\n * 不再插手。其它错误照抛,不吞。\n */\nexport function runFindCommand(run: () => void): void {\n try {\n run();\n } catch (error) {\n if (error instanceof RangeError && MISMATCH_RE.test(error.message)) {\n run();\n return;\n }\n throw error;\n }\n}\n","import type { SVGProps } from 'react';\n\n/**\n * 内联 SVG 图标集(0 运行时依赖)。stroke 走 currentColor,尺寸默认 1em 跟随字号,\n * 故颜色/大小随文本与主题自动变化。\n *\n * 通用图标(粗体/斜体/下划线/列表/撤销/查找等,本文件表格图标之前的部分)的 path 取自\n * Lucide,其许可证为 ISC,其中部分图形源自 Feather 的 MIT——完整版权声明见\n * 仓库根目录 THIRD_PARTY_LICENSES.md。表格行列操作图标(Column / Row / TableDelete 系列)为自绘。\n */\nexport interface IconProps extends SVGProps<SVGSVGElement> {\n size?: number | string;\n}\n\nfunction Svg({ size = '1em', children, ...rest }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n {...rest}\n >\n {children}\n </svg>\n );\n}\n\nexport const BoldIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" />\n <path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" />\n </Svg>\n);\n\nexport const ItalicIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"19\" x2=\"10\" y1=\"4\" y2=\"4\" />\n <line x1=\"14\" x2=\"5\" y1=\"20\" y2=\"20\" />\n <line x1=\"15\" x2=\"9\" y1=\"4\" y2=\"20\" />\n </Svg>\n);\n\nexport const UnderlineIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M6 4v6a6 6 0 0 0 12 0V4\" />\n <line x1=\"4\" x2=\"20\" y1=\"20\" y2=\"20\" />\n </Svg>\n);\n\nexport const StrikethroughIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M16 4H9a3 3 0 0 0-2.83 4\" />\n <path d=\"M14 12a4 4 0 0 1 0 8H6\" />\n <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\n </Svg>\n);\n\nexport const CodeIcon = (p: IconProps) => (\n <Svg {...p}>\n <polyline points=\"16 18 22 12 16 6\" />\n <polyline points=\"8 6 2 12 8 18\" />\n </Svg>\n);\n\nexport const LinkIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\" />\n <path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\" />\n </Svg>\n);\n\n/** 橡皮擦:表格菜单的「清空内容」。 */\nexport const EraserIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21\" />\n <path d=\"M22 21H7\" />\n <path d=\"m5 11 9 9\" />\n </Svg>\n);\n\n/** 逆时针回转:表格菜单的「重置单元格样式」。 */\nexport const RotateCcwIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </Svg>\n);\n\n/** 在新窗口打开(链接浮层的「打开」按钮)。 */\nexport const ExternalLinkIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M15 3h6v6\" />\n <path d=\"M10 14 21 3\" />\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n </Svg>\n);\n\nexport const ImageIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\" />\n </Svg>\n);\n\n/**\n * 段落排版图标(对齐四件 / 缩进两件 / 行高):块状 fill 风格,自绘。\n * 细线条(stroke 2)在 14px 下会发虚——这组统一用带圆角的实心块。\n */\nconst BlockSvg = ({ size = '1em', children, ...rest }: IconProps & { children: React.ReactNode }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n {...rest}\n >\n {children}\n </svg>\n);\n\nexport const AlignLeftIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"8.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"19\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const AlignCenterIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"6\" y=\"8.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"6\" y=\"19\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const AlignRightIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"9\" y=\"8.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"9\" y=\"19\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const AlignJustifyIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"8.6\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"19\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const IndentIncreaseIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <path d=\"M3 6.6 8.5 10.8 3 15 Z\" />\n <rect x=\"10.5\" y=\"3.6\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"10.8\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"18\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const IndentDecreaseIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <path d=\"M8.5 6.6 3 10.8 8.5 15 Z\" />\n <rect x=\"10.5\" y=\"3.6\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"10.8\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"18\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\n/** 行高:左侧轴 + 双向块三角,右侧两条块线。 */\nexport const LineHeightIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"1.8\" y=\"4.4\" width=\"2.4\" height=\"15.2\" rx=\"1.2\" />\n <path d=\"M3 2.4 5.4 5.8 H0.6 Z\" />\n <path d=\"M3 21.6 0.6 18.2 H5.4 Z\" />\n <rect x=\"9\" y=\"5.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"9\" y=\"16\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const CaptionsIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"18\" height=\"14\" x=\"3\" y=\"5\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 15h4\" />\n <path d=\"M15 15h2\" />\n <path d=\"M7 11h2\" />\n <path d=\"M13 11h4\" />\n </Svg>\n);\n\nexport const ImportIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M12 3v12\" />\n <path d=\"m8 11 4 4 4-4\" />\n <path d=\"M8 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4\" />\n </Svg>\n);\n\nexport const ListUnorderedIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"8\" x2=\"21\" y1=\"6\" y2=\"6\" />\n <line x1=\"8\" x2=\"21\" y1=\"12\" y2=\"12\" />\n <line x1=\"8\" x2=\"21\" y1=\"18\" y2=\"18\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"6\" y2=\"6\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"12\" y2=\"12\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"18\" y2=\"18\" />\n </Svg>\n);\n\nexport const ListOrderedIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"10\" x2=\"21\" y1=\"6\" y2=\"6\" />\n <line x1=\"10\" x2=\"21\" y1=\"12\" y2=\"12\" />\n <line x1=\"10\" x2=\"21\" y1=\"18\" y2=\"18\" />\n <path d=\"M4 6h1v4\" />\n <path d=\"M4 10h2\" />\n <path d=\"M6 18H4c0-1 2-2 2-3s-1-1.5-2-1\" />\n </Svg>\n);\n\n/** 折叠块。自绘,不是 Lucide。 */\nexport const DetailsIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m9 7 4 5-4 5\" />\n <path d=\"M15 7h5\" />\n <path d=\"M15 12h5\" />\n <path d=\"M15 17h5\" />\n </Svg>\n);\n\nexport const ListChecksIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m3 17 2 2 4-4\" />\n <path d=\"m3 7 2 2 4-4\" />\n <path d=\"M13 6h8\" />\n <path d=\"M13 12h8\" />\n <path d=\"M13 18h8\" />\n </Svg>\n);\n\nexport const UndoIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M9 14 4 9l5-5\" />\n <path d=\"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5 5.5 5.5 0 0 1-5.5 5.5H11\" />\n </Svg>\n);\n\nexport const RedoIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m15 14 5-5-5-5\" />\n <path d=\"M20 9H9.5A5.5 5.5 0 0 0 4 14.5 5.5 5.5 0 0 0 9.5 20H13\" />\n </Svg>\n);\n\nexport const ChevronDownIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m6 9 6 6 6-6\" />\n </Svg>\n);\n\nexport const CheckIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M20 6 9 17l-5-5\" />\n </Svg>\n);\n\nexport const TrashIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" />\n <path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\" />\n <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\" />\n </Svg>\n);\n\nexport const LockIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </Svg>\n);\n\nexport const MenuIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\n <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" />\n <line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n </Svg>\n);\n\nexport const SearchIcon = (p: IconProps) => (\n <Svg {...p}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </Svg>\n);\n\nexport const XIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </Svg>\n);\n\nexport const ChevronUpIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m18 15-6-6-6 6\" />\n </Svg>\n);\n\nexport const ReplaceIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M14 4a1 1 0 0 1 1-1\" />\n <path d=\"M15 10a1 1 0 0 1-1-1\" />\n <path d=\"M21 4a1 1 0 0 0-1-1\" />\n <path d=\"M21 9a1 1 0 0 1-1 1\" />\n <path d=\"m3 7 3 3 3-3\" />\n <path d=\"M6 10V5a2 2 0 0 1 2-2h2\" />\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\" />\n </Svg>\n);\n\nexport const ReplaceAllIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M14 14a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1\" />\n <path d=\"M14 4a1 1 0 0 1 1-1\" />\n <path d=\"M15 10a1 1 0 0 1-1-1\" />\n <path d=\"M19 14a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1\" />\n <path d=\"M21 4a1 1 0 0 0-1-1\" />\n <path d=\"M21 9a1 1 0 0 1-1 1\" />\n <path d=\"m3 7 3 3 3-3\" />\n <path d=\"M6 10V5a2 2 0 0 1 2-2h2\" />\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\" />\n </Svg>\n);\n\n/* ── 表格行列操作图标(自绘:表格轮廓 + 目标边高亮 + 加号/叉号)── */\n\nexport const ColumnBeforeIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"10\" y=\"4\" width=\"11\" height=\"16\" rx=\"1\" />\n <path d=\"M14 4v16\" />\n <path d=\"M5 9v6\" />\n <path d=\"M2 12h6\" />\n </Svg>\n);\n\nexport const ColumnAfterIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"3\" y=\"4\" width=\"11\" height=\"16\" rx=\"1\" />\n <path d=\"M10 4v16\" />\n <path d=\"M19 9v6\" />\n <path d=\"M16 12h6\" />\n </Svg>\n);\n\nexport const ColumnDeleteIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"1\" />\n <path d=\"M12 4v16\" />\n <path d=\"m8 9 8 6\" />\n <path d=\"m16 9-8 6\" />\n </Svg>\n);\n\nexport const RowBeforeIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"10\" width=\"16\" height=\"11\" rx=\"1\" />\n <path d=\"M4 14h16\" />\n <path d=\"M9 5h6\" />\n <path d=\"M12 2v6\" />\n </Svg>\n);\n\nexport const RowAfterIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"3\" width=\"16\" height=\"11\" rx=\"1\" />\n <path d=\"M4 10h16\" />\n <path d=\"M9 19h6\" />\n <path d=\"M12 16v6\" />\n </Svg>\n);\n\nexport const RowDeleteIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"1\" />\n <path d=\"M4 12h16\" />\n <path d=\"m9 8 6 8\" />\n <path d=\"m15 8-6 8\" />\n </Svg>\n);\n\nexport const TableDeleteIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"1\" />\n <path d=\"M3 9h18\" />\n <path d=\"M9 5v14\" />\n <path d=\"M15 5v14\" />\n <path d=\"m7 2 10 20\" />\n </Svg>\n);\n\nexport const CopyIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </Svg>\n);\n\nexport const ChevronsUpDownIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m7 15 5 5 5-5\" />\n <path d=\"m7 9 5-5 5 5\" />\n </Svg>\n);\n\n/** 六点握把:块拖拽手柄。 */\nexport const GripIcon = (p: IconProps) => (\n <Svg {...p} fill=\"currentColor\" strokeWidth={0}>\n <circle cx=\"9\" cy=\"6\" r=\"1.25\" />\n <circle cx=\"15\" cy=\"6\" r=\"1.25\" />\n <circle cx=\"9\" cy=\"12\" r=\"1.25\" />\n <circle cx=\"15\" cy=\"12\" r=\"1.25\" />\n <circle cx=\"9\" cy=\"18\" r=\"1.25\" />\n <circle cx=\"15\" cy=\"18\" r=\"1.25\" />\n </Svg>\n);\n\nexport const DownloadIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\" />\n </Svg>\n);\n\nexport const KeyboardIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"M6 8h.01\" />\n <path d=\"M10 8h.01\" />\n <path d=\"M14 8h.01\" />\n <path d=\"M18 8h.01\" />\n <path d=\"M6 12h.01\" />\n <path d=\"M10 12h.01\" />\n <path d=\"M14 12h.01\" />\n <path d=\"M18 12h.01\" />\n <path d=\"M7 16h10\" />\n </Svg>\n);","/**\n * UI 文案类型与英文默认。语言包在 `src/i18n`:宿主传 `locale`,不传单条文案。\n */\n\nexport interface ToolbarLabels {\n undo: string;\n redo: string;\n style: string;\n normalText: string;\n /** 传入层级,返回如 \"Heading 2\"。 */\n headingLabel: (level: number) => string;\n fontSize: string;\n fontSizeDefault: string;\n bold: string;\n italic: string;\n underline: string;\n strike: string;\n code: string;\n textColor: string;\n highlight: string;\n colorNone: string;\n colorTheme: string;\n scriptMenu: string;\n scriptNormal: string;\n superscript: string;\n subscript: string;\n link: string;\n /** 段落对齐下拉:触发器 title / aria-label。 */\n align: string;\n alignLeft: string;\n alignCenter: string;\n alignRight: string;\n alignJustify: string;\n /** 缩进下拉:触发器 + 增加 / 减少两项。 */\n indent: string;\n indentIncrease: string;\n indentDecrease: string;\n /** 行高下拉:触发器 + 「默认」项(其余按数字直显)。 */\n lineHeight: string;\n lineHeightDefault: string;\n /**\n * 链接浮层输入框的占位与无障碍名(原 `window.prompt` 的提示语,现在落到输入框上)。\n */\n linkPrompt: string;\n /** 链接浮层「应用」按钮的 title / aria-label。 */\n linkApply: string;\n /** 链接浮层「在新窗口打开」按钮的 title / aria-label。 */\n linkOpen: string;\n /** 链接浮层「移除链接」按钮的 title / aria-label。 */\n linkRemove: string;\n /** 链接浮层:地址被编辑器拒绝时的提示(落在输入框 title 上)。 */\n linkInvalid: string;\n image: string;\n imageUploadFailed: string;\n /** 图片对齐工具条(悬停图片时浮现)的 aria-label。 */\n imageAlign: string;\n /** 左对齐按钮的 title / aria-label。 */\n imageAlignLeft: string;\n /** 居中对齐按钮的 title / aria-label。 */\n imageAlignCenter: string;\n /** 右对齐按钮的 title / aria-label。 */\n imageAlignRight: string;\n /** 图片描述(图注)按钮的 title / aria-label。 */\n imageCaption: string;\n /** 描述输入框的占位与无障碍名。 */\n imageCaptionPlaceholder: string;\n /** Markdown 文件导入失败(与图片上传共用 onError 回调时,便于宿主区分文案)。 */\n importMarkdownFailed: string;\n /** 主栏导入下拉触发器上的短文案(对齐 style / fontSize)。 */\n importDocument: string;\n /** 导入触发器 title / aria-label。 */\n importDocumentHint: string;\n /** 未传 importMenuItems、但传了 onImportDocument + importAccept 时的第二项。 */\n importDocumentOther: string;\n /** 导入进行中的按钮 title。 */\n importDocumentBusy: string;\n /** 光标占位:上传阶段,传入 0–100。 */\n importDocumentUploading: (percent: number) => string;\n /** 光标占位:转换阶段,不要百分比。 */\n importDocumentConverting: string;\n /** 非 Markdown 文件导入失败(onError 的 source === 'import')。 */\n importDocumentFailed: string;\n blockquote: string;\n /** 折叠块(官方 Details)。工具栏按钮 title / aria-label。 */\n details: string;\n bulletList: string;\n orderedList: string;\n /** 有序列表旁的编号格式下拉:触发器 title / aria-label。 */\n numberingFormat: string;\n taskList: string;\n more: string;\n /**\n * 工具栏的搜索入口(放大镜按钮,落在 More 之后):title 与 aria-label。\n * 只在宿主传了 `onSearch` 时才渲染——它只是入口,条子本身归编辑器\n * (`<MarkdownWysiwygEditor>`),点击后由宿主调 `handle.openFind()`。\n */\n search: string;\n codeBlock: string;\n hr: string;\n /** 导入下拉里的 Markdown 项。 */\n importMarkdown: string;\n tableInsert: string;\n /** 行手柄的 title / aria-label(鼠标悬停单元格时出现在行的左缘)。 */\n tableRowMenu: string;\n /** 列手柄的 title / aria-label(悬停时出现在表格上方、对齐该列)。 */\n tableColumnMenu: string;\n /** 表格菜单:清空选中行 / 列 / 单元格矩形里的内容(保留单元格与表头)。 */\n tableClearContent: string;\n /** 表格菜单:把选中单元格的样式属性复位(列宽、对齐等;不动合并结构)。 */\n tableResetCellStyles: string;\n /** 表格底边 `+` 手柄(末尾追加一行)的 title / aria-label。 */\n tableAppendRow: string;\n /** 表格右边 `+` 手柄(末尾追加一列)的 title / aria-label。 */\n tableAppendColumn: string;\n /** 网格选择器底部尺寸文案,如 \"3 × 4\"。参数顺序:列、行。 */\n tableSizeSelected: (cols: number, rows: number) => string;\n tableAddColumnBefore: string;\n tableAddColumnAfter: string;\n tableDeleteColumn: string;\n tableAddRowBefore: string;\n tableAddRowAfter: string;\n tableDeleteRow: string;\n /** 多格选区:在左侧插入 N 列。 */\n tableAddColumnBeforeN: (n: number) => string;\n /** 多格选区:在右侧插入 N 列。 */\n tableAddColumnAfterN: (n: number) => string;\n /** 多格选区:删除 N 列。 */\n tableDeleteColumnN: (n: number) => string;\n /** 多格选区:在上方插入 N 行。 */\n tableAddRowBeforeN: (n: number) => string;\n /** 多格选区:在下方插入 N 行。 */\n tableAddRowAfterN: (n: number) => string;\n /** 多格选区:删除 N 行。 */\n tableDeleteRowN: (n: number) => string;\n tableDeleteTable: string;\n inlineMath: string;\n blockMath: string;\n mathPlaceholder: string;\n mathDone: string;\n mathCancel: string;\n mathNewInline: string;\n mathNewBlock: string;\n}\n\n/** 斜杠菜单(键入 / 唤起的块级插入弹窗)文案。 */\nexport interface SlashMenuLabels {\n /** 弹窗 aria-label。 */\n menuLabel: string;\n /** 分组标题:文本块。 */\n groupText: string;\n /** 分组标题:列表。 */\n groupList: string;\n /** 分组标题:结构 / 高级块。 */\n groupAdvanced: string;\n normalText: string;\n headingLabel: (level: number) => string;\n bulletList: string;\n orderedList: string;\n taskList: string;\n blockquote: string;\n details: string;\n codeBlock: string;\n divider: string;\n table: string;\n inlineMath: string;\n blockMath: string;\n}\n\nexport interface ChartLabels {\n /** Tab label when config has no title. */\n tabLabel: (chartType: string, index: number) => string;\n done: string;\n cancel: string;\n configLabel: string;\n tableLabel: string;\n /** Modal dialog title. */\n editTitle: string;\n}\n\nexport const defaultChartLabels: ChartLabels = {\n tabLabel: (chartType, index) => `${chartType} ${index + 1}`,\n done: 'Done',\n cancel: 'Cancel',\n configLabel: 'Chart config (JSON)',\n tableLabel: 'Data table (Markdown)',\n editTitle: 'Edit chart',\n};\n\nexport const defaultToolbarLabels: ToolbarLabels = {\n undo: 'Undo',\n redo: 'Redo',\n style: 'Style',\n normalText: 'Normal text',\n headingLabel: (level) => `Heading ${level}`,\n fontSize: 'Font size',\n fontSizeDefault: 'Default',\n bold: 'Bold',\n italic: 'Italic',\n underline: 'Underline',\n strike: 'Strikethrough',\n code: 'Inline code',\n textColor: 'Text color',\n highlight: 'Highlight',\n colorNone: 'None',\n colorTheme: 'THEME',\n scriptMenu: 'Superscript / Subscript',\n scriptNormal: 'Normal',\n superscript: 'Superscript',\n subscript: 'Subscript',\n link: 'Link',\n align: 'Alignment',\n alignLeft: 'Align left',\n alignCenter: 'Align center',\n alignRight: 'Align right',\n alignJustify: 'Justify',\n indent: 'Indent',\n indentIncrease: 'Increase indent',\n indentDecrease: 'Decrease indent',\n lineHeight: 'Line height',\n lineHeightDefault: 'Default',\n linkPrompt: 'Enter URL',\n linkApply: 'Apply link',\n linkOpen: 'Open in new window',\n linkRemove: 'Remove link',\n linkInvalid: 'The editor rejected this address',\n image: 'Image',\n imageUploadFailed: 'Image upload failed',\n imageAlign: 'Image alignment',\n imageAlignLeft: 'Align left',\n imageAlignCenter: 'Align center',\n imageAlignRight: 'Align right',\n imageCaption: 'Caption',\n imageCaptionPlaceholder: 'Add a caption…',\n importMarkdownFailed: 'Markdown import failed',\n importDocument: 'Import',\n importDocumentHint: 'Import a file',\n importDocumentOther: 'Document',\n importDocumentBusy: 'Importing…',\n importDocumentUploading: (percent) => `Uploading ${percent}%`,\n importDocumentConverting: 'Converting…',\n importDocumentFailed: 'Import failed',\n blockquote: 'Blockquote',\n details: 'Toggle',\n bulletList: 'Bullet list',\n orderedList: 'Ordered list',\n numberingFormat: 'Numbering format',\n taskList: 'Task list',\n more: 'More',\n search: 'Find & replace',\n codeBlock: 'Code block',\n hr: 'Divider',\n importMarkdown: 'Markdown',\n tableInsert: 'Insert table',\n tableRowMenu: 'Row actions',\n tableColumnMenu: 'Column actions',\n tableClearContent: 'Clear content',\n tableResetCellStyles: 'Reset cell styles',\n tableAppendRow: 'Add row',\n tableAppendColumn: 'Add column',\n tableSizeSelected: (cols, rows) => `${cols} × ${rows}`,\n tableAddColumnBefore: 'Add column before',\n tableAddColumnAfter: 'Add column after',\n tableDeleteColumn: 'Delete column',\n tableAddRowBefore: 'Add row before',\n tableAddRowAfter: 'Add row after',\n tableDeleteRow: 'Delete row',\n tableAddColumnBeforeN: (n) => `Add ${n} columns before`,\n tableAddColumnAfterN: (n) => `Add ${n} columns after`,\n tableDeleteColumnN: (n) => `Delete ${n} columns`,\n tableAddRowBeforeN: (n) => `Add ${n} rows before`,\n tableAddRowAfterN: (n) => `Add ${n} rows after`,\n tableDeleteRowN: (n) => `Delete ${n} rows`,\n tableDeleteTable: 'Delete table',\n inlineMath: 'Inline equation',\n blockMath: 'Block equation',\n mathPlaceholder: 'E = mc^2',\n mathDone: 'Done',\n mathCancel: 'Cancel',\n mathNewInline: 'New equation',\n mathNewBlock: 'Add a TeX equation',\n};\n\n/**\n * 块拖拽手柄和它的菜单。悬停块时出现在左侧,拖动重排,点击出菜单。\n */\nexport interface BlockDragLabels {\n /** 拖拽手柄的 title / aria-label。 */\n dragHandle: string;\n /** 菜单 aria-label。 */\n menuLabel: string;\n /** 「转成」分组标题。 */\n turnInto: string;\n paragraph: string;\n headingLabel: (level: number) => string;\n bulletList: string;\n orderedList: string;\n taskList: string;\n blockquote: string;\n codeBlock: string;\n copy: string;\n duplicate: string;\n resetFormatting: string;\n downloadImage: string;\n delete: string;\n}\n\nexport const defaultBlockDragLabels: BlockDragLabels = {\n dragHandle: 'Drag to move',\n menuLabel: 'Block actions',\n turnInto: 'Turn into',\n paragraph: 'Normal text',\n headingLabel: (level) => `Heading ${level}`,\n bulletList: 'Bullet list',\n orderedList: 'Ordered list',\n taskList: 'Task list',\n blockquote: 'Blockquote',\n codeBlock: 'Code block',\n copy: 'Copy',\n duplicate: 'Duplicate',\n resetFormatting: 'Reset formatting',\n downloadImage: 'Download image',\n delete: 'Delete',\n};\n\nexport const defaultSlashMenuLabels: SlashMenuLabels = {\n menuLabel: 'Insert block',\n groupText: 'Text',\n groupList: 'Lists',\n groupAdvanced: 'Advanced',\n normalText: 'Normal text',\n headingLabel: (level) => `Heading ${level}`,\n bulletList: 'Bullet list',\n orderedList: 'Ordered list',\n taskList: 'Task list',\n blockquote: 'Blockquote',\n details: 'Toggle',\n codeBlock: 'Code block',\n divider: 'Divider',\n table: 'Table',\n inlineMath: 'Inline equation',\n blockMath: 'Block equation',\n};\n\n/** 快捷键抽屉面板(键位 + Markdown 触发写法对照)文案。 */\nexport interface ShortcutLabels {\n /** 抽屉标题。 */\n panelTitle: string;\n /** 关闭按钮 aria / title。 */\n close: string;\n /** 三列表头:格式 / 快捷键 / Markdown。 */\n colFormat: string;\n colShortcut: string;\n colMarkdown: string;\n /** 分组标题。 */\n formatGroup: string;\n insertGroup: string;\n editGroup: string;\n bold: string;\n italic: string;\n strike: string;\n underline: string;\n inlineCode: string;\n highlight: string;\n headingLabel: (level: number) => string;\n paragraph: string;\n blockquote: string;\n bulletList: string;\n orderedList: string;\n taskList: string;\n codeBlock: string;\n divider: string;\n undo: string;\n redo: string;\n hardBreak: string;\n findReplace: string;\n /** 段落对齐四条(官方 TextAlign 的默认键位)。 */\n alignLeft: string;\n alignCenter: string;\n alignRight: string;\n alignJustify: string;\n /** 图片上传占位块(Mod+Shift+I)。 */\n imageUpload: string;\n /** 智能标点:直引号收成弯引号。 */\n smartQuote: string;\n /** 智能标点:`...` 收成省略号。 */\n ellipsis: string;\n /** 智能标点:`--` 收成破折号。 */\n emDash: string;\n /** 表情:`:smile:` 这类短码。 */\n emoji: string;\n}\n\nexport const defaultShortcutLabels: ShortcutLabels = {\n panelTitle: 'Shortcuts',\n close: 'Close',\n colFormat: 'Format',\n colShortcut: 'Shortcut',\n colMarkdown: 'Markdown',\n formatGroup: 'Formatting',\n insertGroup: 'Insert',\n editGroup: 'Editing',\n bold: 'Bold',\n italic: 'Italic',\n strike: 'Strikethrough',\n underline: 'Underline',\n inlineCode: 'Inline code',\n highlight: 'Highlight',\n headingLabel: (level) => `Heading ${level}`,\n paragraph: 'Normal text',\n blockquote: 'Blockquote',\n bulletList: 'Bullet list',\n orderedList: 'Ordered list',\n taskList: 'Task list',\n codeBlock: 'Code block',\n divider: 'Divider',\n undo: 'Undo',\n redo: 'Redo',\n hardBreak: 'Hard break',\n findReplace: 'Find & replace',\n alignLeft: 'Align left',\n alignCenter: 'Align center',\n alignRight: 'Align right',\n alignJustify: 'Justify',\n imageUpload: 'Upload image',\n smartQuote: 'Smart quotes',\n ellipsis: 'Ellipsis',\n emDash: 'Em dash',\n emoji: 'Emoji',\n};\n\n/** 全文统计(官方 CharacterCount)文案。 */\nexport interface CharacterCountLabels {\n /** 统计条的无障碍名。 */\n label: string;\n /** 字符数,如 \"128 characters\"。 */\n characters: (count: number) => string;\n /** 词数,如 \"24 words\"。 */\n words: (count: number) => string;\n}\n\nexport const defaultCharacterCountLabels: CharacterCountLabels = {\n label: 'Document statistics',\n characters: (count) =>\n `${count} ${count === 1 ? 'character' : 'characters'}`,\n words: (count) => `${count} ${count === 1 ? 'word' : 'words'}`,\n};\n\n/** 表情建议列表(键入 `:` 唤起)文案。 */\nexport interface EmojiSuggestionLabels {\n /** 弹窗 aria-label。 */\n menuLabel: string;\n}\n\nexport const defaultEmojiSuggestionLabels: EmojiSuggestionLabels = {\n menuLabel: 'Emoji',\n};\n\n/** 链接编辑浮层(`<LinkPopover>`)文案。 */\nexport interface LinkPopoverLabels {\n /** 输入框占位与无障碍名,也是浮层的 aria-label。 */\n field: string;\n apply: string;\n open: string;\n remove: string;\n /** 地址被编辑器拒绝时的提示(落在输入框 title 上)。 */\n invalid: string;\n}\n\nexport const defaultLinkPopoverLabels: LinkPopoverLabels = {\n field: 'Enter URL',\n apply: 'Apply link',\n open: 'Open in new window',\n remove: 'Remove link',\n invalid: 'The editor rejected this address',\n};\n\nexport interface ColorPaletteLabels {\n none: string;\n theme: string;\n}\n\nexport const defaultColorPaletteLabels: ColorPaletteLabels = {\n none: 'None',\n theme: 'THEME',\n};\n\nexport interface TocLabels {\n title: string;\n expand: string;\n collapse: string;\n}\n\nexport const defaultTocLabels: TocLabels = {\n title: 'Contents',\n expand: 'Expand',\n collapse: 'Collapse',\n};\n\nexport interface CodeBlockLabels {\n autoDetect: string;\n delete: string;\n /** 只读态:复制代码内容。 */\n copy: string;\n /** 只读态:展开超出限高的代码块。 */\n expand: string;\n /** 只读态:收起已展开的代码块。 */\n collapse: string;\n /** 语言选择器的 aria-label。 */\n language: string;\n}\n\nexport const defaultCodeBlockLabels: CodeBlockLabels = {\n autoDetect: 'Auto-detect',\n delete: 'Delete code block',\n copy: 'Copy code',\n expand: 'Expand code',\n collapse: 'Collapse code',\n language: 'Code language',\n};\n\nexport interface CommentLabels {\n open: string;\n resolved: string;\n dismissed: string;\n outdated: string;\n ambiguous: string;\n partial: string;\n gutterTitle: string;\n}\n\nexport const defaultCommentLabels: CommentLabels = {\n open: 'Open',\n resolved: 'Resolved',\n dismissed: 'Dismissed',\n outdated: 'Outdated',\n ambiguous: 'Ambiguous',\n partial: 'Partially matched',\n gutterTitle: 'Comment',\n};\n\nexport interface FindLabels {\n /** 查找输入框的占位与无障碍名,也用作浮动条的 aria-label。 */\n find: string;\n /** 替换输入框的占位与无障碍名。 */\n replace: string;\n next: string;\n previous: string;\n replaceOne: string;\n replaceAll: string;\n close: string;\n caseSensitive: string;\n wholeWord: string;\n useRegex: string;\n /** 计数文案。无结果时 current 传 0;有结果但还没定位到某一条(currentIndex 为 null)时也是 0。 */\n counter: (current: number, total: number) => string;\n /** 正则模式下模式非法(RE2 不支持 lookaround / backreference 等,见官方扩展)。 */\n invalidRegex: string;\n /** 只读态提示:可查不可换。 */\n readOnly: string;\n}\n\nexport const defaultFindLabels: FindLabels = {\n find: 'Find',\n replace: 'Replace with',\n next: 'Next match',\n previous: 'Previous match',\n replaceOne: 'Replace',\n replaceAll: 'Replace all',\n close: 'Close',\n caseSensitive: 'Match case',\n wholeWord: 'Whole word',\n useRegex: 'Use regular expression',\n counter: (current, total) => `${current} / ${total}`,\n invalidRegex: 'Invalid pattern',\n readOnly: 'Read-only',\n};\n\n/** 图片上传占位块(`<MarkdownWysiwygEditor imageUpload={{…}}>`)文案。 */\nexport interface ImageUploadLabels {\n /** 空态主文案前半(下划线强调,对齐官方的 \"Click to upload\"),如 `Click to upload`。 */\n dropzoneClick: string;\n /** 空态主文案后半,如 ` or drag and drop`(中文可写成「,或拖拽到此处」)。 */\n dropzoneRest: string;\n /** 空态副文案:数量与单文件体积上限;`maxSizeMb` 为 null 表示未设上限。 */\n dropzoneLimits: (limit: number, maxSizeMb: number | null) => string;\n /** 多文件排队时的标题,参数为文件数。 */\n uploadingCount: (count: number) => string;\n /** 清空全部排队文件的按钮。 */\n clearAll: string;\n /** 单个文件行的移除按钮(title / aria-label)。 */\n remove: string;\n /** 文件行上传失败时的状态文字(替掉体积位置)。 */\n failed: string;\n /** 上传成功后写入图片 alt / title 的兜底(文件名去扩展名后为空时)。 */\n fallbackAlt: string;\n}\n\nexport const defaultImageUploadLabels: ImageUploadLabels = {\n dropzoneClick: 'Click to upload',\n dropzoneRest: ' or drag and drop',\n dropzoneLimits: (limit, maxSizeMb) => {\n const count = `Maximum ${limit} file${limit === 1 ? '' : 's'}`;\n return maxSizeMb == null ? `${count}.` : `${count}, ${maxSizeMb}MB each.`;\n },\n uploadingCount: (count) => `Uploading ${count} files`,\n clearAll: 'Clear all',\n remove: 'Remove',\n failed: 'Upload failed',\n fallbackAlt: 'image',\n};\n","import type { LocaleBundle } from './types';\n\n/** 内置中文。键必须齐全,宿主不能再补单条。 */\nexport const zh: LocaleBundle = {\n toolbar: {\n undo: '撤销',\n redo: '重做',\n style: '样式',\n normalText: '正文',\n headingLabel: (level) => `标题 ${level}`,\n fontSize: '字号',\n fontSizeDefault: '默认',\n bold: '加粗',\n italic: '斜体',\n underline: '下划线',\n strike: '删除线',\n code: '行内代码',\n textColor: '文字颜色',\n highlight: '高亮',\n colorNone: '无',\n colorTheme: '主题色',\n scriptMenu: '上标 / 下标',\n scriptNormal: '常规',\n superscript: '上标',\n subscript: '下标',\n link: '链接',\n align: '对齐',\n alignLeft: '左对齐',\n alignCenter: '居中',\n alignRight: '右对齐',\n alignJustify: '两端对齐',\n indent: '缩进',\n indentIncrease: '增加缩进',\n indentDecrease: '减少缩进',\n lineHeight: '行高',\n lineHeightDefault: '默认',\n linkPrompt: '输入链接地址',\n linkApply: '应用链接',\n linkOpen: '在新窗口打开',\n linkRemove: '移除链接',\n linkInvalid: '编辑器不接受这个地址',\n image: '图片',\n imageUploadFailed: '图片上传失败',\n imageAlign: '图片对齐',\n imageAlignLeft: '左对齐',\n imageAlignCenter: '居中',\n imageAlignRight: '右对齐',\n imageCaption: '图片描述',\n imageCaptionPlaceholder: '添加描述…',\n importMarkdownFailed: 'Markdown 导入失败',\n importDocument: '导入',\n importDocumentHint: '导入文件',\n importDocumentOther: '文档',\n importDocumentBusy: '正在导入…',\n importDocumentUploading: (percent) => `上传中 ${percent}%`,\n importDocumentConverting: '正在转换…',\n importDocumentFailed: '导入失败',\n blockquote: '引用',\n details: '折叠块',\n bulletList: '无序列表',\n orderedList: '有序列表',\n numberingFormat: '编号格式',\n taskList: '任务列表',\n more: '更多',\n search: '查找替换',\n codeBlock: '代码块',\n hr: '分隔线',\n importMarkdown: 'Markdown',\n tableInsert: '插入表格',\n tableRowMenu: '行操作',\n tableColumnMenu: '列操作',\n tableClearContent: '清空内容',\n tableResetCellStyles: '重置单元格样式',\n tableAppendRow: '添加行',\n tableAppendColumn: '添加列',\n tableSizeSelected: (cols, rows) => `${cols} × ${rows}`,\n tableAddColumnBefore: '在左侧插入列',\n tableAddColumnAfter: '在右侧插入列',\n tableDeleteColumn: '删除列',\n tableAddRowBefore: '在上方插入行',\n tableAddRowAfter: '在下方插入行',\n tableDeleteRow: '删除行',\n tableAddColumnBeforeN: (n) => `在左侧插入 ${n} 列`,\n tableAddColumnAfterN: (n) => `在右侧插入 ${n} 列`,\n tableDeleteColumnN: (n) => `删除 ${n} 列`,\n tableAddRowBeforeN: (n) => `在上方插入 ${n} 行`,\n tableAddRowAfterN: (n) => `在下方插入 ${n} 行`,\n tableDeleteRowN: (n) => `删除 ${n} 行`,\n tableDeleteTable: '删除表格',\n inlineMath: '行内公式',\n blockMath: '块级公式',\n mathPlaceholder: 'E = mc^2',\n mathDone: '完成',\n mathCancel: '取消',\n mathNewInline: '新建公式',\n mathNewBlock: '插入 TeX 公式',\n },\n slashMenu: {\n menuLabel: '插入块',\n groupText: '文本',\n groupList: '列表',\n groupAdvanced: '高级',\n normalText: '正文',\n headingLabel: (level) => `标题 ${level}`,\n bulletList: '无序列表',\n orderedList: '有序列表',\n taskList: '任务列表',\n blockquote: '引用',\n details: '折叠块',\n codeBlock: '代码块',\n divider: '分隔线',\n table: '表格',\n inlineMath: '行内公式',\n blockMath: '块级公式',\n },\n chart: {\n tabLabel: (chartType, index) => `${chartType} ${index + 1}`,\n done: '完成',\n cancel: '取消',\n configLabel: '图表配置(JSON)',\n tableLabel: '数据表(Markdown)',\n editTitle: '编辑图表',\n },\n blockDrag: {\n dragHandle: '拖动以移动',\n menuLabel: '块操作',\n turnInto: '转换成',\n paragraph: '正文',\n headingLabel: (level) => `标题 ${level}`,\n bulletList: '无序列表',\n orderedList: '有序列表',\n taskList: '任务列表',\n blockquote: '引用',\n codeBlock: '代码块',\n copy: '复制',\n duplicate: '再制一份',\n resetFormatting: '清除格式',\n downloadImage: '下载图片',\n delete: '删除',\n },\n shortcut: {\n panelTitle: '快捷键',\n close: '关闭',\n colFormat: '格式',\n colShortcut: '快捷键',\n colMarkdown: 'Markdown',\n formatGroup: '格式',\n insertGroup: '插入',\n editGroup: '编辑',\n bold: '加粗',\n italic: '斜体',\n strike: '删除线',\n underline: '下划线',\n inlineCode: '行内代码',\n highlight: '高亮',\n headingLabel: (level) => `标题 ${level}`,\n paragraph: '正文',\n blockquote: '引用',\n bulletList: '无序列表',\n orderedList: '有序列表',\n taskList: '任务列表',\n codeBlock: '代码块',\n divider: '分隔线',\n undo: '撤销',\n redo: '重做',\n hardBreak: '硬换行',\n findReplace: '查找替换',\n alignLeft: '左对齐',\n alignCenter: '居中',\n alignRight: '右对齐',\n alignJustify: '两端对齐',\n imageUpload: '上传图片',\n smartQuote: '智能引号',\n ellipsis: '省略号',\n emDash: '破折号',\n emoji: '表情',\n },\n characterCount: {\n label: '字数统计',\n characters: (count) => `${count} 字符`,\n words: (count) => `${count} 词`,\n },\n emoji: {\n menuLabel: '表情',\n },\n link: {\n field: '输入链接地址',\n apply: '应用链接',\n open: '在新窗口打开',\n remove: '移除链接',\n invalid: '编辑器不接受这个地址',\n },\n palette: {\n none: '无',\n theme: '主题色',\n },\n toc: {\n title: '目录',\n expand: '展开',\n collapse: '收起',\n },\n codeBlock: {\n autoDetect: '自动识别',\n delete: '删除代码块',\n copy: '复制代码',\n expand: '展开代码',\n collapse: '收起代码',\n language: '代码语言',\n },\n find: {\n find: '查找',\n replace: '替换为',\n next: '下一处',\n previous: '上一处',\n replaceOne: '替换',\n replaceAll: '全部替换',\n close: '关闭',\n caseSensitive: '区分大小写',\n wholeWord: '全词匹配',\n useRegex: '正则表达式',\n counter: (current, total) => `${current} / ${total}`,\n invalidRegex: '正则无效',\n readOnly: '只读',\n },\n imageUpload: {\n dropzoneClick: '点击上传',\n dropzoneRest: ',或拖拽到此处',\n dropzoneLimits: (limit, maxSizeMb) => {\n const count = `最多 ${limit} 个文件`;\n return maxSizeMb == null ? `${count}。` : `${count},每个 ${maxSizeMb}MB。`;\n },\n uploadingCount: (count) => `正在上传 ${count} 个文件`,\n clearAll: '全部清除',\n remove: '移除',\n failed: '上传失败',\n fallbackAlt: '图片',\n },\n};\n","import {\n defaultBlockDragLabels,\n defaultCharacterCountLabels,\n defaultChartLabels,\n defaultCodeBlockLabels,\n defaultColorPaletteLabels,\n defaultEmojiSuggestionLabels,\n defaultFindLabels,\n defaultImageUploadLabels,\n defaultLinkPopoverLabels,\n defaultShortcutLabels,\n defaultSlashMenuLabels,\n defaultTocLabels,\n defaultToolbarLabels,\n} from '../labels';\nimport type { LocaleBundle, LocaleCode } from './types';\nimport { zh } from './zh';\n\nexport type { LocaleBundle, LocaleCode } from './types';\n\nconst en: LocaleBundle = {\n toolbar: defaultToolbarLabels,\n slashMenu: defaultSlashMenuLabels,\n chart: defaultChartLabels,\n blockDrag: defaultBlockDragLabels,\n shortcut: defaultShortcutLabels,\n characterCount: defaultCharacterCountLabels,\n emoji: defaultEmojiSuggestionLabels,\n link: defaultLinkPopoverLabels,\n palette: defaultColorPaletteLabels,\n toc: defaultTocLabels,\n codeBlock: defaultCodeBlockLabels,\n find: defaultFindLabels,\n imageUpload: defaultImageUploadLabels,\n};\n\nconst bundles: Record<LocaleCode, LocaleBundle> = { en, zh };\n\n/** 取一份完整文案。未知语言回落英文。 */\nexport function getLocaleBundle(locale: LocaleCode = 'en'): LocaleBundle {\n return bundles[locale] ?? en;\n}\n","'use client';\n\nimport {\n createSearchRegex,\n FindAndReplacePluginKey,\n} from '@tiptap/extension-find-and-replace';\nimport { useEditorState, type Editor } from '@tiptap/react';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type RefObject,\n} from 'react';\nimport {\n FIND_DEBOUNCE_MS,\n hasFindReplaceExtension,\n isComposingKeyEvent,\n runFindCommand,\n} from '../findReplace';\nimport {\n ChevronDownIcon,\n ChevronUpIcon,\n ReplaceAllIcon,\n ReplaceIcon,\n SearchIcon,\n XIcon,\n} from '../icons';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport styles from '../styles/findBar.module.css';\n\nexport interface FindReplaceBarProps {\n /** 编辑器实例(由 `<MarkdownWysiwygEditor>` 提供,浮动条不自行创建编辑器)。 */\n editor: Editor;\n /** 关闭(Esc / ×):宿主负责收起浮动条并把焦点还给编辑器。 */\n onClose: () => void;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n /**\n * 查找输入框 ref。宿主拿它实现「再按一次 Mod+F 聚焦并全选当前查询」。\n * 也用于浮动条打开时自动聚焦。\n */\n inputRef?: RefObject<HTMLInputElement | null>;\n /** 定位 / 层级 class,由宿主给(浮动条自身只负责内部布局)。 */\n className?: string;\n /** 根节点内联样式(编辑器用它落 findBarOffset;宿主自绘时也可用)。 */\n style?: CSSProperties;\n}\n\n/**\n * 查找替换浮动条。匹配、高亮、替换全部由官方\n * `@tiptap/extension-find-and-replace` 承担(含 RE2 安全正则、跨标记匹配、单事务全部替换),\n * 这里只做输入、计数、开关与键位。\n *\n * 打开即把查询推给编辑器,关闭时 `clearSearch()`——高亮是会话级的,不留残影。\n *\n * 给宿主的稳定选择器(内部 class 是 CSS Modules 哈希过的,宿主够不着):根节点\n * `data-find-bar`、两个输入框 `data-find-field=\"search|replace\"`、计数 `data-find-counter`、\n * 两个开关 `data-find-option=\"match-case|whole-words\"`、按钮\n * `data-find-action=\"previous|next|close|replace|replace-all\"`。\n */\n\n/** 「扩展没注册」的警告只发一次(模块级):同页多实例、或条子被反复开关时,重复同一句没有信息量。 */\nlet warnedMissingExtension = false;\nfunction warnMissingExtensionOnce(): void {\n if (warnedMissingExtension) return;\n warnedMissingExtension = true;\n console.warn(\n '[tiptap-markdown-react] FindReplaceBar 需要官方 find 扩展:' +\n '请让 <MarkdownWysiwygEditor findReplace> 保持开启(默认 true),' +\n '否则 setSearchTerm / replaceAll 等命令不存在。',\n );\n}\n\nexport function FindReplaceBar({\n editor,\n onClose,\n locale = 'en',\n inputRef,\n className,\n style,\n}: FindReplaceBarProps) {\n const t = getLocaleBundle(locale).find;\n const [term, setTerm] = useState('');\n const [replaceTerm, setReplaceTerm] = useState('');\n\n // 宿主自己摆条子时可能把 findReplace 关了——那时扩展没注册,命令与插件 state 都不存在。\n // 不拦住的话下面每个 effect 都会 TypeError 把宿主的树带崩;这里退化为不渲染并留一句提示。\n const hasExtension = hasFindReplaceExtension(editor);\n useEffect(() => {\n if (!hasExtension) warnMissingExtensionOnce();\n }, [hasExtension]);\n\n const state = useEditorState({\n editor,\n selector: ({ editor: instance }) => {\n const plugin = FindAndReplacePluginKey.getState(instance.state);\n return {\n currentIndex: plugin?.currentIndex ?? null,\n total: plugin?.results.length ?? 0,\n caseSensitive: plugin?.caseSensitive ?? false,\n wholeWord: plugin?.wholeWord ?? false,\n useRegex: plugin?.useRegex ?? false,\n };\n },\n });\n\n /** 所有命令都从这里走:扩展缺席时直接跳过(见上面 hasExtension)。 */\n const runCommand = useCallback(\n (run: () => void) => {\n if (!hasExtension) return;\n runFindCommand(run);\n },\n [hasExtension],\n );\n\n // 查询词防抖由这里做(扩展注册时 searchDebounceMs: 0,原因见 findReplace.ts):\n // 输入时延迟推给编辑器,回车 / 替换等动作前先 flush,否则会拿着旧查询执行。\n const appliedTermRef = useRef<string | null>(null);\n const pushTerm = useCallback(\n (value: string) => {\n appliedTermRef.current = value;\n runCommand(() => editor.commands.setSearchTerm(value));\n },\n [editor, runCommand],\n );\n const flushTerm = useCallback(() => {\n if (appliedTermRef.current !== term) pushTerm(term);\n }, [pushTerm, term]);\n\n useEffect(() => {\n if (appliedTermRef.current === term) return;\n const timer = setTimeout(() => pushTerm(term), FIND_DEBOUNCE_MS);\n return () => clearTimeout(timer);\n }, [term, pushTerm]);\n\n useEffect(() => {\n runCommand(() => editor.commands.setReplaceTerm(replaceTerm));\n }, [editor, replaceTerm, runCommand]);\n\n // 关闭 / 卸载时清空查询与高亮。\n useEffect(() => {\n return () => {\n if (!editor.isDestroyed) runCommand(() => editor.commands.clearSearch());\n };\n }, [editor, runCommand]);\n\n /** 动作类命令(导航 / 替换):先落定查询词,再执行。 */\n const runAction = useCallback(\n (action: () => void) => {\n flushTerm();\n runCommand(action);\n },\n [flushTerm, runCommand],\n );\n\n // RE2 模式非法(lookaround / backreference 等):官方返回零结果不报错,这里给个提示,\n // 否则「输入了正则却一直 0 / 0」没有解释。\n const invalidPattern = useMemo(\n () =>\n term !== '' &&\n state.useRegex &&\n createSearchRegex(term, {\n caseSensitive: state.caseSensitive,\n useRegex: true,\n wholeWord: false,\n }) === null,\n [term, state.useRegex, state.caseSensitive],\n );\n\n // currentIndex 为 null = 有结果、但还没定位到某一条(官方 `formatResultCount` 同款语义:\n // 那一条分支返回 `0 / total`)。不这样判的话第 1 条会被冒充成「当前」。\n const current =\n state.total === 0 || state.currentIndex === null\n ? 0\n : state.currentIndex + 1;\n const counter = t.counter(current, state.total);\n\n const keepFocus = (e: ReactMouseEvent) => e.preventDefault();\n\n const onFindKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (isComposingKeyEvent(e.nativeEvent)) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n runAction(() => {\n if (e.shiftKey) editor.commands.goToPreviousResult();\n else editor.commands.goToNextResult();\n });\n }\n };\n\n const onReplaceKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (isComposingKeyEvent(e.nativeEvent)) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n runAction(() => editor.commands.replace());\n }\n };\n\n const toggle = (key: 'caseSensitive' | 'wholeWord', value: boolean) => {\n runCommand(() => {\n if (key === 'caseSensitive') editor.commands.setCaseSensitive(value);\n else editor.commands.setWholeWord(value);\n });\n };\n\n if (!hasExtension) return null;\n\n const toggleClass = (on: boolean) =>\n `${styles.btn} ${styles.toggle}${on ? ` ${styles.toggleOn}` : ''}`;\n\n return (\n <div\n className={className ? `${styles.bar} ${className}` : styles.bar}\n style={style}\n data-find-bar=\"\"\n role=\"dialog\"\n aria-label={t.find}\n onKeyDown={(e) => {\n // 输入法组合中的 Esc 是「取消候选词」,放行。其余吃到 Esc 就自己收掉并**消费掉**这次\n // 按键(stopPropagation)——宿主按文档把条子放进自己的弹窗 / 抽屉时,那一层的 Esc\n // 处理不该被同一次按键带着一起关。\n if (isComposingKeyEvent(e.nativeEvent)) return;\n if (e.key !== 'Escape') return;\n e.preventDefault();\n e.stopPropagation();\n onClose();\n }}\n >\n <div className={styles.row}>\n <span className={styles.field}>\n <SearchIcon size={14} className={styles.fieldIcon} />\n <input\n ref={inputRef}\n className={styles.input}\n data-find-field=\"search\"\n value={term}\n onChange={(e) => setTerm(e.target.value)}\n onKeyDown={onFindKeyDown}\n placeholder={t.find}\n aria-label={t.find}\n autoFocus\n spellCheck={false}\n />\n <span\n className={`${styles.counter}${invalidPattern ? ` ${styles.counterInvalid}` : ''}`}\n data-find-counter=\"\"\n aria-live=\"polite\"\n title={invalidPattern ? t.invalidRegex : undefined}\n >\n {invalidPattern ? t.invalidRegex : counter}\n </span>\n </span>\n <button\n type=\"button\"\n className={toggleClass(state.caseSensitive)}\n data-find-option=\"match-case\"\n title={t.caseSensitive}\n aria-label={t.caseSensitive}\n aria-pressed={state.caseSensitive}\n onMouseDown={keepFocus}\n onClick={() => toggle('caseSensitive', !state.caseSensitive)}\n >\n Aa\n </button>\n <button\n type=\"button\"\n className={toggleClass(state.wholeWord)}\n data-find-option=\"whole-words\"\n title={t.wholeWord}\n aria-label={t.wholeWord}\n aria-pressed={state.wholeWord}\n onMouseDown={keepFocus}\n onClick={() => toggle('wholeWord', !state.wholeWord)}\n >\n ab\n </button>\n\n <span className={styles.spacer} />\n <span className={styles.iconGroup}>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"previous\"\n title={t.previous}\n aria-label={t.previous}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.goToPreviousResult())}\n >\n <ChevronUpIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"next\"\n title={t.next}\n aria-label={t.next}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.goToNextResult())}\n >\n <ChevronDownIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"close\"\n title={t.close}\n aria-label={t.close}\n onClick={onClose}\n >\n <XIcon size={16} />\n </button>\n </span>\n </div>\n\n {editor.isEditable ? (\n <div className={styles.row}>\n <span className={styles.field}>\n <input\n className={styles.input}\n data-find-field=\"replace\"\n value={replaceTerm}\n onChange={(e) => setReplaceTerm(e.target.value)}\n onKeyDown={onReplaceKeyDown}\n placeholder={t.replace}\n aria-label={t.replace}\n spellCheck={false}\n />\n </span>\n <span className={styles.spacer} />\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"replace\"\n title={t.replaceOne}\n aria-label={t.replaceOne}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.replace())}\n >\n <ReplaceIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"replace-all\"\n title={t.replaceAll}\n aria-label={t.replaceAll}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.replaceAll())}\n >\n <ReplaceAllIcon size={16} />\n </button>\n </div>\n ) : (\n <div className={styles.row}>\n <span className={styles.readOnly}>{t.readOnly}</span>\n </div>\n )}\n </div>\n );\n}\n","import { Node } from '@tiptap/core';\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n importPlaceholder: {\n insertImportPlaceholder: (label: string) => ReturnType;\n updateImportPlaceholder: (label: string) => ReturnType;\n removeImportPlaceholder: () => ReturnType;\n };\n }\n}\n\nfunction findPlaceholderPos(doc: {\n descendants: (\n f: (node: { type: { name: string } }, pos: number) => boolean | void,\n ) => void;\n}): number | null {\n let found: number | null = null;\n doc.descendants((node, pos) => {\n if (node.type.name === 'importPlaceholder') {\n found = pos;\n return false;\n }\n });\n return found;\n}\n\n/** 导入进行中插在光标处的占位块。不进 Markdown,autosave 不会把进度文案落库。 */\nexport const ImportPlaceholder = Node.create({\n name: 'importPlaceholder',\n group: 'block',\n atom: true,\n selectable: false,\n draggable: false,\n\n addAttributes() {\n return {\n label: {\n default: '',\n parseHTML: (el) => el.getAttribute('data-label') ?? '',\n renderHTML: (attrs) =>\n attrs.label ? { 'data-label': attrs.label } : {},\n },\n };\n },\n\n parseHTML() {\n return [{ tag: 'div[data-type=\"import-placeholder\"]' }];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const label = String(node.attrs.label ?? '');\n return [\n 'div',\n {\n ...HTMLAttributes,\n 'data-type': 'import-placeholder',\n class: 'tmr-import-placeholder',\n contenteditable: 'false',\n 'aria-live': 'polite',\n 'aria-busy': 'true',\n },\n ['span', { class: 'tmr-import-placeholder-spin', 'aria-hidden': 'true' }],\n ['span', { class: 'tmr-import-placeholder-label' }, label],\n ];\n },\n\n renderMarkdown() {\n return '';\n },\n\n addCommands() {\n return {\n insertImportPlaceholder:\n (label) =>\n ({ chain }) =>\n chain()\n .focus()\n .insertContent({\n type: this.name,\n attrs: { label },\n })\n .command(({ tr }) => {\n tr.setMeta('addToHistory', false);\n tr.setMeta('preventUpdate', true);\n return true;\n })\n .run(),\n updateImportPlaceholder:\n (label) =>\n ({ tr, dispatch, state }) => {\n const pos = findPlaceholderPos(state.doc);\n if (pos == null) return false;\n if (dispatch) {\n dispatch(\n tr\n .setNodeMarkup(pos, undefined, { label })\n .setMeta('addToHistory', false)\n .setMeta('preventUpdate', true),\n );\n }\n return true;\n },\n removeImportPlaceholder:\n () =>\n ({ tr, dispatch, state }) => {\n const pos = findPlaceholderPos(state.doc);\n if (pos == null) return false;\n const node = state.doc.nodeAt(pos);\n if (!node) return false;\n if (dispatch) {\n dispatch(\n tr\n .delete(pos, pos + node.nodeSize)\n .setMeta('addToHistory', false)\n .setMeta('preventUpdate', true),\n );\n }\n return true;\n },\n };\n },\n});\n","import type { Editor } from '@tiptap/core';\nimport { useCallback, useEffect, useLayoutEffect, useSyncExternalStore } from 'react';\nimport { getLocaleBundle } from './index';\nimport type { LocaleBundle, LocaleCode } from './types';\n\nconst serverBundle = getLocaleBundle('en');\n\ninterface LocaleEntry {\n locale: LocaleCode;\n bundle: LocaleBundle;\n listeners: Set<() => void>;\n /** 渲染期只改快照,绘制前再通知 NodeView。 */\n pendingNotify: boolean;\n}\n\nconst stores = new WeakMap<Editor, LocaleEntry>();\n\nfunction ensure(editor: Editor): LocaleEntry {\n let entry = stores.get(editor);\n if (!entry) {\n entry = {\n locale: 'en',\n bundle: serverBundle,\n listeners: new Set(),\n pendingNotify: false,\n };\n stores.set(editor, entry);\n }\n return entry;\n}\n\n/** 写入当前语言。同值不重复通知。可在渲染期间调用。 */\nexport function setEditorLocale(editor: Editor, locale: LocaleCode): void {\n const entry = ensure(editor);\n if (entry.locale === locale) return;\n entry.locale = locale;\n entry.bundle = getLocaleBundle(locale);\n entry.pendingNotify = true;\n}\n\n/** 把渲染期写入的语言变化发给已挂载的 NodeView。 */\nexport function flushEditorLocale(editor: Editor): void {\n const entry = stores.get(editor);\n if (!entry?.pendingNotify) return;\n entry.pendingNotify = false;\n entry.listeners.forEach((listener) => listener());\n}\n\nexport function getEditorLocale(editor: Editor): LocaleCode {\n return stores.get(editor)?.locale ?? 'en';\n}\n\nexport function getEditorBundle(editor: Editor): LocaleBundle {\n return stores.get(editor)?.bundle ?? serverBundle;\n}\n\nexport function subscribeEditorLocale(\n editor: Editor,\n listener: () => void,\n): () => void {\n const entry = ensure(editor);\n entry.listeners.add(listener);\n return () => {\n entry.listeners.delete(listener);\n };\n}\n\nconst useIsoLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\n/**\n * 编辑器 / 预览在渲染时写入语言,绘制前通知独立 React 根上的 NodeView。\n * `immediatelyRender: false` 时视图在 effect 之后才创建,首帧就能读到正确语言。\n */\nexport function usePublishEditorLocale(\n editor: Editor | null,\n locale: LocaleCode,\n): void {\n if (editor) setEditorLocale(editor, locale);\n useIsoLayoutEffect(() => {\n if (!editor || editor.isDestroyed) return;\n flushEditorLocale(editor);\n }, [editor, locale]);\n}\n\n/** NodeView 订阅所属编辑器的文案。快照引用稳定,语言不变时不重渲染。 */\nexport function useEditorBundle(editor: Editor): LocaleBundle {\n const subscribe = useCallback(\n (onStoreChange: () => void) => subscribeEditorLocale(editor, onStoreChange),\n [editor],\n );\n const getSnapshot = useCallback(() => getEditorBundle(editor), [editor]);\n return useSyncExternalStore(subscribe, getSnapshot, () => serverBundle);\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/core';\nimport { Selection, TextSelection } from '@tiptap/pm/state';\nimport { NodeViewWrapper, type NodeViewProps } from '@tiptap/react';\nimport { useEffect, useRef, useState } from 'react';\nimport { XIcon } from '../icons';\nimport { useEditorBundle } from '../i18n/store';\nimport type { ImageUploadOptions } from '../imageUpload';\nimport styles from '../styles/imageUpload.module.css';\n\ninterface FileItem {\n id: string;\n file: File;\n /** 0–100。 */\n progress: number;\n status: 'uploading' | 'error';\n abort: AbortController;\n}\n\nlet seq = 0;\nconst nextId = () => `image-upload-${++seq}`;\n\n/** 文件体积的人类可读格式。 */\nfunction formatFileSize(bytes: number): string {\n if (bytes <= 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const i = Math.min(\n Math.floor(Math.log(bytes) / Math.log(1024)),\n units.length - 1,\n );\n return `${parseFloat((bytes / 1024 ** i).toFixed(1))} ${units[i]}`;\n}\n\n/** MB 数值去掉多余的 .0(5MB / 2.5MB)。 */\nconst toMb = (bytes: number) => parseFloat((bytes / 1024 / 1024).toFixed(1));\n\n/**\n * 上传完成后把光标移到图片之后;没有后继节点就补一个空段落。\n * 不挪的话替换成 atom 图片后光标悬空,用户接着打字会落在占位块之前。\n */\nfunction focusNextNode(editor: Editor) {\n const { state, view } = editor;\n const next = Selection.findFrom(state.selection.$to, 1, true);\n if (next) {\n view.dispatch(state.tr.setSelection(next).scrollIntoView());\n return;\n }\n const paragraph = state.schema.nodes.paragraph;\n if (!paragraph) return;\n const end = state.doc.content.size;\n const tr = state.tr.insert(end, paragraph.create());\n view.dispatch(\n tr\n .setSelection(TextSelection.near(tr.doc.resolve(end + 1)))\n .scrollIntoView(),\n );\n}\n\n/**\n * 图片上传占位块的 NodeView:空态是点击 / 拖拽区,排队后是文件行(进度条 + 百分比 +\n * 移除),全部成功时就地把自己换成图片节点。上传函数与回调都来自扩展 options。\n *\n * 组件结构 / 文件队列 / 拖拽 / 中止逻辑按官方 UI Components 的 `image-upload-node`\n * 移植(MIT,见 THIRD_PARTY_LICENSES.md 第 2 节),按本库的样式体系(CSS Modules +\n * --tmr-* 变量)与 labels 注入重写。\n */\nexport function ImageUploadView(props: NodeViewProps) {\n const { node, extension, editor, getPos } = props;\n const options = extension.options as ImageUploadOptions;\n const labels = useEditorBundle(editor).imageUpload;\n\n const accept = String(node.attrs.accept ?? 'image/*');\n const limit = Number(node.attrs.limit ?? 1) || 1;\n const maxSize = Number(node.attrs.maxSize ?? 0) || 0;\n\n const [items, setItems] = useState<FileItem[]>([]);\n const [dragActive, setDragActive] = useState(false);\n const [dragOver, setDragOver] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n // 卸载(占位块被替换 / 删除 / 换文档)时,把在飞的请求全部中断\n const itemsRef = useRef(items);\n itemsRef.current = items;\n useEffect(\n () => () => itemsRef.current.forEach((it) => it.abort.abort()),\n [],\n );\n\n const patchItem = (id: string, partial: Partial<FileItem>) => {\n setItems((prev) =>\n prev.map((it) => (it.id === id ? { ...it, ...partial } : it)),\n );\n };\n\n const uploadOne = async (file: File): Promise<string | null> => {\n if (maxSize > 0 && file.size > maxSize) {\n options.onError?.(\n new Error(\n `File size exceeds the maximum of ${formatFileSize(maxSize)}`,\n ),\n );\n return null;\n }\n if (!options.upload) {\n options.onError?.(new Error('Upload function is not defined'));\n return null;\n }\n\n const abort = new AbortController();\n const id = nextId();\n setItems((prev) => [\n ...prev,\n { id, file, progress: 0, status: 'uploading', abort },\n ]);\n\n try {\n const url = await options.upload(\n file,\n ({ progress }) => patchItem(id, { progress }),\n abort.signal,\n );\n if (!url) throw new Error('Upload returned no URL');\n if (abort.signal.aborted) return null;\n options.onSuccess?.(url);\n return url;\n } catch (err) {\n // abort 是用户主动移除,不算失败\n if (!abort.signal.aborted) {\n patchItem(id, { status: 'error', progress: 0 });\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n }\n return null;\n }\n };\n\n const uploadFiles = async (files: File[]) => {\n if (files.length === 0) return;\n if (limit > 0 && files.length > limit) {\n options.onError?.(\n new Error(`Maximum ${limit} file${limit === 1 ? '' : 's'} at a time`),\n );\n return;\n }\n // 逐文件配对结果:只对成功的做插入,且文件名跟着自己的 URL 走\n const settled = await Promise.all(\n files.map(async (file) => ({ file, url: await uploadOne(file) })),\n );\n const uploaded = settled.filter(\n (s): s is { file: File; url: string } => s.url != null,\n );\n if (uploaded.length === 0) return;\n\n const pos = getPos();\n if (typeof pos !== 'number') return;\n const imageType = options.type ?? 'image';\n const nodes = uploaded.map(({ file, url }) => {\n const alt = file.name.replace(/\\.[^/.]+$/, '') || labels.fallbackAlt;\n return { type: imageType, attrs: { src: url, alt, title: alt } };\n });\n editor\n .chain()\n .focus()\n .deleteRange({ from: pos, to: pos + node.nodeSize })\n .insertContentAt(pos, nodes)\n .run();\n focusNextNode(editor);\n };\n\n const removeItem = (id: string) => {\n itemsRef.current.find((it) => it.id === id)?.abort.abort();\n setItems((prev) => prev.filter((it) => it.id !== id));\n };\n\n const clearAll = () => {\n itemsRef.current.forEach((it) => it.abort.abort());\n setItems([]);\n };\n\n const openPicker = () => {\n if (!editor.isEditable || items.length > 0) return;\n const input = inputRef.current;\n if (!input) return;\n input.value = '';\n input.click();\n };\n\n const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(e.target.files ?? []);\n e.target.value = '';\n if (files.length > 0) void uploadFiles(files);\n };\n\n const onDrop = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n setDragOver(false);\n const files = Array.from(e.dataTransfer.files);\n if (files.length > 0) void uploadFiles(files);\n };\n\n const editable = editor.isEditable;\n const hasFiles = items.length > 0;\n const maxSizeMb = maxSize > 0 ? toMb(maxSize) : null;\n const dropzoneClass = [\n styles.dropzone,\n dragActive ? styles.dragActive : '',\n dragOver ? styles.dragOver : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <NodeViewWrapper\n className={styles.wrap}\n data-image-upload=\"\"\n tabIndex={editable ? 0 : undefined}\n onClick={openPicker}\n >\n {!hasFiles ? (\n <div\n className={dropzoneClass}\n data-image-upload-dropzone=\"\"\n role={editable ? 'button' : undefined}\n aria-label={\n editable ? `${labels.dropzoneClick}${labels.dropzoneRest}` : undefined\n }\n onDragEnter={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(true);\n }}\n onDragOver={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragOver(true);\n }}\n onDragLeave={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n setDragActive(false);\n setDragOver(false);\n }\n }}\n onDrop={onDrop}\n >\n <span className={styles.fileShape} aria-hidden>\n <FileDocIcon />\n <FileCornerIcon />\n <span className={styles.fileBadge}>\n <CloudUploadIcon />\n </span>\n </span>\n <span className={styles.text}>\n <span className={styles.textMain}>\n <em className={styles.textEm}>{labels.dropzoneClick}</em>\n {labels.dropzoneRest}\n </span>\n <span className={styles.subtext}>\n {labels.dropzoneLimits(limit, maxSizeMb)}\n </span>\n </span>\n </div>\n ) : (\n <div className={styles.previews} data-image-upload-list=\"\">\n {items.length > 1 && (\n <div className={styles.header}>\n <span>{labels.uploadingCount(items.length)}</span>\n <button\n type=\"button\"\n className={styles.clearAll}\n onClick={(e) => {\n e.stopPropagation();\n clearAll();\n }}\n >\n {labels.clearAll}\n </button>\n </div>\n )}\n {items.map((it) => (\n <div\n key={it.id}\n className={styles.preview}\n data-image-upload-item=\"\"\n >\n {it.status === 'uploading' && (\n <div\n className={styles.progress}\n style={{ width: `${it.progress}%` }}\n aria-hidden\n />\n )}\n <div className={styles.previewContent}>\n <span className={styles.fileInfo}>\n <span className={styles.fileIcon} aria-hidden>\n <CloudUploadIcon />\n </span>\n <span className={styles.details}>\n <span className={styles.fileName}>{it.file.name}</span>\n <span className={styles.subtext}>\n {it.status === 'error'\n ? labels.failed\n : formatFileSize(it.file.size)}\n </span>\n </span>\n </span>\n <span className={styles.actions}>\n {it.status === 'uploading' && (\n <span className={styles.progressText}>\n {Math.round(it.progress)}%\n </span>\n )}\n <button\n type=\"button\"\n className={styles.remove}\n data-image-upload-remove=\"\"\n title={labels.remove}\n aria-label={labels.remove}\n onClick={(e) => {\n e.stopPropagation();\n removeItem(it.id);\n }}\n >\n <XIcon size={14} />\n </button>\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n\n {editable && (\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={limit > 1}\n hidden\n onChange={onInputChange}\n onClick={(e) => e.stopPropagation()}\n />\n )}\n </NodeViewWrapper>\n );\n}\n\n/* 图标 path 取自 tiptap-ui-components(MIT)的 image-upload-node,见 THIRD_PARTY_LICENSES.md */\n\nfunction CloudUploadIcon({ size = 14 }: { size?: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden\n >\n <path d=\"M11.1953 4.41771C10.3478 4.08499 9.43578 3.94949 8.5282 4.02147C7.62062 4.09345 6.74133 4.37102 5.95691 4.83316C5.1725 5.2953 4.50354 5.92989 4.00071 6.68886C3.49788 7.44783 3.17436 8.31128 3.05465 9.2138C2.93495 10.1163 3.0222 11.0343 3.3098 11.8981C3.5974 12.7619 4.07781 13.5489 4.71463 14.1995C5.10094 14.5942 5.09414 15.2274 4.69945 15.6137C4.30476 16 3.67163 15.9932 3.28532 15.5985C2.43622 14.731 1.79568 13.6816 1.41221 12.5299C1.02875 11.3781 0.91241 10.1542 1.07201 8.95084C1.23162 7.74748 1.66298 6.59621 2.33343 5.58425C3.00387 4.57229 3.89581 3.72617 4.9417 3.10998C5.98758 2.4938 7.15998 2.1237 8.37008 2.02773C9.58018 1.93176 10.7963 2.11243 11.9262 2.55605C13.0561 2.99968 14.0703 3.69462 14.8919 4.58825C15.5423 5.29573 16.0585 6.11304 16.4177 7.00002H17.4999C18.6799 6.99991 19.8288 7.37933 20.7766 8.08222C21.7245 8.78515 22.4212 9.7743 22.7637 10.9036C23.1062 12.0328 23.0765 13.2423 22.6788 14.3534C22.2812 15.4644 21.5367 16.4181 20.5554 17.0736C20.0962 17.3803 19.4752 17.2567 19.1684 16.7975C18.8617 16.3382 18.9853 15.7172 19.4445 15.4105C20.069 14.9934 20.5427 14.3865 20.7958 13.6794C21.0488 12.9724 21.0678 12.2027 20.8498 11.4841C20.6318 10.7655 20.1885 10.136 19.5853 9.6887C18.9821 9.24138 18.251 8.99993 17.5001 9.00002H15.71C15.2679 9.00002 14.8783 8.70973 14.7518 8.28611C14.4913 7.41374 14.0357 6.61208 13.4195 5.94186C12.8034 5.27164 12.0427 4.75043 11.1953 4.41771Z\" />\n <path d=\"M11 14.4142V21C11 21.5523 11.4477 22 12 22C12.5523 22 13 21.5523 13 21V14.4142L15.2929 16.7071C15.6834 17.0976 16.3166 17.0976 16.7071 16.7071C17.0976 16.3166 17.0976 15.6834 16.7071 15.2929L12.7078 11.2936C12.7054 11.2912 12.703 11.2888 12.7005 11.2864C12.5208 11.1099 12.2746 11.0008 12.003 11L12 11L11.997 11C11.8625 11.0004 11.7343 11.0273 11.6172 11.0759C11.502 11.1236 11.3938 11.1937 11.2995 11.2864C11.297 11.2888 11.2946 11.2912 11.2922 11.2936L7.29289 15.2929C6.90237 15.6834 6.90237 16.3166 7.29289 16.7071C7.68342 17.0976 8.31658 17.0976 8.70711 16.7071L11 14.4142Z\" />\n </svg>\n );\n}\n\nfunction FileDocIcon() {\n return (\n <svg\n width=\"43\"\n height=\"57\"\n viewBox=\"0 0 43 57\"\n fill=\"none\"\n aria-hidden\n className={styles.fileShapeDoc}\n >\n <path\n d=\"M0.75 10.75C0.75 5.64137 4.89137 1.5 10 1.5H32.3431C33.2051 1.5 34.0317 1.84241 34.6412 2.4519L40.2981 8.10876C40.9076 8.71825 41.25 9.5449 41.25 10.4069V46.75C41.25 51.8586 37.1086 56 32 56H10C4.89137 56 0.75 51.8586 0.75 46.75V10.75Z\"\n fill=\"currentColor\"\n fillOpacity=\"0.11\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n />\n </svg>\n );\n}\n\nfunction FileCornerIcon() {\n return (\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"currentColor\"\n aria-hidden\n className={styles.fileShapeCorner}\n >\n <path d=\"M0 0.75H0.343146C1.40401 0.75 2.42143 1.17143 3.17157 1.92157L8.82843 7.57843C9.57857 8.32857 10 9.34599 10 10.4069V10.75H4C1.79086 10.75 0 8.95914 0 6.75V0.75Z\" />\n </svg>\n );\n}\n","/**\n * 图片上传占位块(`ImageUploadNode`):交互与扩展结构对齐官方 UI Components 的\n * `image-upload-node`(MIT,移植范围与声明见 THIRD_PARTY_LICENSES.md 第 2 节)——\n * 工具栏按钮插入占位块,块内拖拽 / 点选、进度就地显示,全部成功后原地换成图片节点。\n */\nimport { mergeAttributes, Node, type Editor } from '@tiptap/core';\nimport { ReactNodeViewRenderer } from '@tiptap/react';\nimport { ImageUploadView } from './components/ImageUploadView';\n\n/**\n * 上传函数:收到文件与进度回调(`progress` 为 0–100),返回可访问的图片 URL。\n * `signal` 在用户移除该文件 / 清空队列 / 占位块被删掉时 abort,宿主据此中断请求。\n * 旧的 `(file) => Promise<string>` 单参函数仍可直接传入。\n */\nexport type ImageUploadFn = (\n file: File,\n onProgress?: (event: { progress: number }) => void,\n signal?: AbortSignal,\n) => Promise<string>;\n\nexport interface ImageUploadOptions {\n /** 真正干活的上传(网络 I/O 归宿主)。未配置时选中文件即走 `onError`。 */\n upload?: ImageUploadFn;\n /** 文件选择框接受的文件类型,默认 `image/*`。 */\n accept?: string;\n /** 一次可排队上传的文件数上限,默认 1;大于 1 时文件选择框允许多选。 */\n limit?: number;\n /** 单文件体积上限(字节),默认 0 = 不限。 */\n maxSize?: number;\n /** 上传成功后就地替换成的节点类型,默认 `image`(本库的块级图片)。 */\n type?: string;\n /** 单个文件失败的回调:超体积、超数量、上传函数抛错都走这里。 */\n onError?: (error: Error) => void;\n /** 单个文件成功的回调(先于占位块替换触发)。 */\n onSuccess?: (url: string) => void;\n}\n\n/**\n * 组件层(`<MarkdownWysiwygEditor imageUpload={{…}}>`)的配置:`upload` 必填——\n * 组件入口就是要注入 I/O 的地方;扩展层单独用时才允许先不配。\n */\nexport type ImageUploadConfig = ImageUploadOptions & { upload: ImageUploadFn };\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n imageUpload: {\n /**\n * 在光标处插入图片上传占位块:可点击选图 / 拖拽,上传进度就地显示,\n * 全部成功后就地换成图片节点。参数覆盖 configure 的 accept / limit / maxSize。\n */\n setImageUploadNode: (options?: {\n accept?: string;\n limit?: number;\n maxSize?: number;\n }) => ReturnType;\n };\n }\n}\n\n/** 编辑器里是否注册了图片上传扩展(工具栏据此把图片按钮切成「插入上传块」)。 */\nexport function hasImageUpload(editor: Editor): boolean {\n return editor.extensionManager.extensions.some(\n (ext) => ext.name === ImageUploadNode.name,\n );\n}\n\n/**\n * 图片上传占位块(对齐官方 ImageUploadNode 的交互):块级 atom,NodeView 里自带\n * 拖拽区 / 文件队列 / 进度条,上传成功后原地替换为 `type` 指定的图片节点。\n *\n * 两条与官方不同的边界处理:\n * - `renderMarkdown` 返回空串——上传中的占位块不进 Markdown,autosave 不会把\n * 进度文案落库(HTML 序列化才有 `data-type=\"image-upload\"` 的痕迹)。\n * - 体积上限 0 表示「不限」:判超限必须走 `maxSize > 0`,否则默认值 0 会把所有\n * 非空文件都判成超限(官方实现即栽在这条上)。\n */\nexport const ImageUploadNode = Node.create<ImageUploadOptions>({\n name: 'imageUpload',\n group: 'block',\n atom: true,\n draggable: true,\n selectable: true,\n\n addOptions() {\n return {\n upload: undefined,\n accept: 'image/*',\n limit: 1,\n maxSize: 0,\n type: 'image',\n onError: undefined,\n onSuccess: undefined,\n };\n },\n\n addAttributes() {\n return {\n accept: { default: this.options.accept },\n limit: { default: this.options.limit },\n maxSize: { default: this.options.maxSize },\n };\n },\n\n parseHTML() {\n return [{ tag: 'div[data-type=\"image-upload\"]' }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'div',\n mergeAttributes({ 'data-type': 'image-upload' }, HTMLAttributes),\n ];\n },\n\n /** 占位块不进 Markdown(与 ImportPlaceholder 同):上传中的状态不该被保存。 */\n renderMarkdown() {\n return '';\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(ImageUploadView);\n },\n\n addCommands() {\n return {\n setImageUploadNode:\n (options) =>\n ({ commands }) =>\n commands.insertContent({ type: this.name, attrs: options }),\n };\n },\n\n addKeyboardShortcuts() {\n return {\n // 官方图片按钮的快捷键(Cmd/Ctrl+Shift+I)。Windows 上 Ctrl+Shift+I 是\n // DevTools 的键位,冲突时浏览器优先——与官方行为一致。\n 'Mod-Shift-i': () => this.editor.commands.setImageUploadNode(),\n // 选中占位块时按 Enter = 打开文件选择框。借 NodeView 的点击路径(NodeViewWrapper\n // 外包一层 div,所以要点它的第一个子元素),与官方同款实现。\n Enter: ({ editor }) => {\n const { selection } = editor.state;\n const { nodeAfter } = selection.$from;\n if (nodeAfter?.type.name !== this.name || !editor.isActive(this.name)) {\n return false;\n }\n const nodeEl = editor.view.nodeDOM(selection.$from.pos);\n const clickable =\n nodeEl instanceof HTMLElement ? nodeEl.firstChild : null;\n if (clickable instanceof HTMLElement) {\n clickable.click();\n return true;\n }\n return false;\n },\n };\n },\n});\n","import type { Editor } from '@tiptap/core';\nimport { NodeSelection } from '@tiptap/pm/state';\nimport { normalizeImageAlign, type ImageAlign } from './imageMarkdown';\n\n/**\n * 找「当前图片」的位置:节点选区落在图上,或光标紧邻图前 / 图后。\n * 对齐与重置尺寸(resetImageSize)共用这套判据。\n */\nexport function findImagePos(editor: Editor): number | null {\n const { selection } = editor.state;\n if (selection instanceof NodeSelection && selection.node.type.name === 'image') {\n return selection.from;\n }\n const { $from } = selection;\n if ($from.nodeAfter?.type.name === 'image') return $from.pos;\n if ($from.nodeBefore?.type.name === 'image') {\n return $from.pos - $from.nodeBefore.nodeSize;\n }\n return null;\n}\n\nfunction readAlign(editor: Editor, pos: number): ImageAlign | null {\n return normalizeImageAlign(editor.state.doc.nodeAt(pos)?.attrs.align);\n}\n\n/** 当前图片是否已对齐到指定方向(宿主画按钮态用;没选中图时为 false)。 */\nexport function isImageAlignActive(editor: Editor, align: ImageAlign): boolean {\n const pos = findImagePos(editor);\n return pos != null && readAlign(editor, pos) === align;\n}\n\n/**\n * 把「当前图片」(选中 / 光标紧邻)对齐到 left / center / right。\n * attrs 存 `align`、渲染成 `data-align`;markdown 里以\n * `<img … data-align=\"…\">` 保真(见 imageMarkdown.ts)。返回是否找到图片。\n * 已是对齐值时不写事务(不污染 undo 历史)。\n */\nexport function setImageAlign(editor: Editor, align: ImageAlign): boolean {\n const pos = findImagePos(editor);\n if (pos == null) return false;\n if (readAlign(editor, pos) === align) return true;\n return editor\n .chain()\n .focus()\n .setNodeSelection(pos)\n .updateAttributes('image', { align })\n .run();\n}\n\n/**\n * 按文档位置对齐(悬停工具条用:hover 时记下位置,attrs 读写都走 pos)。\n * 不改变选区——点一下对齐按钮不该先「选中」图片。\n *\n * 注意别改成长期持有 img 元素的引用:**attrs 更新时 prosemirror 会替换裸渲染的\n * img 元素**(旧引用脱离文档、读不到新属性,后续 posAtDOM 也会抛),所以位置比元素可靠。\n */\nexport function setImageAlignAtPos(\n editor: Editor,\n pos: number,\n align: ImageAlign,\n): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return false;\n if (normalizeImageAlign(node.attrs.align) === align) return true;\n editor.view.dispatch(editor.state.tr.setNodeAttribute(pos, 'align', align));\n return true;\n}\n\n/**\n * 按 DOM 元素对齐(宿主自定义 UI 用:拿到的是当前挂着的元素)。转成位置后走\n * {@link setImageAlignAtPos}。\n */\nexport function setImageAlignAt(\n editor: Editor,\n imageElement: HTMLElement,\n align: ImageAlign,\n): boolean {\n try {\n const pos = editor.view.posAtDOM(imageElement, 0);\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return false;\n return setImageAlignAtPos(editor, pos, align);\n } catch {\n return false;\n }\n}\n","import type { EmojiItem } from '@tiptap/extension-emoji';\n\n/** 底部标签。顺序与官方数据的 group 一致;图标是分类代表,不依赖某一条数据还在不在。 */\nexport const EMOJI_CATEGORIES = [\n { id: 'smileys', group: '', label: 'Smileys', icon: '😀' },\n { id: 'people', group: 'people & body', label: 'People', icon: '👋' },\n { id: 'nature', group: 'animals & nature', label: 'Animals & nature', icon: '🐻' },\n { id: 'food', group: 'food & drink', label: 'Food & drink', icon: '🍔' },\n { id: 'travel', group: 'travel & places', label: 'Travel & places', icon: '✈️' },\n { id: 'activities', group: 'activities', label: 'Activities', icon: '⚽' },\n { id: 'objects', group: 'objects', label: 'Objects', icon: '💡' },\n { id: 'symbols', group: 'symbols', label: 'Symbols', icon: '💯' },\n { id: 'flags', group: 'flags', label: 'Flags', icon: '🏁' },\n] as const;\n\nexport type EmojiCategoryId = (typeof EMOJI_CATEGORIES)[number]['id'];\n\n/** 肤色和发型零件。官方扩展不支持肤色修饰,单独显示也不是可选表情。 */\nexport function isEmojiComponent(item: EmojiItem): boolean {\n return item.group === 'components';\n}\n\nexport function itemsInCategory(items: EmojiItem[], id: EmojiCategoryId): EmojiItem[] {\n const category = EMOJI_CATEGORIES.find((entry) => entry.id === id);\n if (!category) return [];\n return items.filter((item) => (item.group ?? '') === category.group);\n}\n","import type { EmojiItem } from '@tiptap/extension-emoji';\nimport { isEmojiComponent } from './emojiCategories';\n\n/**\n * 单独的区域指示符是拼国旗的字母零件,系统字体画成方框字母。\n * 建议列表里不放;手打完整 `:regional_indicator_a:` 仍能变成节点。\n */\nfunction isRegionalIndicator(item: EmojiItem): boolean {\n return item.name.startsWith('regional_indicator_');\n}\n\n/**\n * 按短码 / 名称优先、标签其次过滤表情。\n * 空查询返回全部可用表情,弹层再按分类切开。\n * 区域指示符和肤色零件不进列表。没有 Unicode 字符的自定义图也不进。\n */\nexport function filterEmojiItems(items: EmojiItem[], query: string): EmojiItem[] {\n const q = query.trim().toLowerCase();\n const usable = items.filter(\n (item) => !!item.emoji && !isRegionalIndicator(item) && !isEmojiComponent(item),\n );\n if (!q) return usable;\n\n const primary: EmojiItem[] = [];\n const secondary: EmojiItem[] = [];\n for (const item of usable) {\n const nameHit =\n item.name.toLowerCase().startsWith(q) ||\n item.shortcodes.some((code) => code.toLowerCase().startsWith(q));\n if (nameHit) primary.push(item);\n else if (item.tags.some((tag) => tag.toLowerCase().startsWith(q))) {\n secondary.push(item);\n }\n }\n return [...primary, ...secondary];\n}\n","import type { EmojiItem } from '@tiptap/extension-emoji';\nimport { EmojiSuggestionPluginKey } from '@tiptap/extension-emoji';\nimport type { AnyExtension, Editor, Range } from '@tiptap/core';\nimport type { EditorState } from '@tiptap/pm/state';\nimport { ReactRenderer } from '@tiptap/react';\nimport type {\n SuggestionKeyDownProps,\n SuggestionProps,\n} from '@tiptap/suggestion';\nimport {\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { getEditorBundle } from '../i18n/store';\nimport styles from '../styles/emojiSuggestion.module.css';\nimport { EMOJI_CATEGORIES, itemsInCategory, type EmojiCategoryId } from './emojiCategories';\nimport { filterEmojiItems } from './filterEmoji';\n\n/** 与样式里的 8 列对齐,方向键按格子移动。 */\nconst COLUMNS = 8;\n\nexport interface EmojiSuggestionPopupHandle {\n /** 编辑器键盘事件经 IME 守卫后委托进来;返回 true 表示已消费。 */\n onKeyDown: (event: KeyboardEvent) => boolean;\n}\n\nexport interface EmojiSuggestionPopupProps {\n items: EmojiItem[];\n query: string;\n menuLabel: string;\n command: (item: EmojiItem) => void;\n}\n\n/** 输入法组合态下的按键交给 IME 自己处理(上游 suggestion 不查 composing)。 */\nfunction isComposing(event: KeyboardEvent, view: { composing: boolean }): boolean {\n return view.composing || event.isComposing || event.keyCode === 229;\n}\n\nconst themeVars = [\n '--tmr-text',\n '--tmr-accent',\n '--tmr-toolbar-border',\n '--tmr-toolbar-muted',\n] as const;\n\nfunction copyThemeVars(from: HTMLElement, to: HTMLElement) {\n const computed = getComputedStyle(from);\n for (const name of themeVars) {\n const value = computed.getPropertyValue(name);\n if (value) to.style.setProperty(name, value);\n }\n}\n\nfunction moveIndex(index: number, key: string, length: number, shiftKey: boolean): number | null {\n if (key === 'ArrowRight' || (key === 'Tab' && !shiftKey)) {\n return (index + 1) % length;\n }\n if (key === 'ArrowLeft' || (key === 'Tab' && shiftKey)) {\n return (index - 1 + length) % length;\n }\n if (key === 'ArrowDown') {\n const next = index + COLUMNS;\n return next < length ? next : null;\n }\n if (key === 'ArrowUp') {\n const next = index - COLUMNS;\n return next >= 0 ? next : null;\n }\n return null;\n}\n\nfunction keepCellInView(menu: HTMLElement, cell: HTMLElement) {\n const menuRect = menu.getBoundingClientRect();\n const cellRect = cell.getBoundingClientRect();\n if (cellRect.top < menuRect.top) menu.scrollTop -= menuRect.top - cellRect.top;\n else if (cellRect.bottom > menuRect.bottom) {\n menu.scrollTop += cellRect.bottom - menuRect.bottom;\n }\n}\n\n/**\n * 冒号表情格子。空查询按底部分类标签浏览;继续打短码则在全部分类里筛选。\n * 定位由 @tiptap/suggestion 的 mount 接管。\n */\nexport const EmojiSuggestionPopup = forwardRef<\n EmojiSuggestionPopupHandle,\n EmojiSuggestionPopupProps\n>(function EmojiSuggestionPopup({ items, query, menuLabel, command }, ref) {\n const gridId = useId();\n const browsing = !query.trim();\n const [categoryId, setCategoryId] = useState<EmojiCategoryId>(EMOJI_CATEGORIES[0].id);\n const [index, setIndex] = useState(0);\n const menuRef = useRef<HTMLDivElement>(null);\n const cellRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const categories = useMemo(\n () => EMOJI_CATEGORIES.filter((category) => itemsInCategory(items, category.id).length > 0),\n [items],\n );\n const activeId = categories.some((category) => category.id === categoryId)\n ? categoryId\n : (categories[0]?.id ?? EMOJI_CATEGORIES[0].id);\n const visible = browsing ? itemsInCategory(items, activeId) : items;\n\n useEffect(() => {\n setIndex(0);\n menuRef.current?.scrollTo(0, 0);\n }, [query, activeId]);\n\n useEffect(() => {\n const menu = menuRef.current;\n const cell = cellRefs.current[index];\n if (menu && cell) keepCellInView(menu, cell);\n }, [index, activeId, query]);\n\n useImperativeHandle(\n ref,\n () => ({\n onKeyDown(event) {\n if (!visible.length) return false;\n const next = moveIndex(index, event.key, visible.length, event.shiftKey);\n if (next !== null) {\n setIndex(next);\n return true;\n }\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight' ||\n event.key === 'Tab'\n ) {\n return true;\n }\n if (event.key === 'Enter') {\n const item = visible[index];\n if (!item) return false;\n command(item);\n return true;\n }\n return false;\n },\n }),\n [visible, index, command],\n );\n\n if (!visible.length) return null;\n\n return (\n <div className={styles.panel}>\n <div\n id={gridId}\n ref={menuRef}\n className={styles.menu}\n role=\"listbox\"\n aria-label={menuLabel}\n >\n {visible.map((item, i) => {\n const shortcode = `:${item.name}:`;\n return (\n <button\n key={item.name}\n ref={(node) => {\n cellRefs.current[i] = node;\n }}\n type=\"button\"\n role=\"option\"\n aria-label={shortcode}\n aria-selected={i === index}\n title={shortcode}\n className={i === index ? styles.cellActive : styles.cell}\n tabIndex={-1}\n onMouseEnter={() => setIndex(i)}\n onMouseDown={(event) => {\n event.preventDefault();\n command(item);\n }}\n >\n {item.emoji}\n </button>\n );\n })}\n </div>\n {browsing ? (\n <div className={styles.tabs} role=\"tablist\" aria-label={menuLabel}>\n {categories.map((category) => {\n const selected = category.id === activeId;\n return (\n <button\n key={category.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={selected}\n aria-controls={gridId}\n aria-label={category.label}\n title={category.label}\n className={selected ? styles.tabActive : styles.tab}\n tabIndex={-1}\n onMouseDown={(event) => {\n event.preventDefault();\n setCategoryId(category.id);\n }}\n >\n {category.icon}\n </button>\n );\n })}\n </div>\n ) : null}\n </div>\n );\n});\n\n/**\n * 把 schema 上的 emoji 扩展配成带(或不带)建议列表的那一份。\n * configure 深合并 options,官方的 command / char / pluginKey 留着。\n * 关掉时 allow 恒为 false:不弹层,`:smile:` 输入规则仍在。\n */\nexport function configureEmojiSuggestion(\n extension: AnyExtension,\n enabled: boolean,\n): AnyExtension {\n if (!enabled) {\n return extension.configure({\n suggestion: { allow: () => false },\n });\n }\n\n return extension.configure({\n suggestion: {\n pluginKey: EmojiSuggestionPluginKey,\n items: ({ editor, query }: { editor: Editor; query: string }) =>\n filterEmojiItems(editor.storage.emoji.emojis, query),\n allow: ({\n editor,\n state,\n range,\n }: {\n editor: Editor;\n state: EditorState;\n range: Range;\n }) => {\n if (!editor.isEditable) return false;\n if (editor.isActive('code') || editor.isActive('codeBlock')) return false;\n const type = state.schema.nodes.emoji;\n if (!type) return false;\n return !!state.doc.resolve(range.from).parent.type.contentMatch.matchType(type);\n },\n render: () => {\n let renderer: ReactRenderer<EmojiSuggestionPopupHandle> | null = null;\n let unmountPopup: (() => void) | null = null;\n return {\n onStart: (props: SuggestionProps<EmojiItem>) => {\n renderer = new ReactRenderer(EmojiSuggestionPopup, {\n editor: props.editor,\n props: {\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).emoji.menuLabel,\n command: props.command,\n },\n className: styles.popupHost,\n });\n copyThemeVars(props.editor.view.dom, renderer.element);\n unmountPopup = props.mount(renderer.element);\n },\n onUpdate: (props: SuggestionProps<EmojiItem>) => {\n renderer?.updateProps({\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).emoji.menuLabel,\n command: props.command,\n });\n },\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (!renderer || isComposing(props.event, props.view)) return false;\n return renderer.ref?.onKeyDown(props.event) ?? false;\n },\n onExit: () => {\n unmountPopup?.();\n unmountPopup = null;\n renderer?.destroy();\n renderer = null;\n },\n };\n },\n },\n });\n}\n","/**\n * 本库自写的词数。传给 MIT 包 `@tiptap/extensions` 的 CharacterCount 当作\n * `wordCounter`,不复制该扩展的源码。\n *\n * 扩展默认按空格切分,一整段中文会被算成 1 个词。这里每个汉字算 1 个词,\n * 其余仍按空白切开,并丢掉纯标点。\n */\nconst CJK_IDEOGRAPH =\n /[\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff]/g;\n\nexport function countWords(text: string): number {\n let cjk = 0;\n const rest = text.replace(CJK_IDEOGRAPH, () => {\n cjk += 1;\n return ' ';\n });\n const words = rest\n .split(/\\s+/)\n .filter((word) => /[\\p{L}\\p{N}]/u.test(word));\n return cjk + words.length;\n}\n","import type { Editor } from '@tiptap/core';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport type { SlashMenuItem } from './types';\n\n/**\n * 默认斜杠命令项:全部是「可序列化为 markdown」的块级操作。\n * 图片、附件、AI、二级子菜单、最近使用不在 v1 范围。\n *\n * 图标沿用工具栏下拉菜单的文字字形语言(`{ }` / `√x` / `∑` / `—` / `❝`),\n * 三组(文本 / 列表 / 高级)在弹窗里分节展示。\n */\nexport function createDefaultSlashMenuItems(\n locale: LocaleCode = 'en',\n): SlashMenuItem[] {\n const labels = getLocaleBundle(locale).slashMenu;\n return [\n {\n id: 'normal-text',\n title: labels.normalText,\n group: labels.groupText,\n icon: '¶',\n aliases: ['zhengwen', '正文', 'paragraph', 'text'],\n order: 0,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).setParagraph().run();\n },\n },\n ...([1, 2, 3] as const).map((level): SlashMenuItem => ({\n id: `heading-${level}`,\n title: labels.headingLabel(level),\n group: labels.groupText,\n icon: `H${level}`,\n aliases: [`h${level}`, 'biaoti', '标题', 'heading'],\n order: level,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .setHeading({ level })\n .run();\n },\n })),\n {\n id: 'bullet-list',\n title: labels.bulletList,\n group: labels.groupList,\n icon: '•',\n aliases: ['wuxu', '无序', '列表', 'bullet', 'ul'],\n order: 4,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleBulletList().run();\n },\n },\n {\n id: 'ordered-list',\n title: labels.orderedList,\n group: labels.groupList,\n icon: '1.',\n aliases: ['youxu', '有序', '列表', 'ordered', 'ol'],\n order: 5,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleOrderedList().run();\n },\n },\n {\n id: 'task-list',\n title: labels.taskList,\n group: labels.groupList,\n icon: '✓',\n aliases: ['renwu', '任务', '待办', 'task', 'todo', 'checklist'],\n order: 6,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleTaskList().run();\n },\n },\n {\n id: 'blockquote',\n title: labels.blockquote,\n group: labels.groupAdvanced,\n icon: '❝',\n aliases: ['yinyong', '引用', 'quote', 'blockquote'],\n order: 7,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleBlockquote().run();\n },\n },\n {\n id: 'details',\n title: labels.details,\n group: labels.groupAdvanced,\n icon: '▸',\n aliases: ['zhedie', '折叠', 'toggle', 'details', 'collapse'],\n order: 8,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).setDetails().run();\n },\n },\n {\n id: 'code-block',\n title: labels.codeBlock,\n group: labels.groupAdvanced,\n icon: '{ }',\n aliases: ['daima', '代码', 'code', 'fence'],\n order: 8,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleCodeBlock().run();\n },\n },\n {\n id: 'divider',\n title: labels.divider,\n group: labels.groupAdvanced,\n icon: '—',\n aliases: ['fenge', '分割线', '分隔线', 'hr', 'divider'],\n order: 9,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).setHorizontalRule().run();\n },\n },\n {\n id: 'table',\n title: labels.table,\n group: labels.groupAdvanced,\n icon: '▦',\n aliases: ['biaoge', '表格', 'table'],\n order: 10,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n .run();\n },\n },\n {\n id: 'inline-math',\n title: labels.inlineMath,\n group: labels.groupAdvanced,\n icon: '√x',\n aliases: ['gongshi', '公式', '行内公式', 'math', 'latex'],\n order: 11,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({ type: 'inlineMath', attrs: { latex: 'E=mc^2' } })\n .run();\n },\n },\n {\n id: 'block-math',\n title: labels.blockMath,\n group: labels.groupAdvanced,\n icon: '∑',\n aliases: ['gongshi', '公式', '块级公式', 'mathblock', 'latex'],\n order: 12,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({ type: 'blockMath', attrs: { latex: 'E=mc^2' } })\n .run();\n },\n },\n ];\n}\n\n/** 按 title / aliases 做小写 includes 过滤;空 query 返回全部。 */\nexport function filterSlashItems(\n items: SlashMenuItem[],\n query: string,\n): SlashMenuItem[] {\n const q = query.trim().toLowerCase();\n if (!q) return items;\n return items.filter(\n (item) =>\n item.title.toLowerCase().includes(q) ||\n item.aliases?.some((alias) => alias.toLowerCase().includes(q)),\n );\n}\n","import {\n Fragment,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useState,\n type ReactNode,\n} from 'react';\nimport styles from '../styles/slashMenu.module.css';\nimport type { SlashMenuItem } from './types';\n\nexport interface SlashMenuPopupHandle {\n /** 编辑器键盘事件经 IME 守卫后委托进来;返回 true 表示已消费。 */\n onKeyDown: (event: KeyboardEvent) => boolean;\n}\n\nexport interface SlashMenuPopupProps {\n items: SlashMenuItem[];\n query: string;\n menuLabel: string;\n command: (item: SlashMenuItem) => void;\n}\n\n/**\n * 把有序列表切成「相邻同名组」的节:返回 [组名 | null, 项][]。\n * 组首项的行携带组名(渲染为节标题 + 该项),其余行组名为 null。\n */\nfunction withGroupHeaders(\n items: SlashMenuItem[],\n): Array<[ReactNode, SlashMenuItem]> {\n const rows: Array<[ReactNode, SlashMenuItem]> = [];\n let lastGroup: string | undefined;\n for (const item of items) {\n if (item.group && item.group !== lastGroup) {\n rows.push([item.group, item]);\n lastGroup = item.group;\n } else {\n if (!item.group) lastGroup = undefined;\n rows.push([null, item]);\n }\n }\n return rows;\n}\n\n/**\n * 斜杠菜单弹层。样式对齐工具栏下拉菜单(radix 面板 + 文字字形图标 +\n * 品牌色高亮);定位由 @tiptap/suggestion 的 mount(floating-ui)接管。\n * 零匹配时渲染 null:弹层隐藏但 suggestion 保持激活,query 恢复命中时\n * 重新出现。\n */\nexport const SlashMenuPopup = forwardRef<\n SlashMenuPopupHandle,\n SlashMenuPopupProps\n>(function SlashMenuPopup({ items, query, menuLabel, command }, ref) {\n const [index, setIndex] = useState(0);\n\n // query 变化(过滤)后选区回到第一项\n useEffect(() => {\n setIndex(0);\n }, [query]);\n\n useImperativeHandle(\n ref,\n () => ({\n onKeyDown(event) {\n if (!items.length) return false;\n if (event.key === 'ArrowDown' || (event.key === 'Tab' && !event.shiftKey)) {\n setIndex((i) => (i + 1) % items.length);\n return true;\n }\n if (event.key === 'ArrowUp' || (event.key === 'Tab' && event.shiftKey)) {\n setIndex((i) => (i - 1 + items.length) % items.length);\n return true;\n }\n if (event.key === 'Enter') {\n const item = items[index];\n if (!item) return false;\n command(item);\n return true;\n }\n return false;\n },\n }),\n [items, index, command],\n );\n\n if (!items.length) return null;\n\n // 键盘索引按「可选项」计;分组头不占位\n let optionIndex = -1;\n\n return (\n <div className={styles.menu} role=\"listbox\" aria-label={menuLabel}>\n {withGroupHeaders(items).map(([header, item]) => {\n optionIndex += 1;\n const i = optionIndex;\n return (\n <Fragment key={item.id}>\n {header !== null ? (\n <div className={styles.groupLabel} aria-hidden=\"true\">\n {header}\n </div>\n ) : null}\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={i === index}\n title={item.title}\n className={i === index ? styles.itemActive : styles.item}\n // 键盘焦点留在编辑器里,↑↓/Enter 才能继续驱动弹层\n tabIndex={-1}\n onMouseEnter={() => setIndex(i)}\n onMouseDown={(e) => {\n // 用 mousedown 抢在编辑器 blur 之前执行,避免选区先被抖掉\n e.preventDefault();\n command(item);\n }}\n >\n {item.icon != null ? (\n <span className={styles.itemIcon} aria-hidden=\"true\">\n {item.icon}\n </span>\n ) : (\n <span className={styles.itemIcon} aria-hidden=\"true\" />\n )}\n <span className={styles.itemTitle}>{item.title}</span>\n </button>\n </Fragment>\n );\n })}\n </div>\n );\n});\n","import { Extension } from '@tiptap/core';\nimport { ReactRenderer } from '@tiptap/react';\nimport Suggestion, {\n type SuggestionKeyDownProps,\n type SuggestionProps,\n} from '@tiptap/suggestion';\nimport { PluginKey } from '@tiptap/pm/state';\nimport { getEditorBundle, getEditorLocale } from '../i18n/store';\nimport styles from '../styles/slashMenu.module.css';\nimport { createDefaultSlashMenuItems, filterSlashItems } from './items';\nimport { SlashMenuPopup, type SlashMenuPopupHandle } from './SlashMenuPopup';\nimport type { SlashMenuItem } from './types';\n\nexport interface SlashMenuOptions {\n /** 追加的宿主自定义命令项(排在默认项之后)。 */\n items?: SlashMenuItem[];\n}\n\n/** 输入法组合态下的按键交给 IME 自己处理(上游 suggestion 不查 composing)。 */\nfunction isComposing(event: KeyboardEvent, view: { composing: boolean }): boolean {\n return view.composing || event.isComposing || event.keyCode === 229;\n}\n\n/**\n * 斜杠菜单:键入 `/` 唤起块级插入弹窗。\n *\n * - 触发规则:行首或空白后(`allowedPrefixes`,空白含全角 U+3000);\n * 正文里「和/或」这类合法斜杠不会触发。\n * - 代码块内不触发;表格单元格内触发(见 allow)。\n * - 定位/滚动跟随/点外关闭全走官方 `props.mount`(floating-ui),\n * 弹层挂 document.body(逃出宿主 overflow 裁剪),`--tmr-*` 主题\n * 变量从编辑器解析后复制到弹层宿主。\n * - 只读态由 suggestion 状态机自身拦截(isEditable 短路)。\n */\nexport const SlashMenu = Extension.create<SlashMenuOptions>({\n name: 'slashMenu',\n\n addOptions() {\n return { items: undefined };\n },\n\n addProseMirrorPlugins() {\n const { items: extraItems } = this.options;\n\n let renderer: ReactRenderer<SlashMenuPopupHandle> | null = null;\n let unmountPopup: (() => void) | null = null;\n\n return [\n Suggestion<SlashMenuItem>({\n editor: this.editor,\n pluginKey: new PluginKey('tmrSlashMenu'),\n char: '/',\n // 行首(节点内起始,空 prefix 天然放行)或空白后触发\n allowedPrefixes: [' ', '\\t', '\\u3000'],\n allowSpaces: false,\n decorationClass: 'tmr-slash-suggestion',\n decorationEmptyClass: 'tmr-slash-suggestion-empty',\n items: ({ editor, query }) =>\n filterSlashItems(\n [\n ...createDefaultSlashMenuItems(getEditorLocale(editor)),\n ...(extraItems ?? []),\n ],\n query,\n ),\n command: ({ editor, range, props: item }) => {\n item.apply(editor, range);\n },\n allow: ({ editor }) => editor.isEditable && !editor.isActive('codeBlock'),\n render: () => ({\n onStart: (props: SuggestionProps<SlashMenuItem>) => {\n renderer = new ReactRenderer(\n SlashMenuPopup,\n {\n editor: props.editor,\n props: {\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).slashMenu.menuLabel,\n command: props.command,\n },\n className: styles.popupHost,\n },\n );\n // 挂到 body(suggestion 默认容器):弹层要能浮出编辑器,宿主在\n // 编辑器外层套 overflow:hidden(圆角窗口/滚动容器)时不会被裁。\n // 主题变量随行:编辑器子树上的 --tmr-* 解析值复制到弹层宿主,\n // 宿主把变量定义在任意深度的子树里也能保持配色一致。\n const pmStyle = getComputedStyle(props.editor.view.dom);\n for (const name of [\n '--tmr-text',\n '--tmr-accent',\n '--tmr-toolbar-border',\n '--tmr-toolbar-muted',\n ]) {\n const value = pmStyle.getPropertyValue(name);\n if (value) renderer.element.style.setProperty(name, value);\n }\n unmountPopup = props.mount(renderer.element);\n },\n onUpdate: (props: SuggestionProps<SlashMenuItem>) => {\n renderer?.updateProps({\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).slashMenu.menuLabel,\n command: props.command,\n });\n },\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (!renderer || isComposing(props.event, props.view)) return false;\n return renderer.ref?.onKeyDown(props.event) ?? false;\n },\n onExit: () => {\n unmountPopup?.();\n unmountPopup = null;\n renderer?.destroy();\n renderer = null;\n },\n }),\n }),\n ];\n },\n});\n","import { getLocaleBundle, type LocaleCode } from '../i18n';\n\n/**\n * 快捷键面板数据模型。keys 里的 'Mod' / 'Alt' 按平台渲染\n * (macOS: Cmd / Opt;其他: Ctrl / Alt)。\n * markdown.terminator 为触发终止键;无 terminator 的包裹型语法\n * (如 **x**)在键入收尾字符时即触发。\n */\nexport interface ShortcutEntry {\n id: string;\n /** 展示名(已本地化)。 */\n label: string;\n /** 键帽序列;不传表示无默认键位。 */\n keys?: string[];\n /** Markdown 触发写法;不传表示无对应 input rule。 */\n markdown?: {\n /** 触发片段,如 '**x**' 或 '#'。 */\n marker: string;\n /** 终止键(如 Space / Enter)。 */\n terminator?: string;\n };\n}\n\nexport interface ShortcutGroup {\n id: string;\n title: string;\n entries: ShortcutEntry[];\n}\n\n/**\n * 快捷键注册表。**每一条都必须真实**:键位来自所装 Tiptap 扩展的\n * addKeyboardShortcuts 默认值,markdown 触发来自对应 input rule\n * (上标/下标/链接在本库无默认键位与 input rule,故不列)。\n */\nexport function buildShortcutGroups(locale: LocaleCode = 'en'): ShortcutGroup[] {\n const labels = getLocaleBundle(locale).shortcut;\n return [\n {\n id: 'format',\n title: labels.formatGroup,\n entries: [\n { id: 'bold', label: labels.bold, keys: ['Mod', 'b'], markdown: { marker: '**x**' } },\n { id: 'italic', label: labels.italic, keys: ['Mod', 'i'], markdown: { marker: '*x*' } },\n { id: 'strike', label: labels.strike, keys: ['Mod', 'Shift', 's'], markdown: { marker: '~~x~~' } },\n { id: 'underline', label: labels.underline, keys: ['Mod', 'u'] },\n { id: 'inline-code', label: labels.inlineCode, keys: ['Mod', 'e'], markdown: { marker: '`x`' } },\n { id: 'highlight', label: labels.highlight, keys: ['Mod', 'Shift', 'h'], markdown: { marker: '==x==' } },\n { id: 'smart-quote', label: labels.smartQuote, markdown: { marker: '\"' } },\n { id: 'ellipsis', label: labels.ellipsis, markdown: { marker: '...' } },\n { id: 'em-dash', label: labels.emDash, markdown: { marker: '--' } },\n { id: 'align-left', label: labels.alignLeft, keys: ['Mod', 'Shift', 'l'] },\n { id: 'align-center', label: labels.alignCenter, keys: ['Mod', 'Shift', 'e'] },\n { id: 'align-right', label: labels.alignRight, keys: ['Mod', 'Shift', 'r'] },\n { id: 'align-justify', label: labels.alignJustify, keys: ['Mod', 'Shift', 'j'] },\n ...([1, 2, 3, 4, 5, 6] as const).map((level): ShortcutEntry => ({\n id: `heading-${level}`,\n label: labels.headingLabel(level),\n keys: ['Mod', 'Alt', String(level)],\n markdown: { marker: '#'.repeat(level), terminator: 'Space' },\n })),\n { id: 'paragraph', label: labels.paragraph, keys: ['Mod', 'Alt', '0'] },\n ],\n },\n {\n id: 'insert',\n title: labels.insertGroup,\n entries: [\n { id: 'image-upload', label: labels.imageUpload, keys: ['Mod', 'Shift', 'i'] },\n { id: 'blockquote', label: labels.blockquote, keys: ['Mod', 'Shift', 'b'], markdown: { marker: '>', terminator: 'Space' } },\n { id: 'bullet-list', label: labels.bulletList, keys: ['Mod', 'Shift', '8'], markdown: { marker: '-', terminator: 'Space' } },\n { id: 'ordered-list', label: labels.orderedList, keys: ['Mod', 'Shift', '7'], markdown: { marker: '1.', terminator: 'Space' } },\n { id: 'task-list', label: labels.taskList, markdown: { marker: '[]', terminator: 'Space' } },\n { id: 'code-block', label: labels.codeBlock, markdown: { marker: '```', terminator: 'Space' } },\n { id: 'divider', label: labels.divider, markdown: { marker: '---', terminator: 'Space' } },\n { id: 'emoji', label: labels.emoji, markdown: { marker: ':smile:' } },\n ],\n },\n {\n id: 'edit',\n title: labels.editGroup,\n entries: [\n { id: 'undo', label: labels.undo, keys: ['Mod', 'z'] },\n { id: 'redo', label: labels.redo, keys: ['Mod', 'Shift', 'z'] },\n { id: 'hard-break', label: labels.hardBreak, keys: ['Shift', 'Enter'] },\n { id: 'find-replace', label: labels.findReplace, keys: ['Mod', 'f'] },\n ],\n },\n ];\n}\n","import { useEffect, useMemo } from 'react';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport { XIcon } from '../icons';\nimport styles from '../styles/shortcutPanel.module.css';\nimport { buildShortcutGroups, type ShortcutGroup } from './types';\n\nexport interface ShortcutPanelProps {\n open: boolean;\n onClose: () => void;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n}\n\nfunction isMacPlatform(): boolean {\n if (typeof navigator === 'undefined') return false;\n return /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);\n}\n\n/** 键帽文案:Mod/Alt 按平台落名,单字母统一大写。 */\nfunction keycapText(key: string, mac: boolean): string {\n if (key === 'Mod') return mac ? 'Cmd' : 'Ctrl';\n if (key === 'Alt') return mac ? 'Opt' : 'Alt';\n if (/^[a-z]$/.test(key)) return key.toUpperCase();\n return key;\n}\n\n/**\n * 快捷键抽屉(语雀式):右侧滑出的三列对照表——格式 / 快捷键 / Markdown。\n * 数据来自 buildShortcutGroups(键位与 input rule 均为库内真实行为)。\n * 定位 fixed 贴视口右缘;Esc 关闭。\n */\nexport function ShortcutPanel({\n open,\n onClose,\n locale = 'en',\n}: ShortcutPanelProps) {\n const t = getLocaleBundle(locale).shortcut;\n\n // 打开期间 Esc 关闭\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [open, onClose]);\n\n const groups: ShortcutGroup[] = useMemo(\n () => (open ? buildShortcutGroups(locale) : []),\n [open, locale],\n );\n const mac = useMemo(isMacPlatform, []);\n\n if (!open) return null;\n\n return (\n <aside\n className={styles.drawer}\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label={t.panelTitle}\n >\n <button\n type=\"button\"\n className={styles.closeBtn}\n title={t.close}\n aria-label={t.close}\n onClick={onClose}\n >\n <XIcon size={16} />\n </button>\n <div className={styles.head}>\n <span className={styles.colFormat}>{t.colFormat}</span>\n <span className={styles.colShortcut}>{t.colShortcut}</span>\n <span className={styles.colMarkdown}>{t.colMarkdown}</span>\n </div>\n <div className={styles.body}>\n {groups.map((group) => (\n <section key={group.id} className={styles.group}>\n <h3 className={styles.groupTitle}>{group.title}</h3>\n {group.entries.map((entry) => (\n <div key={entry.id} className={styles.row}>\n <span className={styles.rowLabel}>{entry.label}</span>\n <span className={styles.rowKeys}>\n {entry.keys ? (\n entry.keys.map((key, i) => (\n <kbd key={i} className={styles.keycap}>\n {keycapText(key, mac)}\n </kbd>\n ))\n ) : (\n <span className={styles.none}>—</span>\n )}\n </span>\n <span className={styles.rowMd}>\n {entry.markdown ? (\n <>\n <kbd className={styles.keycap}>{entry.markdown.marker}</kbd>\n {entry.markdown.terminator ? (\n <kbd className={styles.keycap}>{entry.markdown.terminator}</kbd>\n ) : null}\n </>\n ) : (\n <span className={styles.none}>—</span>\n )}\n </span>\n </div>\n ))}\n </section>\n ))}\n </div>\n </aside>\n );\n}\n","// 副作用:增强 Editor.markdown(MarkdownManager)的类型。\nimport '@tiptap/markdown';\nimport { Extension, type Editor } from '@tiptap/core';\nimport { FileHandlePlugin } from '@tiptap/extension-file-handler';\nimport { PluginKey } from '@tiptap/pm/state';\n\n/** .md / .markdown 扩展名(不区分大小写)。 */\nconst MD_FILE_RE = /\\.(md|markdown)$/i;\n\n/**\n * .md 的 MIME 类型在各平台不稳定(text/markdown / text/x-markdown / 空串),\n * 而 FileHandlePlugin 的 allowedMimeTypes 是严格 `includes(file.type)` 匹配\n * (源码确认,不支持扩展名)。故列表包含空串兜住「系统未注册 MIME」的 .md,\n * 真正的判定在回调里按文件名做。副作用:拖入其它空 MIME 文件会被吞成 no-op\n * ——好过浏览器默认导航打开该文件。\n */\nconst MD_MIME_TYPES = ['text/markdown', 'text/x-markdown', ''];\n\n/** 读 .md 文件文本 → markdown.parse → 插入(drop 时插到落点,paste 时插到光标)。 */\nasync function insertMarkdownFiles(\n editor: Editor,\n files: File[],\n pos?: number,\n): Promise<void> {\n const mdFiles = files.filter((f) => MD_FILE_RE.test(f.name));\n if (mdFiles.length === 0) return;\n // markdown manager 仅在注册了 @tiptap/markdown 扩展后存在\n const markdown = editor.markdown;\n if (!markdown) return;\n const texts = await Promise.all(mdFiles.map((f) => f.text()));\n const json = markdown.parse(texts.join('\\n\\n'));\n if (pos != null) {\n editor.chain().focus().insertContentAt(pos, json).run();\n } else {\n editor.chain().focus().insertContent(json).run();\n }\n}\n\n/**\n * 把 .md / .markdown 文件拖拽或粘贴进编辑器,解析后插入内容。\n * 复用官方 @tiptap/extension-file-handler 的底层 FileHandlePlugin,但注册在\n * 自有 PluginKey 下——宿主仍可通过 extraExtensions 注入自己的 FileHandler\n * (如图片上传)而不撞 key。\n */\nexport const MarkdownFileDrop = Extension.create({\n name: 'markdownFileDrop',\n\n addProseMirrorPlugins() {\n const { editor } = this;\n return [\n FileHandlePlugin({\n key: new PluginKey('markdownFileDrop'),\n editor,\n allowedMimeTypes: MD_MIME_TYPES,\n onDrop: (ed, files, pos) => {\n insertMarkdownFiles(ed, files, pos).catch((err) => {\n console.error('markdown file drop failed:', err);\n });\n },\n onPaste: (ed, files) => {\n insertMarkdownFiles(ed, files).catch((err) => {\n console.error('markdown file paste failed:', err);\n });\n },\n }),\n ];\n },\n});\n","import { Extension } from '@tiptap/core';\nimport { skipTrailingNodeMeta } from '@tiptap/extensions';\nimport type { Node } from '@tiptap/pm/model';\nimport { Plugin, PluginKey, type EditorState } from '@tiptap/pm/state';\nimport { Decoration, DecorationSet } from '@tiptap/pm/view';\nimport type { Editor } from '@tiptap/react';\n\n/**\n * 「这段正在被 AI 改写」的高亮。\n *\n * 用 **decoration** 而不是 mark:两者都不会漏进 markdown,都能盖住表格,但 decoration\n * 不需要往 schema 里加标记、也不参与 undo 历史,而且**原子节点(图表 / 图片)也能盖**\n * ——mark 只作用于行内内容,盖不住 `atom: true` 的块级节点。\n *\n * 状态由宿主通过 `pendingAnchors` 推入(提交圈选改写时给上,回填或冲突时清空)。\n * 文档被编辑时装饰会跟着映射(在原文中间插字,高亮跟着变长;在前面插,整体后移)。\n */\nexport interface PendingAnchor {\n /** 宿主自己的标识(例如一次圈选请求的 request_id)。 */\n id: string;\n /** doc 坐标区间;通常是与选区相交的顶层块的首尾(见 getMarkdownForRange)。 */\n ranges: Array<{ from: number; to: number }>;\n}\n\nexport interface PendingAnchorState {\n decorations: DecorationSet;\n}\n\n/** 用 transaction meta 推入锚点:`tr.setMeta(PENDING_ANCHOR_META, anchors)`。 */\nexport const PENDING_ANCHOR_META = 'tmr:pendingAnchor';\n\nexport const pendingAnchorPluginKey = new PluginKey<PendingAnchorState>(\n 'tmrPendingAnchor',\n);\n\n/** 装饰上的 class;点击命中时宿主用 `[data-pending-id]` 取回 id。 */\nexport const PENDING_ANCHOR_CLASS = 'tmr-pending-anchor';\n\nexport const pendingAnchorExtension = Extension.create({\n name: 'pendingAnchor',\n\n addProseMirrorPlugins() {\n return [\n new Plugin<PendingAnchorState>({\n key: pendingAnchorPluginKey,\n state: {\n init: () => ({ decorations: DecorationSet.empty }),\n apply(tr, value) {\n const next = tr.getMeta(PENDING_ANCHOR_META) as\n | PendingAnchor[]\n | undefined;\n if (next) return { decorations: buildDecorations(tr.doc, next) };\n if (tr.docChanged) {\n return { decorations: value.decorations.map(tr.mapping, tr.doc) };\n }\n return value;\n },\n },\n props: {\n decorations(state: EditorState) {\n return pendingAnchorPluginKey.getState(state)?.decorations ?? null;\n },\n },\n }),\n ];\n },\n});\n\n/** 更新待改写高亮(传空数组即清空)。不进 undo 历史。 */\nexport function setPendingAnchors(editor: Editor, anchors: PendingAnchor[]): void {\n editor.view.dispatch(\n editor.state.tr\n .setMeta(PENDING_ANCHOR_META, anchors)\n // Tiptap 的 TrailingNode(StarterKit 自带)会在「第一个 transaction」时给文档末尾补一个\n // 空段落——只要文末是图表 / 表格 / 代码块这类节点。那是它本来的行为,但**不该由一次\n // 「加个高亮」触发**:那等于用户什么都没干、正文却多了个空段落(落库就变成结尾的 \\n\\n)。\n // 用官方 meta 跳过本次。\n .setMeta(skipTrailingNodeMeta, true)\n .setMeta('addToHistory', false),\n );\n}\n\nfunction buildDecorations(doc: Node, anchors: PendingAnchor[]): DecorationSet {\n const bounds: Array<{ from: number; to: number }> = [];\n doc.forEach((child, offset) => {\n bounds.push({ from: offset, to: offset + child.nodeSize });\n });\n\n const decorations: Decoration[] = [];\n for (const anchor of anchors) {\n for (const range of anchor.ranges) {\n const from = Math.max(0, Math.min(range.from, range.to));\n const to = Math.min(doc.content.size, Math.max(range.from, range.to));\n if (from >= to) continue;\n\n for (const bound of bounds) {\n if (bound.to <= from || bound.from >= to) continue;\n const attrs = {\n class: PENDING_ANCHOR_CLASS,\n 'data-pending-id': anchor.id,\n };\n if (from <= bound.from && to >= bound.to) {\n // 整块:用 node 装饰,表格 / 图表这类块级节点才盖得住\n decorations.push(Decoration.node(bound.from, bound.to, attrs));\n } else {\n // 块内的一部分(未扩块的选区):行内高亮\n decorations.push(\n Decoration.inline(Math.max(from, bound.from), Math.min(to, bound.to), attrs),\n );\n }\n }\n }\n }\n\n return decorations.length ? DecorationSet.create(doc, decorations) : DecorationSet.empty;\n}\n","import type { Selection } from '@tiptap/pm/state';\nimport { NodeSelection, TextSelection } from '@tiptap/pm/state';\nimport { CellSelection } from '@tiptap/pm/tables';\n\n/**\n * 选区类型——给宿主判断「用户选的是什么」用。\n *\n * - `text`:普通文字选区(含 AllSelection)\n * - `cell`:表格里的单元格选区(`CellSelection`,拖过表格时就是它)\n * - `node`:整节点选区(`NodeSelection`)。图表 / 图片 / 代码块这类 `atom` 节点只能这样被选中\n * ——它们在编辑器里渲染成图或块,用户没法用拖选文字的方式选中\n */\nexport type SelectionKind = 'text' | 'cell' | 'node';\n\nexport function selectionKind(selection: Selection): SelectionKind {\n if (selection instanceof CellSelection) return 'cell';\n if (selection instanceof NodeSelection) return 'node';\n if (selection instanceof TextSelection) return 'text';\n return 'text';\n}\n","// 副作用:增强 Editor.markdown(MarkdownManager)的类型。\nimport '@tiptap/markdown';\nimport { Extension } from '@tiptap/core';\nimport { Plugin, PluginKey } from '@tiptap/pm/state';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\n\n/**\n * 启发式判断一段纯文本是否「像 markdown」。\n * 只要命中任一块级/行内特征即认为是——宁可偶尔把带星号的普通文本转掉,\n * 也不漏掉真 markdown(用户可用 Shift+粘贴逃生)。\n */\nexport function looksLikeMarkdown(text: string): boolean {\n return (\n /^#{1,6}\\s/m.test(text) || // 标题\n /\\*\\*[^*\\n]+\\*\\*/.test(text) || // 加粗\n /(^|[^!])\\[[^\\]\\n]+\\]\\([^)\\n]+\\)/.test(text) || // 链接\n /!\\[[^\\]\\n]*\\]\\([^)\\n]+\\)/.test(text) || // 图片\n /^[-*+]\\s+\\S/m.test(text) || // 无序列表\n /^\\d+\\.\\s+\\S/m.test(text) || // 有序列表\n /^>\\s/m.test(text) || // 引用\n /^```/m.test(text) || // 代码围栏\n /^\\|.+\\|\\s*$/m.test(text) || // 表格行\n /^(-{3,}|\\*{3,})\\s*$/m.test(text) || // 分割线\n /<!--\\s*[\\[{][\\s\\S]*?\"chartType\"[\\s\\S]*?-->/.test(text) // 图表注释\n );\n}\n\n/**\n * 粘贴 markdown 纯文本自动转富文本(官方 @tiptap/markdown 不内置此能力,\n * 实现方式取自官方文档 \"Paste Markdown Detection\" 配方并调整边界)。\n *\n * 判定只看 text/plain 的内容,不看剪贴板是否带 text/html——很多来源\n * (Xcode/VS Code 等编辑器经 RTF/语法高亮 HTML)复制 markdown 源码时会\n * 附带一份仅是\"字面文本+样式\"的 HTML,按 HTML 标签判定来源不可枚举。\n * 而真富文本(Word/网页文章)的 text/plain 降级不含 markdown 符号,\n * 启发式天然不命中,仍走默认富文本粘贴。\n *\n * 放行(不转换)的边界:\n * - Shift+粘贴(粘贴为纯文本)→ 保留逃生通道\n * - 光标在代码块内 → markdown 源码原样贴入\n * - looksLikeMarkdown 未命中 → 默认粘贴\n */\nexport const MarkdownPaste = Extension.create({\n name: 'markdownPaste',\n\n addProseMirrorPlugins() {\n const { editor } = this;\n return [\n new Plugin({\n key: new PluginKey('markdownPaste'),\n props: {\n handlePaste(view, event) {\n const clipboard = event.clipboardData;\n if (!clipboard) return false;\n const text = clipboard.getData('text/plain');\n if (!text) return false;\n // Shift+粘贴 = 用户显式要纯文本(shiftKey 是 prosemirror-view\n // 内部状态,无公开 API,容错访问;拿不到则照常转换)\n const input = (view as unknown as { input?: { shiftKey?: boolean } })\n .input;\n if (input?.shiftKey) return false;\n // 代码块内粘贴 markdown 源码是常见操作,不转换\n if (view.state.selection.$from.parent.type.spec.code) return false;\n if (!looksLikeMarkdown(text)) return false;\n // markdown manager 仅在注册了 @tiptap/markdown 扩展后存在\n const markdown = editor.markdown;\n if (!markdown) return false;\n try {\n // 与 insertMarkdown / 初始 content 一致:先把\n // `<!-- chartType -->` + GFM 表收成 ```tmr-chart,否则\n // TipTap 会丢掉 HTML 注释,只剩普通表格。\n const json = markdown.parse(prepareChartMarkdown(text));\n editor.commands.insertContent(json);\n return true;\n } catch (err) {\n console.error('markdown paste parse failed:', err);\n return false; // 解析失败回退默认纯文本粘贴\n }\n },\n },\n }),\n ];\n },\n});\n","'use client';\n\nimport type { Chart as ChartJs } from 'chart.js';\nimport {\n NodeViewWrapper,\n type NodeViewProps,\n} from '@tiptap/react';\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type MouseEvent,\n} from 'react';\nimport { destroyChart, renderOrUpdateChart } from '../chart/renderChart';\nimport type { ChartPayload, ChartTheme } from '../chart/types';\nimport { useEditorBundle } from '../i18n/store';\nimport '../styles/chart.css';\n\nfunction tabLabel(\n chartType: string,\n title: string | undefined,\n index: number,\n fallback: (type: string, i: number) => string,\n): string {\n if (title?.trim()) return title;\n return fallback(chartType, index);\n}\n\n/**\n * Chart NodeView: render + NodeSelection (like Image).\n * Config editing is intentionally disabled for now (no modal).\n */\nexport function ChartView({\n node,\n editor,\n extension,\n selected,\n getPos,\n}: NodeViewProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const chartRef = useRef<ChartJs | null>(null);\n const [active, setActive] = useState(0);\n const labels = useEditorBundle(editor).chart;\n\n const payload: ChartPayload = useMemo(\n () => ({\n config: (node.attrs.config ?? []) as ChartPayload['config'],\n columns: (node.attrs.columns ?? []) as ChartPayload['columns'],\n dataSource: (node.attrs.dataSource ??\n []) as ChartPayload['dataSource'],\n }),\n [node.attrs.config, node.attrs.columns, node.attrs.dataSource],\n );\n\n const theme = (extension.options.chartTheme ?? null) as ChartTheme | null;\n const configs = payload.config;\n const safeIndex = Math.min(active, Math.max(0, configs.length - 1));\n const config = configs[safeIndex];\n const height = config?.height ?? 320;\n const canSelect = Boolean(editor?.isEditable);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas || !config) return;\n chartRef.current = renderOrUpdateChart({\n canvas,\n config,\n payload,\n theme,\n chart: chartRef.current,\n });\n }, [config, payload, theme]);\n\n useEffect(() => {\n return () => {\n destroyChart(chartRef.current);\n chartRef.current = null;\n };\n }, []);\n\n // When active tab changes, recreate chart (type may differ)\n useEffect(() => {\n destroyChart(chartRef.current);\n chartRef.current = null;\n const canvas = canvasRef.current;\n if (!canvas || !config) return;\n chartRef.current = renderOrUpdateChart({\n canvas,\n config,\n payload,\n theme,\n chart: null,\n });\n }, [safeIndex]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const selectChart = () => {\n if (!editor || !canSelect) return;\n const pos = getPos();\n if (typeof pos !== 'number') return;\n editor.commands.setNodeSelection(pos);\n };\n\n return (\n <NodeViewWrapper\n as=\"div\"\n className={`tmr-chart${canSelect ? ' tmr-chart--interactive' : ''}${\n selected ? ' ProseMirror-selectednode' : ''\n }`}\n data-type=\"chart\"\n data-selected={selected ? 'true' : undefined}\n contentEditable={false}\n onMouseDown={(e: MouseEvent) => {\n if ((e.target as HTMLElement).closest('button')) return;\n if (e.button !== 0) return;\n // Select node but do not preventDefault — Chart.js needs hover/click.\n selectChart();\n }}\n >\n {config?.title ? (\n <div className=\"tmr-chart-title\">{config.title}</div>\n ) : null}\n {config?.dataTime ? (\n <div className=\"tmr-chart-time\">{config.dataTime}</div>\n ) : null}\n {configs.length > 1 ? (\n <div className=\"tmr-chart-tabs\" role=\"tablist\">\n {configs.map((c, i) => (\n <button\n key={`${c.chartType}-${i}`}\n type=\"button\"\n role=\"tab\"\n className=\"tmr-chart-tab\"\n data-active={i === safeIndex ? 'true' : 'false'}\n aria-selected={i === safeIndex}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => {\n e.stopPropagation();\n setActive(i);\n }}\n >\n {tabLabel(c.chartType, c.title, i, labels.tabLabel)}\n </button>\n ))}\n </div>\n ) : null}\n <div className=\"tmr-chart-canvas-host\" style={{ height }}>\n <canvas ref={canvasRef} />\n </div>\n </NodeViewWrapper>\n );\n}\n","import { ReactNodeViewRenderer } from '@tiptap/react';\nimport { reportChart, type ReportChartOptions } from './ChartExtension';\nimport { ChartView } from '../components/ChartView';\nimport type { ChartTheme } from './types';\n\nexport interface CreateChartOptions {\n /** When true, reserved for future in-place chart editing. Currently unused (no edit UI). Default false. */\n editable?: boolean;\n chartTheme?: ChartTheme | null;\n}\n\nexport interface InteractiveChartOptions extends ReportChartOptions {\n chartEditable: boolean;\n chartTheme: ChartTheme | null;\n}\n\n/**\n * Client chart extension with React NodeView.\n * Server / pure HTML paths should use {@link reportChart} / {@link pureChart}.\n */\nexport function createChart(options: CreateChartOptions = {}) {\n // Chart config editing (modal) is disabled for now; keep option for later.\n const { editable = false, chartTheme = null } = options;\n return reportChart.extend<InteractiveChartOptions>({\n addOptions() {\n return {\n HTMLAttributes: {},\n chartEditable: editable,\n chartTheme,\n };\n },\n addNodeView() {\n return ReactNodeViewRenderer(ChartView);\n },\n });\n}\n","'use client';\n\nimport * as DropdownMenu from '@radix-ui/react-dropdown-menu';\nimport {\n NodeViewContent,\n NodeViewWrapper,\n type NodeViewProps,\n} from '@tiptap/react';\nimport { useEffect, useRef, useState } from 'react';\nimport {\n CheckIcon,\n ChevronDownIcon,\n ChevronsUpDownIcon,\n CopyIcon,\n TrashIcon,\n} from '../icons';\nimport { useEditorBundle } from '../i18n/store';\nimport styles from '../styles/codeBlock.module.css';\n\n// 语言选择可选项。空 value = Auto-detect(交给 lowlight 自动识别)\nconst LANGUAGES: { value: string; label: string }[] = [\n { value: '', label: 'Auto-detect' },\n { value: 'javascript', label: 'JavaScript' },\n { value: 'typescript', label: 'TypeScript' },\n { value: 'python', label: 'Python' },\n { value: 'java', label: 'Java' },\n { value: 'go', label: 'Go' },\n { value: 'rust', label: 'Rust' },\n { value: 'c', label: 'C' },\n { value: 'cpp', label: 'C++' },\n { value: 'csharp', label: 'C#' },\n { value: 'sql', label: 'SQL' },\n { value: 'json', label: 'JSON' },\n { value: 'yaml', label: 'YAML' },\n { value: 'bash', label: 'Bash' },\n { value: 'shell', label: 'Shell' },\n { value: 'html', label: 'HTML' },\n { value: 'css', label: 'CSS' },\n { value: 'markdown', label: 'Markdown' },\n];\n\n/**\n * 代码块自定义 NodeView:右上角语言选择器(Radix DropdownMenu,支持方向键导航)+ 删除。\n * 文案从编辑器的 locale 读取,缺省英文。\n *\n * 只读态(预览 / 只读编辑器)走另一套头部:左侧语言标签,右侧复制 + 展开收起;\n * 超出 `--tmr-code-max-height`(缺省 480px)的代码块默认限高块内滚动,行号槽\n * 用 MutationObserver 跟踪 PM 管理的 code 内容保持同步(纯文本变化不触发 React\n * 重渲染,gutter 行数靠观察器自刷新)。\n */\nexport function CodeBlockView({\n node,\n updateAttributes,\n deleteNode,\n editor,\n}: NodeViewProps) {\n const labels = useEditorBundle(editor).codeBlock;\n const language = (node.attrs.language as string) || '';\n const current = LANGUAGES.find((l) => l.value === language);\n const currentLabel = current\n ? current.value === ''\n ? labels.autoDetect\n : current.label\n : labels.autoDetect;\n const editable = editor.isEditable;\n const readonly = !editable;\n\n const [copied, setCopied] = useState(false);\n const [folded, setFolded] = useState(false);\n const [lineCount, setLineCount] = useState(() =>\n Math.max((node.textContent ?? '').split('\\n').length, 1),\n );\n const preRef = useRef<HTMLPreElement | null>(null);\n\n // 流式更新只改 code 里的文本节点,React 不重渲染;观察 pre 子树同步行数。\n useEffect(() => {\n if (!readonly) return;\n const pre = preRef.current;\n if (!pre) return;\n const update = () => {\n setLineCount(Math.max((pre.textContent ?? '').split('\\n').length, 1));\n };\n update();\n const mo = new MutationObserver(update);\n mo.observe(pre, { childList: true, subtree: true, characterData: true });\n return () => mo.disconnect();\n }, [readonly]);\n\n const handleCopy = () => {\n const text = node.textContent ?? '';\n const done = () => {\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n };\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).then(done, () => {});\n return;\n }\n // 剪贴板 API 不可用(非安全上下文 / 旧 webview):退化为 execCommand\n const ta = document.createElement('textarea');\n ta.value = text;\n document.body.appendChild(ta);\n ta.select();\n try {\n document.execCommand('copy');\n done();\n } catch {\n // 复制失败静默\n }\n ta.remove();\n };\n\n const wrapperClass = [\n styles.wrapper,\n readonly ? styles.readonly : '',\n folded ? styles.folded : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <NodeViewWrapper className={wrapperClass}>\n {editable ? (\n <div className={styles.header} contentEditable={false}>\n <DropdownMenu.Root>\n <DropdownMenu.Trigger asChild>\n <button\n type=\"button\"\n className={styles.langTrigger}\n aria-label={labels.language}\n >\n <span>{currentLabel}</span>\n <ChevronDownIcon size={14} className={styles.langChevron} />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Portal>\n <DropdownMenu.Content\n className={styles.langMenu}\n sideOffset={4}\n align=\"end\"\n >\n {LANGUAGES.map((l) => {\n const optionLabel =\n l.value === '' ? labels.autoDetect : l.label;\n const selected = l.value === language;\n return (\n <DropdownMenu.Item\n key={l.value}\n className={`${styles.langItem}${selected ? ` ${styles.langItemSelected}` : ''}`}\n onSelect={() => updateAttributes({ language: l.value })}\n >\n <span className={styles.langItemCheck}>\n {selected ? <CheckIcon size={14} /> : null}\n </span>\n {optionLabel}\n </DropdownMenu.Item>\n );\n })}\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n <button\n type=\"button\"\n className={styles.deleteBtn}\n title={labels.delete}\n aria-label={labels.delete}\n onClick={() => deleteNode()}\n >\n <TrashIcon />\n </button>\n </div>\n ) : (\n // 只读头部:左侧语言标签,右侧复制 + 展开收起(展开后按钮保留,供收回)\n <div className={styles.readonlyHeader} contentEditable={false}>\n <span className={styles.langBadge}>\n <span className={styles.langBraces}>{'{}'}</span>\n {currentLabel}\n </span>\n <span className={styles.readonlyActions}>\n <button\n type=\"button\"\n className={styles.actionBtn}\n onClick={handleCopy}\n aria-label={labels.copy}\n title={labels.copy}\n >\n {copied ? <CheckIcon size={14} /> : <CopyIcon size={14} />}\n </button>\n <button\n type=\"button\"\n className={styles.actionBtn}\n onClick={() => setFolded((v) => !v)}\n aria-label={folded ? labels.expand : labels.collapse}\n title={folded ? labels.expand : labels.collapse}\n aria-expanded={!folded}\n >\n <ChevronsUpDownIcon size={14} />\n </button>\n </span>\n </div>\n )}\n <pre ref={preRef} className={styles.pre}>\n {readonly ? (\n <span\n className={styles.gutter}\n contentEditable={false}\n aria-hidden=\"true\"\n >\n {Array.from({ length: lineCount }, (_, i) => (\n <span key={i} className={styles.gutterLine}>\n {i + 1}\n </span>\n ))}\n </span>\n ) : null}\n {/* code 标签是代码块标准内容容器;v3 类型未含 'code',运行时有效,故断言 */}\n <NodeViewContent as={'code' as 'div'} />\n </pre>\n </NodeViewWrapper>\n );\n}\n","/**\n * Custom positioning reference element.\n * @see https://floating-ui.com/docs/virtual-elements\n */\n\nconst sides = ['top', 'right', 'bottom', 'left'];\nconst alignments = ['start', 'end'];\nconst placements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + \"-\" + alignments[0], side + \"-\" + alignments[1]), []);\nconst min = Math.min;\nconst max = Math.max;\nconst round = Math.round;\nconst floor = Math.floor;\nconst createCoords = v => ({\n x: v,\n y: v\n});\nconst oppositeSideMap = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nfunction clamp(start, value, end) {\n return max(start, min(value, end));\n}\nfunction evaluate(value, param) {\n return typeof value === 'function' ? value(param) : value;\n}\nfunction getSide(placement) {\n return placement.split('-')[0];\n}\nfunction getAlignment(placement) {\n return placement.split('-')[1];\n}\nfunction getOppositeAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}\nfunction getAxisLength(axis) {\n return axis === 'y' ? 'height' : 'width';\n}\nfunction getSideAxis(placement) {\n const firstChar = placement[0];\n return firstChar === 't' || firstChar === 'b' ? 'y' : 'x';\n}\nfunction getAlignmentAxis(placement) {\n return getOppositeAxis(getSideAxis(placement));\n}\nfunction getAlignmentSides(placement, rects, rtl) {\n if (rtl === void 0) {\n rtl = false;\n }\n const alignment = getAlignment(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const length = getAxisLength(alignmentAxis);\n let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';\n if (rects.reference[length] > rects.floating[length]) {\n mainAlignmentSide = getOppositePlacement(mainAlignmentSide);\n }\n return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];\n}\nfunction getExpandedPlacements(placement) {\n const oppositePlacement = getOppositePlacement(placement);\n return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];\n}\nfunction getOppositeAlignmentPlacement(placement) {\n return placement.includes('start') ? placement.replace('start', 'end') : placement.replace('end', 'start');\n}\nconst lrPlacement = ['left', 'right'];\nconst rlPlacement = ['right', 'left'];\nconst tbPlacement = ['top', 'bottom'];\nconst btPlacement = ['bottom', 'top'];\nfunction getSideList(side, isStart, rtl) {\n switch (side) {\n case 'top':\n case 'bottom':\n if (rtl) return isStart ? rlPlacement : lrPlacement;\n return isStart ? lrPlacement : rlPlacement;\n case 'left':\n case 'right':\n return isStart ? tbPlacement : btPlacement;\n default:\n return [];\n }\n}\nfunction getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {\n const alignment = getAlignment(placement);\n let list = getSideList(getSide(placement), direction === 'start', rtl);\n if (alignment) {\n list = list.map(side => side + \"-\" + alignment);\n if (flipAlignment) {\n list = list.concat(list.map(getOppositeAlignmentPlacement));\n }\n }\n return list;\n}\nfunction getOppositePlacement(placement) {\n const side = getSide(placement);\n return oppositeSideMap[side] + placement.slice(side.length);\n}\nfunction expandPaddingObject(padding) {\n var _padding$top, _padding$right, _padding$bottom, _padding$left;\n return {\n top: (_padding$top = padding.top) != null ? _padding$top : 0,\n right: (_padding$right = padding.right) != null ? _padding$right : 0,\n bottom: (_padding$bottom = padding.bottom) != null ? _padding$bottom : 0,\n left: (_padding$left = padding.left) != null ? _padding$left : 0\n };\n}\nfunction getPaddingObject(padding) {\n return typeof padding !== 'number' ? expandPaddingObject(padding) : {\n top: padding,\n right: padding,\n bottom: padding,\n left: padding\n };\n}\nfunction rectToClientRect(rect) {\n const {\n x,\n y,\n width,\n height\n } = rect;\n return {\n width,\n height,\n top: y,\n left: x,\n right: x + width,\n bottom: y + height,\n x,\n y\n };\n}\n\nexport { alignments, clamp, createCoords, evaluate, expandPaddingObject, floor, getAlignment, getAlignmentAxis, getAlignmentSides, getAxisLength, getExpandedPlacements, getOppositeAlignmentPlacement, getOppositeAxis, getOppositeAxisPlacements, getOppositePlacement, getPaddingObject, getSide, getSideAxis, max, min, placements, rectToClientRect, round, sides };\n","import { getSideAxis, getAlignmentAxis, getAxisLength, getSide, getAlignment, evaluate, getPaddingObject, rectToClientRect, min, clamp, placements, getAlignmentSides, getOppositeAlignmentPlacement, getOppositePlacement, getExpandedPlacements, getOppositeAxisPlacements, sides, max, getOppositeAxis } from '@floating-ui/utils';\nexport { rectToClientRect } from '@floating-ui/utils';\n\nfunction computeCoordsFromPlacement(_ref, placement, rtl) {\n let {\n reference,\n floating\n } = _ref;\n const sideAxis = getSideAxis(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const alignLength = getAxisLength(alignmentAxis);\n const side = getSide(placement);\n const isVertical = sideAxis === 'y';\n const commonX = reference.x + reference.width / 2 - floating.width / 2;\n const commonY = reference.y + reference.height / 2 - floating.height / 2;\n const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;\n let coords;\n switch (side) {\n case 'top':\n coords = {\n x: commonX,\n y: reference.y - floating.height\n };\n break;\n case 'bottom':\n coords = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n case 'right':\n coords = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n case 'left':\n coords = {\n x: reference.x - floating.width,\n y: commonY\n };\n break;\n default:\n coords = {\n x: reference.x,\n y: reference.y\n };\n }\n const alignment = getAlignment(placement);\n if (alignment) {\n coords[alignmentAxis] += commonAlign * (alignment === 'end' ? 1 : -1) * (rtl && isVertical ? -1 : 1);\n }\n return coords;\n}\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nasync function detectOverflow(state, options) {\n var _await$platform$isEle;\n if (options === void 0) {\n options = {};\n }\n const {\n x,\n y,\n platform,\n rects,\n elements,\n strategy\n } = state;\n const {\n boundary = 'clippingAncestors',\n rootBoundary = 'viewport',\n elementContext = 'floating',\n altBoundary = false,\n padding = 0\n } = evaluate(options, state);\n const paddingObject = getPaddingObject(padding);\n const altContext = elementContext === 'floating' ? 'reference' : 'floating';\n const element = elements[altBoundary ? altContext : elementContext];\n const clippingClientRect = rectToClientRect(await platform.getClippingRect({\n element: ((_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),\n boundary,\n rootBoundary,\n strategy\n }));\n const rect = elementContext === 'floating' ? {\n x,\n y,\n width: rects.floating.width,\n height: rects.floating.height\n } : rects.reference;\n const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));\n const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) && (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {\n x: 1,\n y: 1\n };\n const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({\n elements,\n rect,\n offsetParent,\n strategy\n }) : rect);\n return {\n top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,\n bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,\n left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,\n right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x\n };\n}\n\n// Maximum number of resets that can occur before bailing to avoid infinite reset loops.\nconst MAX_RESET_COUNT = 50;\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n *\n * This export does not have any `platform` interface logic. You will need to\n * write one for the platform you are using Floating UI with.\n */\nconst computePosition = async (reference, floating, config) => {\n const {\n placement = 'bottom',\n strategy = 'absolute',\n middleware = [],\n platform\n } = config;\n const platformWithDetectOverflow = platform.detectOverflow ? platform : {\n ...platform,\n detectOverflow\n };\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));\n let rects = await platform.getElementRects({\n reference,\n floating,\n strategy\n });\n let {\n x,\n y\n } = computeCoordsFromPlacement(rects, placement, rtl);\n let statefulPlacement = placement;\n let resetCount = 0;\n const middlewareData = {};\n for (let i = 0; i < middleware.length; i++) {\n const currentMiddleware = middleware[i];\n if (!currentMiddleware) {\n continue;\n }\n const {\n name,\n fn\n } = currentMiddleware;\n const {\n x: nextX,\n y: nextY,\n data,\n reset\n } = await fn({\n x,\n y,\n initialPlacement: placement,\n placement: statefulPlacement,\n strategy,\n middlewareData,\n rects,\n platform: platformWithDetectOverflow,\n elements: {\n reference,\n floating\n }\n });\n x = nextX != null ? nextX : x;\n y = nextY != null ? nextY : y;\n middlewareData[name] = {\n ...middlewareData[name],\n ...data\n };\n if (reset && resetCount < MAX_RESET_COUNT) {\n resetCount++;\n if (typeof reset === 'object') {\n if (reset.placement) {\n statefulPlacement = reset.placement;\n }\n if (reset.rects) {\n rects = reset.rects === true ? await platform.getElementRects({\n reference,\n floating,\n strategy\n }) : reset.rects;\n }\n ({\n x,\n y\n } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));\n }\n i = -1;\n }\n }\n return {\n x,\n y,\n placement: statefulPlacement,\n strategy,\n middlewareData\n };\n};\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = options => ({\n name: 'arrow',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement,\n rects,\n platform,\n elements,\n middlewareData\n } = state;\n // Since `element` is required, we don't Partial<> the type.\n const {\n element,\n padding = 0\n } = evaluate(options, state) || {};\n if (element == null) {\n return {};\n }\n const paddingObject = getPaddingObject(padding);\n const coords = {\n x,\n y\n };\n const axis = getAlignmentAxis(placement);\n const length = getAxisLength(axis);\n const arrowDimensions = await platform.getDimensions(element);\n const isYAxis = axis === 'y';\n const minProp = isYAxis ? 'top' : 'left';\n const maxProp = isYAxis ? 'bottom' : 'right';\n const clientProp = isYAxis ? 'clientHeight' : 'clientWidth';\n const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];\n const startDiff = coords[axis] - rects.reference[axis];\n const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));\n let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0;\n\n // DOM platform can return `window` as the `offsetParent`.\n if (!clientSize || !(await (platform.isElement == null ? void 0 : platform.isElement(arrowOffsetParent)))) {\n clientSize = elements.floating[clientProp] || rects.floating[length];\n }\n const centerToReference = endDiff / 2 - startDiff / 2;\n\n // If the padding is large enough that it causes the arrow to no longer be\n // centered, modify the padding so that it is centered.\n const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;\n const minPadding = min(paddingObject[minProp], largestPossiblePadding);\n const maxPadding = min(paddingObject[maxProp], largestPossiblePadding);\n\n // Make sure the arrow doesn't overflow the floating element if the center\n // point is outside the floating element's bounds.\n const max = clientSize - arrowDimensions[length] - maxPadding;\n const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;\n const offset = clamp(minPadding, center, max);\n\n // If the reference is small enough that the arrow's padding causes it to\n // to point to nothing for an aligned placement, adjust the offset of the\n // floating element itself. To ensure `shift()` continues to take action,\n // a single reset is performed when this is true.\n const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < minPadding ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;\n const alignmentOffset = shouldAddOffset ? center < minPadding ? center - minPadding : center - max : 0;\n return {\n [axis]: coords[axis] + alignmentOffset,\n data: {\n [axis]: offset,\n centerOffset: center - offset - alignmentOffset,\n ...(shouldAddOffset && {\n alignmentOffset\n })\n },\n reset: shouldAddOffset\n };\n }\n});\n\nfunction getPlacementList(alignment, autoAlignment, allowedPlacements) {\n const allowedPlacementsSortedByAlignment = alignment ? [...allowedPlacements.filter(placement => getAlignment(placement) === alignment), ...allowedPlacements.filter(placement => getAlignment(placement) !== alignment)] : allowedPlacements.filter(placement => getSide(placement) === placement);\n return allowedPlacementsSortedByAlignment.filter(placement => {\n if (alignment) {\n return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);\n }\n return true;\n });\n}\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'autoPlacement',\n options,\n async fn(state) {\n var _middlewareData$autoP, _middlewareData$autoP2, _placementsThatFitOnE;\n const {\n rects,\n middlewareData,\n placement,\n platform,\n elements\n } = state;\n const {\n crossAxis = false,\n alignment,\n allowedPlacements = placements,\n autoAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n const placements$1 = alignment !== undefined || allowedPlacements === placements ? getPlacementList(alignment || null, autoAlignment, allowedPlacements) : allowedPlacements;\n const currentIndex = ((_middlewareData$autoP = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP.index) || 0;\n const currentPlacement = placements$1[currentIndex];\n if (currentPlacement == null) {\n return {};\n }\n\n // Make `computeCoords` start from the right place.\n if (placement !== currentPlacement) {\n return {\n reset: {\n placement: placements$1[0]\n }\n };\n }\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const alignmentSides = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));\n const currentOverflows = [overflow[getSide(currentPlacement)], overflow[alignmentSides[0]], overflow[alignmentSides[1]]];\n const allOverflows = [...(((_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.overflows) || []), {\n placement: currentPlacement,\n overflows: currentOverflows\n }];\n const nextPlacement = placements$1[currentIndex + 1];\n\n // There are more placements to check.\n if (nextPlacement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n const placementsSortedByMostSpace = allOverflows.map(d => {\n const alignment = getAlignment(d.placement);\n return [d.placement, alignment && crossAxis ?\n // Check along the mainAxis and main crossAxis side.\n d.overflows.slice(0, 2).reduce((acc, v) => acc + v, 0) :\n // Check only the mainAxis.\n d.overflows[0], d.overflows];\n }).sort((a, b) => a[1] - b[1]);\n const placementsThatFitOnEachSide = placementsSortedByMostSpace.filter(d => d[2].slice(0,\n // Aligned placements should not check their opposite crossAxis\n // side.\n getAlignment(d[0]) ? 2 : 3).every(v => v <= 0));\n const resetPlacement = ((_placementsThatFitOnE = placementsThatFitOnEachSide[0]) == null ? void 0 : _placementsThatFitOnE[0]) || placementsSortedByMostSpace[0][0];\n if (resetPlacement !== placement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: resetPlacement\n }\n };\n }\n return {};\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'flip',\n options,\n async fn(state) {\n var _middlewareData$arrow, _middlewareData$flip;\n const {\n placement,\n middlewareData,\n rects,\n initialPlacement,\n platform,\n elements\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true,\n fallbackPlacements: specifiedFallbackPlacements,\n fallbackStrategy = 'bestFit',\n fallbackAxisSideDirection = 'none',\n flipAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n\n // If a reset by the arrow was caused due to an alignment offset being\n // added, we should skip any logic now since `flip()` has already done its\n // work.\n // https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643\n if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n const side = getSide(placement);\n const initialSideAxis = getSideAxis(initialPlacement);\n const isBasePlacement = getSide(initialPlacement) === initialPlacement;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));\n const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== 'none';\n if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {\n fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));\n }\n const placements = [initialPlacement, ...fallbackPlacements];\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const overflows = [];\n let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];\n if (checkMainAxis) {\n overflows.push(overflow[side]);\n }\n if (checkCrossAxis) {\n const sides = getAlignmentSides(placement, rects, rtl);\n overflows.push(overflow[sides[0]], overflow[sides[1]]);\n }\n overflowsData = [...overflowsData, {\n placement,\n overflows\n }];\n\n // One or more sides is overflowing.\n if (!overflows.every(side => side <= 0)) {\n var _middlewareData$flip2, _overflowsData$filter;\n const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;\n const nextPlacement = placements[nextIndex];\n if (nextPlacement) {\n const ignoreCrossAxisOverflow = checkCrossAxis === 'alignment' ? initialSideAxis !== getSideAxis(nextPlacement) : false;\n if (!ignoreCrossAxisOverflow ||\n // We leave the current main axis only if every placement on that axis\n // overflows the main axis.\n overflowsData.every(d => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) {\n // Try next placement and re-run the lifecycle.\n return {\n data: {\n index: nextIndex,\n overflows: overflowsData\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n }\n\n // First, find the candidates that fit on the mainAxis side of overflow,\n // then find the placement that fits the best on the main crossAxis side.\n let resetPlacement = (_overflowsData$filter = overflowsData.filter(d => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;\n\n // Otherwise fallback.\n if (!resetPlacement) {\n switch (fallbackStrategy) {\n case 'bestFit':\n {\n var _overflowsData$filter2;\n const placement = (_overflowsData$filter2 = overflowsData.filter(d => {\n if (hasFallbackAxisSideDirection) {\n const currentSideAxis = getSideAxis(d.placement);\n return currentSideAxis === initialSideAxis ||\n // Create a bias to the `y` side axis due to horizontal\n // reading directions favoring greater width.\n currentSideAxis === 'y';\n }\n return true;\n }).map(d => [d.placement, d.overflows.filter(overflow => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$filter2[0];\n if (placement) {\n resetPlacement = placement;\n }\n break;\n }\n case 'initialPlacement':\n resetPlacement = initialPlacement;\n break;\n }\n }\n if (placement !== resetPlacement) {\n return {\n reset: {\n placement: resetPlacement\n }\n };\n }\n }\n return {};\n }\n };\n};\n\nfunction getSideOffsets(overflow, rect) {\n return {\n top: overflow.top - rect.height,\n right: overflow.right - rect.width,\n bottom: overflow.bottom - rect.height,\n left: overflow.left - rect.width\n };\n}\nfunction isAnySideFullyClipped(overflow) {\n return sides.some(side => overflow[side] >= 0);\n}\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'hide',\n options,\n async fn(state) {\n const {\n rects,\n platform\n } = state;\n const {\n strategy = 'referenceHidden',\n ...detectOverflowOptions\n } = evaluate(options, state);\n switch (strategy) {\n case 'referenceHidden':\n {\n const overflow = await platform.detectOverflow(state, {\n ...detectOverflowOptions,\n elementContext: 'reference'\n });\n const offsets = getSideOffsets(overflow, rects.reference);\n return {\n data: {\n referenceHiddenOffsets: offsets,\n referenceHidden: isAnySideFullyClipped(offsets)\n }\n };\n }\n case 'escaped':\n {\n const overflow = await platform.detectOverflow(state, {\n ...detectOverflowOptions,\n altBoundary: true\n });\n const offsets = getSideOffsets(overflow, rects.floating);\n return {\n data: {\n escapedOffsets: offsets,\n escaped: isAnySideFullyClipped(offsets)\n }\n };\n }\n default:\n {\n return {};\n }\n }\n }\n };\n};\n\nfunction getBoundingRect(rects) {\n const minX = min(...rects.map(rect => rect.left));\n const minY = min(...rects.map(rect => rect.top));\n const maxX = max(...rects.map(rect => rect.right));\n const maxY = max(...rects.map(rect => rect.bottom));\n return {\n x: minX,\n y: minY,\n width: maxX - minX,\n height: maxY - minY\n };\n}\nfunction getRectsByLine(rects) {\n const sortedRects = rects.slice().sort((a, b) => a.y - b.y);\n const groups = [];\n let prevRect = null;\n for (let i = 0; i < sortedRects.length; i++) {\n const rect = sortedRects[i];\n if (!prevRect || rect.y - prevRect.y > prevRect.height / 2) {\n groups.push([rect]);\n } else {\n groups[groups.length - 1].push(rect);\n }\n prevRect = rect;\n }\n return groups.map(rect => rectToClientRect(getBoundingRect(rect)));\n}\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'inline',\n options,\n async fn(state) {\n const {\n placement,\n elements,\n rects,\n platform,\n strategy\n } = state;\n // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a\n // ClientRect's bounds, despite the event listener being triggered. A\n // padding of 2 seems to handle this issue.\n const {\n padding = 2,\n x,\n y\n } = evaluate(options, state);\n const nativeClientRects = Array.from((await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) || []);\n\n // No rects (e.g. a hidden or detached reference, or a collapsed range) —\n // keep the existing reference rect rather than resetting to an invalid\n // one with non-finite values.\n if (!nativeClientRects.length) {\n return {};\n }\n const clientRects = getRectsByLine(nativeClientRects);\n const fallback = rectToClientRect(getBoundingRect(nativeClientRects));\n const paddingObject = getPaddingObject(padding);\n function getBoundingClientRect() {\n // There are two rects and they are disjoined.\n if (clientRects.length === 2 && (clientRects[0].left > clientRects[1].right || clientRects[1].left > clientRects[0].right) && x != null && y != null) {\n // Find the first rect in which the point is fully inside.\n return clientRects.find(rect => x > rect.left - paddingObject.left && x < rect.right + paddingObject.right && y > rect.top - paddingObject.top && y < rect.bottom + paddingObject.bottom) || fallback;\n }\n\n // There are 2 or more connected rects.\n if (clientRects.length >= 2) {\n if (getSideAxis(placement) === 'y') {\n const firstRect = clientRects[0];\n const lastRect = clientRects[clientRects.length - 1];\n const isTop = getSide(placement) === 'top';\n const top = firstRect.top;\n const bottom = lastRect.bottom;\n const left = isTop ? firstRect.left : lastRect.left;\n const right = isTop ? firstRect.right : lastRect.right;\n return rectToClientRect({\n x: left,\n y: top,\n width: right - left,\n height: bottom - top\n });\n }\n const isLeftSide = getSide(placement) === 'left';\n const maxRight = max(...clientRects.map(rect => rect.right));\n const minLeft = min(...clientRects.map(rect => rect.left));\n const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);\n const top = measureRects[0].top;\n const bottom = measureRects[measureRects.length - 1].bottom;\n return rectToClientRect({\n x: minLeft,\n y: top,\n width: maxRight - minLeft,\n height: bottom - top\n });\n }\n return fallback;\n }\n const resetRects = await platform.getElementRects({\n reference: {\n getBoundingClientRect\n },\n floating: elements.floating,\n strategy\n });\n if (rects.reference.x !== resetRects.reference.x || rects.reference.y !== resetRects.reference.y || rects.reference.width !== resetRects.reference.width || rects.reference.height !== resetRects.reference.height) {\n return {\n reset: {\n rects: resetRects\n }\n };\n }\n return {};\n }\n };\n};\n\nconst originSides = /*#__PURE__*/new Set(['left', 'top']);\n\n// For type backwards-compatibility, the `OffsetOptions` type was also\n// Derivable.\n\nasync function convertValueToCoords(state, options) {\n const {\n placement,\n platform,\n elements\n } = state;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isVertical = getSideAxis(placement) === 'y';\n const mainAxisMulti = originSides.has(side) ? -1 : 1;\n const crossAxisMulti = rtl && isVertical ? -1 : 1;\n const rawValue = evaluate(options, state);\n\n // eslint-disable-next-line prefer-const\n let {\n mainAxis,\n crossAxis,\n alignmentAxis\n } = typeof rawValue === 'number' ? {\n mainAxis: rawValue,\n crossAxis: 0,\n alignmentAxis: null\n } : {\n mainAxis: rawValue.mainAxis || 0,\n crossAxis: rawValue.crossAxis || 0,\n alignmentAxis: rawValue.alignmentAxis\n };\n if (alignment && typeof alignmentAxis === 'number') {\n crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;\n }\n return isVertical ? {\n x: crossAxis * crossAxisMulti,\n y: mainAxis * mainAxisMulti\n } : {\n x: mainAxis * mainAxisMulti,\n y: crossAxis * crossAxisMulti\n };\n}\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = function (options) {\n if (options === void 0) {\n options = 0;\n }\n return {\n name: 'offset',\n options,\n async fn(state) {\n var _middlewareData$offse, _middlewareData$arrow;\n const {\n x,\n y,\n placement,\n middlewareData\n } = state;\n const diffCoords = await convertValueToCoords(state, options);\n\n // If the placement is the same and the arrow caused an alignment offset\n // then we don't need to change the positioning coordinates.\n if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n return {\n x: x + diffCoords.x,\n y: y + diffCoords.y,\n data: {\n ...diffCoords,\n placement\n }\n };\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'shift',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement,\n platform\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = false,\n limiter = {\n fn: _ref => {\n let {\n x,\n y\n } = _ref;\n return {\n x,\n y\n };\n }\n },\n ...detectOverflowOptions\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const crossAxis = getSideAxis(placement);\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n const clampCoord = (axis, coord) => clamp(coord + overflow[axis === 'y' ? 'top' : 'left'], coord, coord - overflow[axis === 'y' ? 'bottom' : 'right']);\n if (checkMainAxis) {\n mainAxisCoord = clampCoord(mainAxis, mainAxisCoord);\n }\n if (checkCrossAxis) {\n crossAxisCoord = clampCoord(crossAxis, crossAxisCoord);\n }\n const limitedCoords = limiter.fn({\n ...state,\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n });\n return {\n ...limitedCoords,\n data: {\n x: limitedCoords.x - x,\n y: limitedCoords.y - y,\n enabled: {\n [mainAxis]: checkMainAxis,\n [crossAxis]: checkCrossAxis\n }\n }\n };\n }\n };\n};\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n options,\n fn(state) {\n var _rawOffset$mainAxis, _rawOffset$crossAxis;\n const {\n x,\n y,\n placement,\n rects,\n middlewareData\n } = state;\n const {\n offset = 0,\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const crossAxis = getSideAxis(placement);\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n const rawOffset = evaluate(offset, state);\n const computedOffset = typeof rawOffset === 'number' ? {\n mainAxis: rawOffset,\n crossAxis: 0\n } : {\n mainAxis: (_rawOffset$mainAxis = rawOffset.mainAxis) != null ? _rawOffset$mainAxis : 0,\n crossAxis: (_rawOffset$crossAxis = rawOffset.crossAxis) != null ? _rawOffset$crossAxis : 0\n };\n if (checkMainAxis) {\n const len = mainAxis === 'y' ? 'height' : 'width';\n const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;\n const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;\n if (mainAxisCoord < limitMin) {\n mainAxisCoord = limitMin;\n } else if (mainAxisCoord > limitMax) {\n mainAxisCoord = limitMax;\n }\n }\n if (checkCrossAxis) {\n var _middlewareData$offse, _middlewareData$offse2;\n const len = mainAxis === 'y' ? 'width' : 'height';\n const isOriginSide = originSides.has(getSide(placement));\n const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis);\n const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0);\n if (crossAxisCoord < limitMin) {\n crossAxisCoord = limitMin;\n } else if (crossAxisCoord > limitMax) {\n crossAxisCoord = limitMax;\n }\n }\n return {\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n };\n }\n };\n};\n\n// Method syntax keeps callback parameters bivariant, but expressing the\n// explicit `| undefined` required by `exactOptionalPropertyTypes` needs\n// property syntax, which is contravariant under `strictFunctionTypes`.\n// Extracting the function from a method position restores that bivariance so\n// consumers can still assign callbacks with narrower parameter types.\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'size',\n options,\n async fn(state) {\n const {\n placement,\n rects,\n platform,\n elements\n } = state;\n const {\n apply = () => {},\n ...detectOverflowOptions\n } = evaluate(options, state);\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isYAxis = getSideAxis(placement) === 'y';\n const {\n width,\n height\n } = rects.floating;\n let heightSide;\n let widthSide;\n if (side === 'top' || side === 'bottom') {\n heightSide = side;\n widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';\n } else {\n widthSide = side;\n heightSide = alignment === 'end' ? 'top' : 'bottom';\n }\n const maximumClippingHeight = height - overflow.top - overflow.bottom;\n const maximumClippingWidth = width - overflow.left - overflow.right;\n const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight);\n const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth);\n const shiftData = state.middlewareData.shift;\n const noShift = !shiftData;\n let availableHeight = overflowAvailableHeight;\n let availableWidth = overflowAvailableWidth;\n if (shiftData != null && shiftData.enabled.x) {\n availableWidth = maximumClippingWidth;\n }\n if (shiftData != null && shiftData.enabled.y) {\n availableHeight = maximumClippingHeight;\n }\n if (noShift && !alignment) {\n if (isYAxis) {\n availableWidth = width - 2 * max(overflow.left, overflow.right);\n } else {\n availableHeight = height - 2 * max(overflow.top, overflow.bottom);\n }\n }\n await apply({\n ...state,\n availableWidth,\n availableHeight\n });\n const nextDimensions = await platform.getDimensions(elements.floating);\n if (width !== nextDimensions.width || height !== nextDimensions.height) {\n return {\n reset: {\n rects: true\n }\n };\n }\n return {};\n }\n };\n};\n\nexport { arrow, autoPlacement, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, shift, size };\n","import { rectToClientRect, arrow as arrow$1, autoPlacement as autoPlacement$1, detectOverflow as detectOverflow$1, flip as flip$1, hide as hide$1, inline as inline$1, limitShift as limitShift$1, offset as offset$1, shift as shift$1, size as size$1, computePosition as computePosition$1 } from '@floating-ui/core';\nimport { round, createCoords, max, min, floor } from '@floating-ui/utils';\nimport { getComputedStyle as getComputedStyle$1, isHTMLElement, isElement, getWindow, isWebKit, getFrameElement, getNodeScroll, getDocumentElement, isTopLayer, getNodeName, isOverflowElement, getOverflowAncestors, getParentNode, isLastTraversableNode, isContainingBlock, isTableElement, getContainingBlock } from '@floating-ui/utils/dom';\nexport { getOverflowAncestors } from '@floating-ui/utils/dom';\n\nfunction getCssDimensions(element) {\n const css = getComputedStyle$1(element);\n // In testing environments, the `width` and `height` properties are empty\n // strings for SVG elements, returning NaN. Fallback to `0` in this case.\n let width = parseFloat(css.width) || 0;\n let height = parseFloat(css.height) || 0;\n const hasOffset = isHTMLElement(element);\n const offsetWidth = hasOffset ? element.offsetWidth : width;\n const offsetHeight = hasOffset ? element.offsetHeight : height;\n const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;\n if (shouldFallback) {\n width = offsetWidth;\n height = offsetHeight;\n }\n return {\n width,\n height,\n $: shouldFallback\n };\n}\n\nfunction unwrapElement(element) {\n return !isElement(element) ? element.contextElement : element;\n}\n\nfunction getScale(element) {\n const domElement = unwrapElement(element);\n if (!isHTMLElement(domElement)) {\n return createCoords(1);\n }\n const rect = domElement.getBoundingClientRect();\n const {\n width,\n height,\n $\n } = getCssDimensions(domElement);\n let x = ($ ? round(rect.width) : rect.width) / width;\n let y = ($ ? round(rect.height) : rect.height) / height;\n\n // 0, NaN, or Infinity should always fallback to 1.\n\n if (!x || !Number.isFinite(x)) {\n x = 1;\n }\n if (!y || !Number.isFinite(y)) {\n y = 1;\n }\n return {\n x,\n y\n };\n}\n\nconst noOffsets = /*#__PURE__*/createCoords(0);\nfunction getVisualOffsets(element) {\n const win = getWindow(element);\n if (!isWebKit() || !win.visualViewport) {\n return noOffsets;\n }\n return {\n x: win.visualViewport.offsetLeft,\n y: win.visualViewport.offsetTop\n };\n}\nfunction shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n return !!floatingOffsetParent && isFixed && floatingOffsetParent === getWindow(element);\n}\n\nfunction getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n const clientRect = element.getBoundingClientRect();\n const domElement = unwrapElement(element);\n let scale = createCoords(1);\n if (includeScale) {\n if (offsetParent) {\n if (isElement(offsetParent)) {\n scale = getScale(offsetParent);\n }\n } else {\n scale = getScale(element);\n }\n }\n const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);\n let x = (clientRect.left + visualOffsets.x) / scale.x;\n let y = (clientRect.top + visualOffsets.y) / scale.y;\n let width = clientRect.width / scale.x;\n let height = clientRect.height / scale.y;\n if (domElement && offsetParent) {\n const win = getWindow(domElement);\n const offsetWin = isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;\n let currentWin = win;\n let currentIFrame = getFrameElement(currentWin);\n while (currentIFrame && offsetWin !== currentWin) {\n const iframeScale = getScale(currentIFrame);\n const iframeRect = currentIFrame.getBoundingClientRect();\n const css = getComputedStyle$1(currentIFrame);\n const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;\n const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;\n x *= iframeScale.x;\n y *= iframeScale.y;\n width *= iframeScale.x;\n height *= iframeScale.y;\n x += left;\n y += top;\n currentWin = getWindow(currentIFrame);\n currentIFrame = getFrameElement(currentWin);\n }\n }\n return rectToClientRect({\n width,\n height,\n x,\n y\n });\n}\n\n// If <html> has a CSS width greater than the viewport, then this will be\n// incorrect for RTL.\nfunction getWindowScrollBarX(element, rect) {\n const leftScroll = getNodeScroll(element).scrollLeft;\n if (!rect) {\n return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;\n }\n return rect.left + leftScroll;\n}\n\nfunction getHTMLOffset(documentElement, scroll) {\n const htmlRect = documentElement.getBoundingClientRect();\n const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);\n const y = htmlRect.top + scroll.scrollTop;\n return {\n x,\n y\n };\n}\n\nfunction convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {\n let {\n elements,\n rect,\n offsetParent,\n strategy\n } = _ref;\n const isFixed = strategy === 'fixed';\n const documentElement = getDocumentElement(offsetParent);\n const topLayer = elements ? isTopLayer(elements.floating) : false;\n if (offsetParent === documentElement || topLayer && isFixed) {\n return rect;\n }\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n let scale = createCoords(1);\n const offsets = createCoords(0);\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n if (isOffsetParentAnElement || !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isOffsetParentAnElement) {\n const offsetRect = getBoundingClientRect(offsetParent);\n scale = getScale(offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n }\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n return {\n width: rect.width * scale.x,\n height: rect.height * scale.y,\n x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,\n y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y\n };\n}\n\nfunction getClientRects(element) {\n return element.getClientRects ? Array.from(element.getClientRects()) : [];\n}\n\n// Gets the entire size of the scrollable document area, even extending outside\n// of the `<html>` and `<body>` rect bounds if horizontally scrollable.\nfunction getDocumentRect(html) {\n const scroll = getNodeScroll(html);\n const body = html.ownerDocument.body;\n const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);\n const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);\n let x = -scroll.scrollLeft + getWindowScrollBarX(html);\n const y = -scroll.scrollTop;\n if (getComputedStyle$1(body).direction === 'rtl') {\n x += max(html.clientWidth, body.clientWidth) - width;\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\n// Safety check: ensure the scrollbar space is reasonable in case this\n// calculation is affected by unusual styles.\n// Most scrollbars leave 15-18px of space.\nconst SCROLLBAR_MAX = 25;\nfunction getViewportRect(element, strategy, rootBoundary) {\n if (rootBoundary === void 0) {\n rootBoundary = 'viewport';\n }\n const isLayoutViewport = rootBoundary === 'layoutViewport';\n const win = getWindow(element);\n const html = getDocumentElement(element);\n const visualViewport = win.visualViewport;\n let width = html.clientWidth;\n let height = html.clientHeight;\n let x = 0;\n let y = 0;\n if (visualViewport) {\n // Client coordinates are relative to the layout viewport, except in\n // WebKit with an `absolute` strategy, where they are relative to the\n // visual viewport.\n const layoutRelativeClientCoords = !isWebKit() || strategy === 'fixed';\n if (isLayoutViewport) {\n if (!layoutRelativeClientCoords) {\n x = -visualViewport.offsetLeft;\n y = -visualViewport.offsetTop;\n }\n } else {\n width = visualViewport.width;\n height = visualViewport.height;\n if (layoutRelativeClientCoords) {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n }\n const windowScrollbarX = getWindowScrollBarX(html);\n // `scrollbar-gutter: stable` on the <html> reserves gutter space that shrinks\n // the visual width but isn't reflected in `html.clientWidth`, so subtract it.\n // Only the inline-end (right) gutter can hold the scrollbar; `both-edges` also\n // reserves an empty inline-start gutter that clips nothing, so exclude just\n // the one scrollbar-side gutter — halve the measured (two-gutter) total. A\n // left-side scrollbar (`windowScrollbarX > 0`) is already handled by\n // `getHTMLOffset`/`visualViewport.width`; skip it here.\n if (windowScrollbarX <= 0) {\n const doc = html.ownerDocument;\n const body = doc.body;\n const bodyStyles = getComputedStyle(body);\n const bodyMarginInline = doc.compatMode === 'CSS1Compat' ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;\n const reservedWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);\n const gutter = getComputedStyle(html).scrollbarGutter === 'stable both-edges' ? reservedWidth / 2 : reservedWidth;\n if (gutter <= SCROLLBAR_MAX) {\n width -= gutter;\n }\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\n// Returns the inner client rect, subtracting scrollbars if present.\nfunction getInnerBoundingClientRect(element, strategy) {\n const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');\n const top = clientRect.top + element.clientTop;\n const left = clientRect.left + element.clientLeft;\n const scale = getScale(element);\n const width = element.clientWidth * scale.x;\n const height = element.clientHeight * scale.y;\n const x = left * scale.x;\n const y = top * scale.y;\n return {\n width,\n height,\n x,\n y\n };\n}\nfunction getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {\n let rect;\n if (clippingAncestor === 'viewport' || clippingAncestor === 'layoutViewport') {\n rect = getViewportRect(element, strategy, clippingAncestor);\n } else if (clippingAncestor === 'document') {\n rect = getDocumentRect(getDocumentElement(element));\n } else if (isElement(clippingAncestor)) {\n rect = getInnerBoundingClientRect(clippingAncestor, strategy);\n } else {\n const visualOffsets = getVisualOffsets(element);\n rect = {\n x: clippingAncestor.x - visualOffsets.x,\n y: clippingAncestor.y - visualOffsets.y,\n width: clippingAncestor.width,\n height: clippingAncestor.height\n };\n }\n return rectToClientRect(rect);\n}\n\n// A \"clipping ancestor\" is an `overflow` element with the characteristic of\n// clipping (or hiding) child elements. This returns all clipping ancestors\n// of the given element up the tree.\nfunction getClippingElementAncestors(element, cache) {\n const cachedResult = cache.get(element);\n if (cachedResult) {\n return cachedResult;\n }\n let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');\n let lastKeptComputedStyle = null;\n const elementIsFixed = getComputedStyle$1(element).position === 'fixed';\n let currentNode = elementIsFixed ? getParentNode(element) : element;\n\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {\n const computedStyle = getComputedStyle$1(currentNode);\n const currentNodeIsContaining = isContainingBlock(currentNode);\n // Position of the containing block chain below the current node. A fixed\n // element whose containing block hasn't been found yet is a fixed chain.\n const lastPosition = lastKeptComputedStyle ? lastKeptComputedStyle.position : elementIsFixed ? 'fixed' : '';\n\n // A non-containing ancestor does not clip the element when the chain\n // below it escapes it: a fixed chain escapes all ancestors up to the\n // next containing block, an absolute chain escapes static ancestors.\n const shouldDropCurrentNode = !currentNodeIsContaining && (lastPosition === 'fixed' || lastPosition === 'absolute' && computedStyle.position === 'static');\n if (shouldDropCurrentNode) {\n // Drop non-containing blocks.\n result = result.filter(ancestor => ancestor !== currentNode);\n } else {\n // The kept node carries the chain position for the next iteration.\n lastKeptComputedStyle = computedStyle;\n }\n currentNode = getParentNode(currentNode);\n }\n cache.set(element, result);\n return result;\n}\n\n// Gets the maximum area that the element is visible in due to any number of\n// clipping ancestors.\nfunction getClippingRect(_ref) {\n let {\n element,\n boundary,\n rootBoundary,\n strategy\n } = _ref;\n const elementClippingAncestors = boundary === 'clippingAncestors' ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary);\n const clippingAncestors = [...elementClippingAncestors, rootBoundary];\n const firstRect = getClientRectFromClippingAncestor(element, clippingAncestors[0], strategy);\n let top = firstRect.top;\n let right = firstRect.right;\n let bottom = firstRect.bottom;\n let left = firstRect.left;\n for (let i = 1; i < clippingAncestors.length; i++) {\n const rect = getClientRectFromClippingAncestor(element, clippingAncestors[i], strategy);\n top = max(rect.top, top);\n right = min(rect.right, right);\n bottom = min(rect.bottom, bottom);\n left = max(rect.left, left);\n }\n return {\n width: right - left,\n height: bottom - top,\n x: left,\n y: top\n };\n}\n\nfunction getDimensions(element) {\n const {\n width,\n height\n } = getCssDimensions(element);\n return {\n width,\n height\n };\n}\n\nfunction getRectRelativeToOffsetParent(element, offsetParent, strategy) {\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n const documentElement = getDocumentElement(offsetParent);\n const isFixed = strategy === 'fixed';\n const rect = getBoundingClientRect(element, true, isFixed, offsetParent);\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n const offsets = createCoords(0);\n if (isOffsetParentAnElement || !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isOffsetParentAnElement) {\n const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n }\n }\n\n // If the <body> scrollbar appears on the left (e.g. RTL systems). Use\n // Firefox with layout.scrollbar.side = 3 in about:config to test this.\n if (!isOffsetParentAnElement && documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;\n const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;\n return {\n x,\n y,\n width: rect.width,\n height: rect.height\n };\n}\n\nfunction isStaticPositioned(element) {\n return getComputedStyle$1(element).position === 'static';\n}\n\nfunction getTrueOffsetParent(element, polyfill) {\n if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {\n return null;\n }\n if (polyfill) {\n return polyfill(element);\n }\n let rawOffsetParent = element.offsetParent;\n\n // Firefox returns the <html> element as the offsetParent if it's non-static,\n // while Chrome and Safari return the <body> element. The <body> element must\n // be used to perform the correct calculations even if the <html> element is\n // non-static.\n if (getDocumentElement(element) === rawOffsetParent) {\n rawOffsetParent = rawOffsetParent.ownerDocument.body;\n }\n return rawOffsetParent;\n}\n\n// Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\nfunction getOffsetParent(element, polyfill) {\n const win = getWindow(element);\n if (isTopLayer(element)) {\n return win;\n }\n if (!isHTMLElement(element)) {\n let svgOffsetParent = getParentNode(element);\n while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {\n if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {\n return svgOffsetParent;\n }\n svgOffsetParent = getParentNode(svgOffsetParent);\n }\n return win;\n }\n let offsetParent = getTrueOffsetParent(element, polyfill);\n while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {\n offsetParent = getTrueOffsetParent(offsetParent, polyfill);\n }\n if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {\n return win;\n }\n return offsetParent || getContainingBlock(element) || win;\n}\n\nconst getElementRects = async function (data) {\n const getOffsetParentFn = this.getOffsetParent || getOffsetParent;\n const getDimensionsFn = this.getDimensions;\n const floatingDimensions = await getDimensionsFn(data.floating);\n return {\n reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),\n floating: {\n x: 0,\n y: 0,\n width: floatingDimensions.width,\n height: floatingDimensions.height\n }\n };\n};\n\nfunction isRTL(element) {\n return getComputedStyle$1(element).direction === 'rtl';\n}\n\nconst platform = {\n convertOffsetParentRelativeRectToViewportRelativeRect,\n getDocumentElement,\n getClippingRect,\n getOffsetParent,\n getElementRects,\n getClientRects,\n getDimensions,\n getScale,\n isElement,\n isRTL\n};\n\nfunction rectsAreEqual(a, b) {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n// https://samthor.au/2021/observing-dom/\nfunction observeMove(element, onMove, ancestorResize) {\n let io = null;\n let timeoutId;\n const root = getDocumentElement(element);\n function cleanup() {\n var _io;\n clearTimeout(timeoutId);\n (_io = io) == null || _io.disconnect();\n io = null;\n }\n function refresh(skip, threshold) {\n if (skip === void 0) {\n skip = false;\n }\n if (threshold === void 0) {\n threshold = 1;\n }\n cleanup();\n const elementRectForRootMargin = element.getBoundingClientRect();\n const {\n left,\n top,\n width,\n height\n } = elementRectForRootMargin;\n if (!skip) {\n onMove();\n }\n if (!width || !height) {\n return;\n }\n const insetTop = floor(top);\n const insetRight = floor(root.clientWidth - (left + width));\n const insetBottom = floor(root.clientHeight - (top + height));\n const insetLeft = floor(left);\n const rootMargin = -insetTop + \"px \" + -insetRight + \"px \" + -insetBottom + \"px \" + -insetLeft + \"px\";\n const options = {\n rootMargin,\n threshold: max(0, min(1, threshold)) || 1\n };\n let isFirstUpdate = true;\n function handleObserve(entries) {\n const ratio = entries[0].intersectionRatio;\n\n // The entry is a snapshot, so the reference may have moved since the\n // intersection was computed (under performance constraints, or between\n // consecutive frames of a multi-frame layout shift). The reported ratio\n // and the observed area are stale in that case and cannot be trusted to\n // detect subsequent movement, so refresh regardless of the ratio.\n if (!rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {\n return refresh();\n }\n if (ratio !== threshold) {\n if (!isFirstUpdate) {\n return refresh();\n }\n if (!ratio) {\n // If the reference is clipped in place, the ratio is 0. Throttle\n // the refresh to prevent an infinite loop of updates.\n timeoutId = setTimeout(() => {\n refresh(false, 1e-7);\n }, 1000);\n } else {\n refresh(false, ratio);\n }\n }\n isFirstUpdate = false;\n }\n\n // Older browsers don't support a `document` as the root and will throw an\n // error.\n try {\n io = new IntersectionObserver(handleObserve, {\n ...options,\n // Handle <iframe>s\n root: root.ownerDocument\n });\n } catch (_e) {\n io = new IntersectionObserver(handleObserve, options);\n }\n io.observe(element);\n }\n const win = getWindow(element);\n // The window is a resize ancestor, so when `ancestorResize` is enabled its\n // listener already runs the update on resize. Here we only need to rebuild\n // the `IntersectionObserver` for the new root size, skipping a redundant\n // update. When `ancestorResize` is disabled, this becomes the sole update.\n const handleResize = () => refresh(ancestorResize);\n win.addEventListener('resize', handleResize);\n refresh(true);\n return () => {\n win.removeEventListener('resize', handleResize);\n cleanup();\n };\n}\n\n/**\n * Automatically updates the position of the floating element when necessary.\n * Should only be called when the floating element is mounted on the DOM or\n * visible on the screen.\n * @returns cleanup function that should be invoked when the floating element is\n * removed from the DOM or hidden from the screen.\n * @see https://floating-ui.com/docs/autoUpdate\n */\nfunction autoUpdate(reference, floating, update, options) {\n if (options === void 0) {\n options = {};\n }\n const {\n ancestorScroll = true,\n ancestorResize = true,\n elementResize = typeof ResizeObserver === 'function',\n layoutShift = typeof IntersectionObserver === 'function',\n animationFrame = false\n } = options;\n const referenceEl = unwrapElement(reference);\n const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...(floating ? getOverflowAncestors(floating) : [])] : [];\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.addEventListener('scroll', update);\n ancestorResize && ancestor.addEventListener('resize', update);\n });\n const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update, ancestorResize) : null;\n let reobserveFrame = -1;\n let resizeObserver = null;\n if (elementResize) {\n resizeObserver = new ResizeObserver(_ref => {\n let [firstEntry] = _ref;\n if (firstEntry && firstEntry.target === referenceEl && resizeObserver && floating) {\n // Prevent update loops when using the `size` middleware.\n // https://github.com/floating-ui/floating-ui/issues/1740\n resizeObserver.unobserve(floating);\n cancelAnimationFrame(reobserveFrame);\n reobserveFrame = requestAnimationFrame(() => {\n var _resizeObserver;\n (_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);\n });\n }\n update();\n });\n if (referenceEl && !animationFrame) {\n resizeObserver.observe(referenceEl);\n }\n if (floating) {\n resizeObserver.observe(floating);\n }\n }\n let frameId;\n let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;\n if (animationFrame) {\n frameLoop();\n }\n function frameLoop() {\n const nextRefRect = getBoundingClientRect(reference);\n if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {\n update();\n }\n prevRefRect = nextRefRect;\n frameId = requestAnimationFrame(frameLoop);\n }\n update();\n return () => {\n var _resizeObserver2;\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.removeEventListener('scroll', update);\n ancestorResize && ancestor.removeEventListener('resize', update);\n });\n cleanupIo == null || cleanupIo();\n (_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();\n resizeObserver = null;\n if (animationFrame) {\n cancelAnimationFrame(frameId);\n }\n };\n}\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nconst detectOverflow = detectOverflow$1;\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = offset$1;\n\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = autoPlacement$1;\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = shift$1;\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = flip$1;\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = size$1;\n\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = hide$1;\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = arrow$1;\n\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = inline$1;\n\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = limitShift$1;\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n */\nconst computePosition = (reference, floating, options) => {\n // This caches the expensive `getClippingElementAncestors` function so that\n // multiple lifecycle resets re-use the same result. It only lives for a\n // single call. If other functions become expensive, we can add them as well.\n const cache = new Map();\n const mergedOptions = options != null ? options : {};\n const platformWithCache = {\n ...platform,\n ...mergedOptions.platform,\n _c: cache\n };\n return computePosition$1(reference, floating, {\n ...mergedOptions,\n platform: platformWithCache\n });\n};\n\nexport { arrow, autoPlacement, autoUpdate, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, platform, shift, size };\n","import '@tiptap/markdown';\nimport type { Editor } from '@tiptap/core';\nimport type { Node as PMNode } from '@tiptap/pm/model';\n\n/**\n * 块拖拽菜单能「转成」的目标。和斜杠菜单里可序列化的块对齐,不含表格 / 公式 / 图片。\n */\nexport const TURN_INTO_KINDS = [\n 'paragraph',\n 'heading-1',\n 'heading-2',\n 'heading-3',\n 'bulletList',\n 'orderedList',\n 'taskList',\n 'blockquote',\n 'codeBlock',\n] as const;\n\nexport type TurnIntoKind = (typeof TURN_INTO_KINDS)[number];\n\nconst TURNABLE = new Set([\n 'paragraph',\n 'heading',\n 'bulletList',\n 'orderedList',\n 'taskList',\n 'listItem',\n 'taskItem',\n 'blockquote',\n 'codeBlock',\n]);\n\n/** 列表 / 引用 / 代码块要先拆掉外壳,段落和标题直接改节点类型即可。 */\nconst LIFT_BEFORE_TURN = new Set([\n 'listItem',\n 'taskItem',\n 'bulletList',\n 'orderedList',\n 'taskList',\n 'blockquote',\n 'codeBlock',\n]);\n\nexport function canTurnBlockInto(node: PMNode | null | undefined): boolean {\n return !!node && TURNABLE.has(node.type.name);\n}\n\n/** 菜单里标出「当前就是这种块」。列表项看它的父列表。 */\nexport function currentTurnIntoKind(\n editor: Editor,\n pos: number,\n node: PMNode,\n): TurnIntoKind | null {\n const name = node.type.name;\n if (name === 'paragraph') return 'paragraph';\n if (name === 'heading') {\n const level = Number(node.attrs.level);\n if (level === 1 || level === 2 || level === 3) {\n return `heading-${level}` as TurnIntoKind;\n }\n return null;\n }\n if (name === 'bulletList') return 'bulletList';\n if (name === 'orderedList') return 'orderedList';\n if (name === 'taskList') return 'taskList';\n if (name === 'blockquote') return 'blockquote';\n if (name === 'codeBlock') return 'codeBlock';\n if (name === 'listItem' || name === 'taskItem') {\n const parent = editor.state.doc.resolve(pos).parent.type.name;\n if (parent === 'bulletList') return 'bulletList';\n if (parent === 'orderedList') return 'orderedList';\n if (parent === 'taskList') return 'taskList';\n }\n return null;\n}\n\n/** 文本块内部的第一个可放光标的位置;容器则钻到第一个文本块里。 */\nfunction firstTextPos(node: PMNode, pos: number): number | null {\n if (node.isTextblock) return pos + 1;\n let found: number | null = null;\n node.descendants((child, offset) => {\n if (found != null) return false;\n if (child.isTextblock) {\n found = pos + 1 + offset + 1;\n return false;\n }\n return true;\n });\n return found;\n}\n\n/**\n * 把 `pos` 上的块转成另一种。光标落进块内再执行,这样列表项 / 引用里的段落\n * 改的是这一块,而不是文档开头。\n */\nexport function turnBlockInto(\n editor: Editor,\n pos: number,\n kind: TurnIntoKind,\n): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || !canTurnBlockInto(node)) return false;\n const textPos = firstTextPos(node, pos);\n if (textPos == null) return false;\n\n const chain = editor.chain().setTextSelection(textPos);\n if (LIFT_BEFORE_TURN.has(node.type.name)) chain.clearNodes();\n\n if (kind === 'heading-1' || kind === 'heading-2' || kind === 'heading-3') {\n const level = Number(kind.slice(-1)) as 1 | 2 | 3;\n chain.setHeading({ level });\n } else if (kind === 'paragraph') {\n // 列表 / 代码块 / 引用先 clearNodes,它已经把文本块还原成段落。\n // 再 setParagraph 会因为「已经是段落」返回 false,把整条 chain 丢掉。\n if (!LIFT_BEFORE_TURN.has(node.type.name)) chain.setParagraph();\n } else if (kind === 'bulletList') {\n chain.toggleBulletList();\n } else if (kind === 'orderedList') {\n chain.toggleOrderedList();\n } else if (kind === 'taskList') {\n chain.toggleTaskList();\n } else if (kind === 'blockquote') {\n chain.toggleBlockquote();\n } else if (kind === 'codeBlock') {\n chain.setCodeBlock();\n }\n\n return chain.run();\n}\n\n/** 在块后面插入一份拷贝(列表项插成同级下一项)。 */\nexport function duplicateBlock(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node) return false;\n return editor.chain().insertContentAt(pos + node.nodeSize, node.toJSON()).run();\n}\n\n/**\n * 删掉这一块。文档里只剩这一块时换成空段落(schema 不允许空 doc)。\n * 列表里只剩这一项时连列表一起删,避免留下空的 ul/ol。\n */\nexport function deleteBlock(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node) return false;\n\n let from = pos;\n let to = pos + node.nodeSize;\n const $pos = editor.state.doc.resolve(pos);\n const parent = $pos.parent;\n if (\n (node.type.name === 'listItem' || node.type.name === 'taskItem') &&\n parent.childCount === 1\n ) {\n from = pos - $pos.parentOffset - 1;\n const list = editor.state.doc.nodeAt(from);\n if (list) to = from + list.nodeSize;\n }\n\n if (from <= 0 && to >= editor.state.doc.content.size) {\n return editor\n .chain()\n .insertContentAt(\n { from: 0, to: editor.state.doc.content.size },\n { type: 'paragraph' },\n )\n .run();\n }\n\n return editor.chain().deleteRange({ from, to }).run();\n}\n\n/** 这一块的 markdown。复制进剪贴板前先拿到字符串,失败时调用方可以自己兜底。 */\nexport function blockMarkdown(editor: Editor, pos: number): string | null {\n const node = editor.state.doc.nodeAt(pos);\n if (!node) return null;\n const serialized = editor.markdown?.serialize(node.toJSON());\n if (typeof serialized === 'string') return serialized;\n return node.textContent;\n}\n\nexport function canResetBlockFormatting(node: PMNode | null | undefined): boolean {\n if (!node || node.isAtom) return false;\n if (node.type.name === 'image' || node.type.name === 'horizontalRule') {\n return false;\n }\n if (node.isTextblock) return true;\n let found = false;\n node.descendants((child) => {\n if (child.isTextblock) {\n found = true;\n return false;\n }\n return true;\n });\n return found;\n}\n\nfunction withoutBlockStyles(node: PMNode): Record<string, unknown> {\n const next: Record<string, unknown> = { ...node.attrs };\n if ('textAlign' in next) next.textAlign = null;\n if ('indent' in next) next.indent = null;\n if ('lineHeight' in next) next.lineHeight = null;\n return next;\n}\n\n/** 清掉块内的行内 mark,以及段落 / 标题上的对齐、缩进、行高。 */\nexport function resetBlockFormatting(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || !canResetBlockFormatting(node)) return false;\n\n const from = pos + 1;\n const to = pos + node.nodeSize - 1;\n let tr = editor.state.tr;\n if (to > from) {\n for (const mark of Object.values(editor.schema.marks)) {\n tr = tr.removeMark(from, to, mark);\n }\n }\n\n const markup: { pos: number; attrs: Record<string, unknown> }[] = [];\n if (node.isTextblock) {\n markup.push({ pos, attrs: withoutBlockStyles(node) });\n }\n node.descendants((child, offset) => {\n if (!child.isTextblock) return;\n markup.push({\n pos: pos + 1 + offset,\n attrs: withoutBlockStyles(child),\n });\n });\n for (const item of markup) {\n tr = tr.setNodeMarkup(item.pos, undefined, item.attrs);\n }\n\n if (!tr.docChanged) return false;\n editor.view.dispatch(tr);\n return true;\n}\n\nexport function isImageBlock(node: PMNode | null | undefined): boolean {\n return (\n node?.type.name === 'image' &&\n typeof node.attrs.src === 'string' &&\n node.attrs.src.length > 0\n );\n}\n\n/** 下载图片节点。跨域地址浏览器会忽略 download,退化为打开链接。 */\nexport function downloadImageBlock(node: PMNode): void {\n if (!isImageBlock(node)) return;\n const src = node.attrs.src as string;\n const alt = typeof node.attrs.alt === 'string' ? node.attrs.alt.trim() : '';\n const fromUrl = src.split('/').pop()?.split('?')[0] ?? '';\n const a = document.createElement('a');\n a.href = src;\n a.download = alt || fromUrl || 'image';\n a.rel = 'noopener';\n a.target = '_blank';\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n\n","import type { Node as PMNode, Slice } from '@tiptap/pm/model';\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state';\nimport { dropPoint } from '@tiptap/pm/transform';\nimport type { EditorView } from '@tiptap/pm/view';\n\n/**\n * 手柄拖过来的是一块闭合节点(openStart 为 0)。ProseMirror 的 dropPoint\n * 第二轮会找包装:列表正好能把段落 / 标题包成 listItem,于是单行块会掉进列表里。\n * 列表在这个编辑器里是一整块,落点只能在它外面(上半段前面,下半段后面)。\n */\nconst LIST_NAMES = new Set(['bulletList', 'orderedList', 'taskList']);\n\nconst draggingViews = new WeakSet<EditorView>();\n\nexport const blockDragListGuardKey = new PluginKey('blockDragListGuard');\n\nexport function setBlockHandleDragging(view: EditorView, dragging: boolean) {\n if (dragging) draggingViews.add(view);\n else draggingViews.delete(view);\n}\n\nexport function isBlockHandleDragging(view: EditorView): boolean {\n return draggingViews.has(view);\n}\n\nfunction outermostListDepth(doc: PMNode, pos: number): number {\n const safe = Math.max(0, Math.min(pos, doc.content.size));\n const $pos = doc.resolve(safe);\n let listDepth = -1;\n for (let depth = $pos.depth; depth > 0; depth -= 1) {\n if (LIST_NAMES.has($pos.node(depth).type.name)) listDepth = depth;\n }\n return listDepth;\n}\n\n/** 坐标落在某份列表(含其中的条目)里面。 */\nexport function isPosInsideList(doc: PMNode, pos: number): boolean {\n return outermostListDepth(doc, pos) > 0;\n}\n\n/**\n * 把列表内部的位置抬到最外层那份列表的边界。\n * `after` 为真时放在列表后面,否则放在前面。\n */\nexport function listEscapePos(\n doc: PMNode,\n pos: number,\n after: boolean,\n): number | null {\n const listDepth = outermostListDepth(doc, pos);\n if (listDepth < 0) return null;\n const $pos = doc.resolve(Math.max(0, Math.min(pos, doc.content.size)));\n const start = $pos.before(listDepth);\n const node = $pos.node(listDepth);\n return after ? start + node.nodeSize : start;\n}\n\n/** 指针在列表盒的下半段时落到列表后面,否则落到前面。 */\nexport function listEscapeEdge(\n view: EditorView,\n pos: number,\n clientY: number,\n): number | null {\n const listDepth = outermostListDepth(view.state.doc, pos);\n if (listDepth < 0) return null;\n const $pos = view.state.doc.resolve(\n Math.max(0, Math.min(pos, view.state.doc.content.size)),\n );\n const start = $pos.before(listDepth);\n const node = $pos.node(listDepth);\n const dom = view.nodeDOM(start);\n let after = pos >= start + node.nodeSize / 2;\n if (dom instanceof HTMLElement) {\n const rect = dom.getBoundingClientRect();\n if (rect.height > 0) after = clientY >= rect.top + rect.height / 2;\n }\n return after ? start + node.nodeSize : start;\n}\n\n/**\n * 把手柄拖来的切片挪到 `insertPos`。位置落在被拖的选区内部时不挪。\n * 返回 true 表示这次落点已经由我们处理(包括故意不挪)。\n */\nexport function moveDraggedSlice(\n view: EditorView,\n slice: Slice,\n insertPos: number,\n): boolean {\n const { state } = view;\n const { from, to } = state.selection;\n if (insertPos > from && insertPos < to) return true;\n\n const tr = state.tr;\n tr.deleteSelection();\n const pos = tr.mapping.map(insertPos, 1);\n const block =\n slice.openStart === 0 &&\n slice.openEnd === 0 &&\n slice.content.childCount === 1\n ? slice.content.firstChild\n : null;\n if (block) tr.replaceRangeWith(pos, pos, block);\n else tr.replaceRange(pos, pos, slice);\n if (tr.doc.eq(state.doc)) return true;\n\n const $pos = tr.doc.resolve(Math.max(0, Math.min(pos, tr.doc.content.size)));\n const near = TextSelection.findFrom($pos, 1, true) ?? TextSelection.findFrom($pos, -1, true);\n if (near) tr.setSelection(near);\n try {\n view.focus();\n } catch {\n // 无 window 的环境里 focus 会抛;文档修改仍然要提交。\n }\n view.dispatch(tr.setMeta('uiEvent', 'drop').scrollIntoView());\n return true;\n}\n\nfunction snappedListEdge(\n view: EditorView,\n clientX: number,\n clientY: number,\n slice: Slice,\n): number | null {\n const coords = view.posAtCoords({ left: clientX, top: clientY });\n if (!coords) return null;\n const natural = dropPoint(view.state.doc, coords.pos, slice);\n const probe = natural ?? coords.pos;\n if (!isPosInsideList(view.state.doc, probe)) return null;\n return listEscapeEdge(view, probe, clientY);\n}\n\n/** 列表内部的落点指示挪到整份列表的上沿或下沿,避免画在条目之间。 */\nfunction placeBlockDropCursor(view: EditorView, pos: number) {\n const parent = view.dom.offsetParent;\n if (!(parent instanceof HTMLElement)) return;\n const cursor = parent.querySelector(\n '.prosemirror-dropcursor-block, .prosemirror-dropcursor-inline',\n );\n if (!(cursor instanceof HTMLElement)) return;\n\n const $pos = view.state.doc.resolve(pos);\n const before = $pos.nodeBefore;\n const anchorPos = before ? pos - before.nodeSize : pos;\n const anchor = view.nodeDOM(anchorPos);\n if (!(anchor instanceof HTMLElement)) return;\n const anchorRect = anchor.getBoundingClientRect();\n let lineTop = before ? anchorRect.bottom : anchorRect.top;\n if (before && $pos.nodeAfter) {\n const next = view.nodeDOM(pos);\n if (next instanceof HTMLElement) {\n lineTop = (anchorRect.bottom + next.getBoundingClientRect().top) / 2;\n }\n }\n\n const editorRect = view.dom.getBoundingClientRect();\n const scaleX = editorRect.width / view.dom.offsetWidth || 1;\n const scaleY = editorRect.height / view.dom.offsetHeight || 1;\n const parentRect = parent.getBoundingClientRect();\n const parentScaleX = parentRect.width / parent.offsetWidth || 1;\n const parentScaleY = parentRect.height / parent.offsetHeight || 1;\n const parentLeft = parentRect.left - parent.scrollLeft * parentScaleX;\n const parentTop = parentRect.top - parent.scrollTop * parentScaleY;\n const half = scaleY;\n\n cursor.classList.add('prosemirror-dropcursor-block');\n cursor.classList.remove('prosemirror-dropcursor-inline');\n cursor.style.left = `${(anchorRect.left - parentLeft) / scaleX}px`;\n cursor.style.top = `${(lineTop - half - parentTop) / scaleY}px`;\n cursor.style.width = `${(anchorRect.right - anchorRect.left) / scaleX}px`;\n cursor.style.height = `${(2 * half) / scaleY}px`;\n}\n\nexport function blockDragListGuardPlugin(): Plugin {\n return new Plugin({\n key: blockDragListGuardKey,\n props: {\n handleDrop(view, event, slice, moved) {\n if (!moved || !isBlockHandleDragging(view)) return false;\n const edge = snappedListEdge(view, event.clientX, event.clientY, slice);\n if (edge == null) return false;\n moveDraggedSlice(view, slice, edge);\n return true;\n },\n },\n view(editorView) {\n const onDragOver = (event: DragEvent) => {\n const slice = editorView.dragging?.slice;\n if (!slice || !isBlockHandleDragging(editorView)) return;\n const edge = snappedListEdge(\n editorView,\n event.clientX,\n event.clientY,\n slice,\n );\n if (edge == null) return;\n placeBlockDropCursor(editorView, edge);\n };\n editorView.dom.addEventListener('dragover', onDragOver);\n return {\n destroy() {\n editorView.dom.removeEventListener('dragover', onDragOver);\n setBlockHandleDragging(editorView, false);\n },\n };\n },\n });\n}\n","'use client';\n\nimport { offset } from '@floating-ui/dom';\nimport type { Editor } from '@tiptap/core';\nimport { DragHandle } from '@tiptap/extension-drag-handle-react';\nimport type { Node as PMNode } from '@tiptap/pm/model';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n TURN_INTO_KINDS,\n blockMarkdown,\n canResetBlockFormatting,\n canTurnBlockInto,\n currentTurnIntoKind,\n deleteBlock,\n downloadImageBlock,\n duplicateBlock,\n isImageBlock,\n resetBlockFormatting,\n turnBlockInto,\n type TurnIntoKind,\n} from '../blockDrag/actions';\nimport {\n blockDragListGuardKey,\n blockDragListGuardPlugin,\n setBlockHandleDragging,\n} from '../blockDrag/listDrop';\nimport {\n CopyIcon,\n DownloadIcon,\n EraserIcon,\n GripIcon,\n TrashIcon,\n} from '../icons';\nimport { useEditorBundle } from '../i18n/store';\nimport type { BlockDragLabels } from '../labels';\nimport styles from '../styles/blockDrag.module.css';\n\n/**\n * 官方 DragHandle 的定位。负的 mainAxis 让手柄盒子伸进块左缘几像素:\n * 手柄在编辑器 DOM 外面,中间只要有空隙,鼠标移过去会先触发 mouseleave,\n * 手柄立刻消失。按钮本身仍靠 cluster 的右内边距留在正文左侧,不压住字。\n * 和 content.module.css 里 `.withBlockDrag` 的左内边距一起算,给评论气泡留空。\n */\nconst HANDLE_POSITION = {\n placement: 'left-start' as const,\n strategy: 'absolute' as const,\n middleware: [offset({ mainAxis: -6, crossAxis: 4 })],\n};\n\n/**\n * 表格的行手柄贴在表左缘。块手柄若也对齐表顶,会压在第一行的行手柄上。\n * 把定位锚点上移,六点落到表格上方的空隙里;clusterOnTable 的热区再接回表角。\n */\nconst TABLE_ANCHOR_LIFT = 24;\n\nconst THEME_VARS = [\n '--tmr-text',\n '--tmr-accent',\n '--tmr-toolbar-border',\n '--tmr-toolbar-muted',\n '--tmr-popover-bg',\n '--tmr-danger',\n '--tmr-code-bg',\n];\n\ninterface BlockTarget {\n node: PMNode;\n pos: number;\n}\n\ninterface OpenMenu {\n x: number;\n y: number;\n pos: number;\n node: PMNode;\n}\n\nexport interface BlockDragHandleProps {\n editor: Editor;\n}\n\nfunction lockDragHandle(editor: Editor, locked: boolean) {\n if (editor.isDestroyed) return;\n editor.view.dispatch(\n editor.state.tr\n .setMeta('lockDragHandle', locked)\n .setMeta('addToHistory', false),\n );\n}\n\nasync function copyMarkdown(markdown: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(markdown);\n return;\n } catch {\n // 非安全上下文里 clipboard 会拒绝,退回选区复制。\n }\n const area = document.createElement('textarea');\n area.value = markdown;\n area.setAttribute('readonly', '');\n area.style.position = 'fixed';\n area.style.left = '-9999px';\n document.body.appendChild(area);\n area.select();\n document.execCommand('copy');\n area.remove();\n}\n\nconst TURN_ICON: Record<TurnIntoKind, string> = {\n paragraph: '¶',\n 'heading-1': 'H1',\n 'heading-2': 'H2',\n 'heading-3': 'H3',\n bulletList: '•',\n orderedList: '1.',\n taskList: '✓',\n blockquote: '❝',\n codeBlock: '{ }',\n};\n\n/**\n * 块拖拽:悬停时在块左侧露出手柄(拖动重排),点手柄打开菜单\n * (转换 / 复制 / 再制一份 / 删除)。交互对齐 Tiptap Drag Context Menu,\n * 样式走本库的 `--tmr-*`。手柄跟着文档最外层的块走,列表和引用整块移动。\n * 松开时如果指针在列表上,块落到整份列表的前面或后面,不会变成其中一条。\n *\n * 不注册 NodeRange 扩展:它会接管 Shift+方向键和 Mod-a。拖拽要用的\n * NodeRangeSelection 在加载 drag-handle 时已经注册进 Selection。\n */\nexport function BlockDragHandle({ editor }: BlockDragHandleProps) {\n const t: BlockDragLabels = useEditorBundle(editor).blockDrag;\n const [target, setTarget] = useState<BlockTarget | null>(null);\n const [menu, setMenu] = useState<OpenMenu | null>(null);\n const draggedRef = useRef(false);\n const targetRef = useRef<BlockTarget | null>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n const closeMenu = useCallback(() => {\n setMenu(null);\n lockDragHandle(editor, false);\n }, [editor]);\n\n // 拦住「单行块被包成列表项」。插件跟着手柄一起挂上,只读 / 窄屏没有拖拽。\n useEffect(() => {\n const plugin = blockDragListGuardPlugin();\n editor.registerPlugin(plugin);\n return () => {\n setBlockHandleDragging(editor.view, false);\n if (!editor.isDestroyed) editor.unregisterPlugin(blockDragListGuardKey);\n };\n }, [editor]);\n\n useEffect(() => {\n if (!menu) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n event.preventDefault();\n closeMenu();\n };\n const onDown = (event: MouseEvent) => {\n const el = event.target;\n if (!(el instanceof Element)) return;\n if (el.closest('[data-block-drag-menu], [data-block-drag-grip]')) return;\n closeMenu();\n };\n const onScroll = () => closeMenu();\n document.addEventListener('keydown', onKey);\n document.addEventListener('mousedown', onDown);\n document.addEventListener('scroll', onScroll, true);\n return () => {\n document.removeEventListener('keydown', onKey);\n document.removeEventListener('mousedown', onDown);\n document.removeEventListener('scroll', onScroll, true);\n };\n }, [menu, closeMenu]);\n\n useEffect(() => {\n if (!menu || !menuRef.current) return;\n const style = getComputedStyle(editor.view.dom);\n for (const name of THEME_VARS) {\n const value = style.getPropertyValue(name);\n if (value) menuRef.current.style.setProperty(name, value);\n }\n }, [menu, editor]);\n\n useEffect(() => () => lockDragHandle(editor, false), [editor, closeMenu]);\n\n const openMenu = (anchor: HTMLElement) => {\n if (!target) return;\n const rect = anchor.getBoundingClientRect();\n const estimated = 380;\n const y =\n rect.top + estimated > window.innerHeight\n ? Math.max(8, window.innerHeight - estimated - 8)\n : rect.top;\n const x = Math.min(rect.right + 6, window.innerWidth - 220);\n lockDragHandle(editor, true);\n setMenu({ x, y, pos: target.pos, node: target.node });\n };\n\n const run = (action: () => void) => {\n action();\n // 菜单按钮上的 preventDefault 会把焦点留在手柄上;命令本身不 focus\n // (无 window 的环境里 focus 会让整条 chain 失败)。\n editor.commands.focus();\n closeMenu();\n };\n\n const activeKind = menu\n ? currentTurnIntoKind(editor, menu.pos, menu.node)\n : null;\n const showTurn = menu ? canTurnBlockInto(menu.node) : false;\n const showReset = menu ? canResetBlockFormatting(menu.node) : false;\n const showDownload = menu ? isImageBlock(menu.node) : false;\n\n const turnLabel = (kind: TurnIntoKind): string => {\n if (kind === 'paragraph') return t.paragraph;\n if (kind === 'heading-1') return t.headingLabel(1);\n if (kind === 'heading-2') return t.headingLabel(2);\n if (kind === 'heading-3') return t.headingLabel(3);\n if (kind === 'bulletList') return t.bulletList;\n if (kind === 'orderedList') return t.orderedList;\n if (kind === 'taskList') return t.taskList;\n if (kind === 'blockquote') return t.blockquote;\n return t.codeBlock;\n };\n\n return (\n <>\n <DragHandle\n editor={editor}\n computePositionConfig={HANDLE_POSITION}\n onNodeChange={({ node, pos }) => {\n if (!node || pos < 0) {\n targetRef.current = null;\n setTarget(null);\n return;\n }\n const next = { node, pos };\n targetRef.current = next;\n setTarget(next);\n }}\n getReferencedVirtualElement={() => {\n const current = targetRef.current;\n if (!current || current.node.type.name !== 'table') return null;\n const pos = current.pos;\n return {\n getBoundingClientRect: () => {\n let dom: globalThis.Node | null = null;\n try {\n dom = editor.view.nodeDOM(pos);\n } catch {\n dom = null;\n }\n if (!(dom instanceof HTMLElement)) return new DOMRect();\n const rect = dom.getBoundingClientRect();\n return new DOMRect(\n rect.x,\n rect.y - TABLE_ANCHOR_LIFT,\n rect.width,\n rect.height,\n );\n },\n };\n }}\n onElementDragStart={() => {\n draggedRef.current = true;\n setBlockHandleDragging(editor.view, true);\n }}\n onElementDragEnd={() => {\n // drop 先于 dragend,松手时落点拦截还能读到这次是手柄拖拽。\n setBlockHandleDragging(editor.view, false);\n // click 紧跟 dragend。等这次点击过去再清掉,避免松手又把菜单打开。\n setTimeout(() => {\n draggedRef.current = false;\n }, 0);\n }}\n >\n <div\n className={`${styles.cluster} ${\n target?.node.type.name === 'table' ? styles.clusterOnTable : ''\n }`}\n data-block-drag=\"\"\n >\n <div\n role=\"button\"\n tabIndex={0}\n className={`${styles.button} ${styles.grip}`}\n data-block-drag-grip=\"\"\n title={t.dragHandle}\n aria-label={t.dragHandle}\n aria-haspopup=\"menu\"\n aria-expanded={menu != null}\n onKeyDown={(event) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n if (menu) closeMenu();\n else openMenu(event.currentTarget);\n }}\n onClick={(event) => {\n if (draggedRef.current) return;\n if (menu) {\n closeMenu();\n return;\n }\n openMenu(event.currentTarget);\n }}\n >\n <GripIcon size={14} />\n </div>\n </div>\n </DragHandle>\n {menu\n ? createPortal(\n <div\n ref={menuRef}\n className={styles.menu}\n data-block-drag-menu=\"\"\n role=\"menu\"\n aria-label={t.menuLabel}\n style={{ left: menu.x, top: menu.y }}\n onMouseDown={(event) => event.stopPropagation()}\n >\n {showTurn ? (\n <>\n <div className={styles.groupLabel}>{t.turnInto}</div>\n {TURN_INTO_KINDS.map((kind) => (\n <button\n key={kind}\n type=\"button\"\n role=\"menuitem\"\n className={\n kind === activeKind\n ? `${styles.item} ${styles.itemActive}`\n : styles.item\n }\n data-block-drag-action={kind}\n onClick={() =>\n run(() => turnBlockInto(editor, menu.pos, kind))\n }\n >\n <span className={styles.itemIcon} aria-hidden>\n {TURN_ICON[kind]}\n </span>\n <span>{turnLabel(kind)}</span>\n </button>\n ))}\n <div className={styles.divider} />\n </>\n ) : null}\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"copy\"\n onClick={() =>\n run(() => {\n const markdown = blockMarkdown(editor, menu.pos);\n if (markdown != null) void copyMarkdown(markdown);\n })\n }\n >\n <span className={styles.itemIcon} aria-hidden>\n <CopyIcon />\n </span>\n <span>{t.copy}</span>\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"duplicate\"\n onClick={() => run(() => duplicateBlock(editor, menu.pos))}\n >\n <span className={styles.itemIcon} aria-hidden>\n <CopyIcon />\n </span>\n <span>{t.duplicate}</span>\n </button>\n {showReset ? (\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"reset\"\n onClick={() =>\n run(() => resetBlockFormatting(editor, menu.pos))\n }\n >\n <span className={styles.itemIcon} aria-hidden>\n <EraserIcon />\n </span>\n <span>{t.resetFormatting}</span>\n </button>\n ) : null}\n {showDownload ? (\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"download\"\n onClick={() => run(() => downloadImageBlock(menu.node))}\n >\n <span className={styles.itemIcon} aria-hidden>\n <DownloadIcon />\n </span>\n <span>{t.downloadImage}</span>\n </button>\n ) : null}\n <div className={styles.divider} />\n <button\n type=\"button\"\n role=\"menuitem\"\n className={`${styles.item} ${styles.itemDanger}`}\n data-block-drag-action=\"delete\"\n onClick={() => run(() => deleteBlock(editor, menu.pos))}\n >\n <span className={styles.itemIcon} aria-hidden>\n <TrashIcon />\n </span>\n <span>{t.delete}</span>\n </button>\n </div>,\n document.body,\n )\n : null}\n </>\n );\n}\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport styles from '../styles/editorToc.module.css';\nimport type { TocItem } from '../toc/extractToc';\nimport { scrollToTocHeading } from '../toc/scrollToTocHeading';\n\n/** 短横线宽度:层级越高越长。 */\nfunction barWidth(level: number): number {\n if (level <= 1) return 16;\n if (level === 2) return 12;\n if (level === 3) return 8;\n return 6;\n}\n\n/**\n * 最近的可滚动祖先。限高但还没溢出的容器也算(内容变长后由它滚)。\n * 都没有时用文档滚动根。\n */\nfunction findScrollParent(start: HTMLElement): HTMLElement {\n let node: HTMLElement | null = start.parentElement;\n while (node && node !== document.documentElement) {\n const style = getComputedStyle(node);\n const overflowY = style.overflowY;\n const canScroll =\n overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay';\n if (\n canScroll &&\n (node.scrollHeight > node.clientHeight + 1 || style.maxHeight !== 'none')\n ) {\n return node;\n }\n node = node.parentElement;\n }\n return (document.scrollingElement as HTMLElement) ?? document.documentElement;\n}\n\nfunction isViewportScroller(node: HTMLElement): boolean {\n return (\n node === document.documentElement ||\n node === document.body ||\n node === document.scrollingElement\n );\n}\n\nexport interface EditorTocProps {\n items: TocItem[];\n /** 编辑器正文根节点,用来找标题和滚动容器。 */\n editorDom: HTMLElement;\n locale?: LocaleCode;\n}\n\n/**\n * 编辑器内目录(Notion 式,贴在左缘)。\n * 收起是一列短横线;悬停或聚焦向右展开标题,点击滚到对应标题。\n */\nexport function EditorToc({ items, editorDom, locale = 'en' }: EditorTocProps) {\n const t = getLocaleBundle(locale).toc;\n const [open, setOpen] = useState(false);\n const [activeId, setActiveId] = useState(items[0]?.id ?? '');\n const [roving, setRoving] = useState(0);\n const navRef = useRef<HTMLElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const hideTimer = useRef<number | null>(null);\n\n const clearHide = () => {\n if (hideTimer.current !== null) {\n window.clearTimeout(hideTimer.current);\n hideTimer.current = null;\n }\n };\n\n const show = () => {\n clearHide();\n setOpen(true);\n };\n\n const hideSoon = () => {\n clearHide();\n hideTimer.current = window.setTimeout(() => {\n hideTimer.current = null;\n if (navRef.current?.contains(document.activeElement)) return;\n setOpen(false);\n }, 80);\n };\n\n useEffect(() => () => clearHide(), []);\n\n // 视口上半区里最靠上的标题为当前章节;都离开上半区时,取已经滚过去的最后一个。\n useEffect(() => {\n const headings = items\n .map((item) =>\n editorDom.querySelector<HTMLElement>(\n `[data-toc-id=\"${CSS.escape(item.id)}\"]`,\n ),\n )\n .filter((el): el is HTMLElement => el !== null);\n if (!headings.length) return;\n\n const scrollParent = findScrollParent(editorDom);\n const viewport = isViewportScroller(scrollParent);\n const visible = new Set<Element>();\n\n const apply = () => {\n const rootTop = viewport ? 0 : scrollParent.getBoundingClientRect().top;\n let best: HTMLElement | null = null;\n let bestTop = Infinity;\n for (const el of visible) {\n const top = (el as HTMLElement).getBoundingClientRect().top;\n if (top < bestTop) {\n bestTop = top;\n best = el as HTMLElement;\n }\n }\n if (!best) {\n let last: HTMLElement | null = null;\n for (const heading of headings) {\n if (heading.getBoundingClientRect().top <= rootTop + 12) last = heading;\n }\n best = last ?? headings[0];\n }\n const id = best.getAttribute('data-toc-id');\n if (id) setActiveId(id);\n };\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting) visible.add(entry.target);\n else visible.delete(entry.target);\n }\n apply();\n },\n {\n root: viewport ? null : scrollParent,\n rootMargin: '0px 0px -60% 0px',\n threshold: [0, 1],\n },\n );\n for (const heading of headings) observer.observe(heading);\n\n const onScroll = () => apply();\n const scrollTarget: HTMLElement | Window = viewport ? window : scrollParent;\n scrollTarget.addEventListener('scroll', onScroll, { passive: true });\n apply();\n\n return () => {\n observer.disconnect();\n scrollTarget.removeEventListener('scroll', onScroll);\n };\n }, [items, editorDom]);\n\n // 目录没被键盘占用时,下一个 Tab 落在当前章节上。\n useEffect(() => {\n if (navRef.current?.contains(document.activeElement)) return;\n const index = items.findIndex((item) => item.id === activeId);\n if (index >= 0) setRoving(index);\n }, [activeId, items]);\n\n useEffect(() => {\n if (!open || !activeId) return;\n const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n navRef.current\n ?.querySelector(`[data-toc-item=\"${CSS.escape(activeId)}\"]`)\n ?.scrollIntoView({ block: 'nearest', behavior: reduce ? 'auto' : 'smooth' });\n }, [activeId, open]);\n\n const jump = (item: TocItem) => {\n setActiveId(item.id);\n scrollToTocHeading(item.id, findScrollParent(editorDom));\n };\n\n const move = (delta: number) => {\n const next = Math.min(items.length - 1, Math.max(0, roving + delta));\n setRoving(next);\n setOpen(true);\n itemRefs.current[next]?.focus();\n };\n\n return (\n <div className={styles.anchor}>\n <nav\n ref={navRef}\n className={open ? styles.navOpen : styles.nav}\n aria-label={t.title}\n data-editor-toc=\"\"\n onMouseEnter={show}\n onMouseLeave={hideSoon}\n onFocus={show}\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget)) hideSoon();\n }}\n onKeyDown={(event) => {\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n move(1);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n move(-1);\n } else if (event.key === 'Escape') {\n setOpen(false);\n }\n }}\n >\n <ul className={styles.list}>\n {items.map((item, index) => {\n const active = item.id === activeId;\n return (\n <li key={item.id}>\n <button\n type=\"button\"\n ref={(node) => {\n itemRefs.current[index] = node;\n }}\n data-toc-item={item.id}\n tabIndex={index === roving ? 0 : -1}\n className={\n open\n ? `${styles.item} ${active ? styles.itemActive : ''}`\n : styles.barButton\n }\n style={\n open\n ? { paddingLeft: `${(item.level - 1) * 12 + 10}px` }\n : undefined\n }\n aria-current={active ? 'true' : undefined}\n aria-label={item.text}\n onClick={() => jump(item)}\n >\n {open ? (\n <span className={styles.itemText}>{item.text}</span>\n ) : (\n <span\n className={active ? styles.barActive : styles.bar}\n style={{ width: barWidth(item.level) }}\n />\n )}\n </button>\n </li>\n );\n })}\n </ul>\n </nav>\n </div>\n );\n}\n","import { useEffect, useState } from 'react';\n\n/**\n * 是否窄屏(默认 <768px)。取代原项目的 useDevice,内联为纯 matchMedia,\n * 无第三方依赖。SSR 首帧返回 false,挂载后按实际视口更新。\n */\nexport function useIsMobile(breakpoint = 768): boolean {\n const [isMobile, setIsMobile] = useState(false);\n\n useEffect(() => {\n const mq = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);\n const update = () => setIsMobile(mq.matches);\n update();\n mq.addEventListener('change', update);\n return () => mq.removeEventListener('change', update);\n }, [breakpoint]);\n\n return isMobile;\n}\n","'use client';\n\nimport CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';\nimport FindAndReplace from '@tiptap/extension-find-and-replace';\nimport Typography from '@tiptap/extension-typography';\nimport Image from '@tiptap/extension-image';\nimport { TableOfContents } from '@tiptap/extension-table-of-contents';\nimport { CharacterCount, Placeholder, Selection } from '@tiptap/extensions';\nimport { Markdown } from '@tiptap/markdown';\nimport {\n EditorContent,\n ReactNodeViewRenderer,\n useEditor,\n useEditorState,\n type AnyExtension,\n type Editor,\n} from '@tiptap/react';\nimport type {\n NodeViewRendererProps,\n ResizableNodeViewDirection,\n} from '@tiptap/core';\nimport { mergeAttributes } from '@tiptap/core';\nimport { createPortal } from 'react-dom';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n} from 'react';\nimport {\n enrichMarkdownCitations,\n type SourceRef,\n} from '../citationUtils';\nimport type { RenderCitation } from '../citationTypes';\nimport { createCitationRef } from '../createCitationRef';\nimport { CommentMark } from '../commentAnchor/CommentMark';\nimport {\n COMMENT_ACTIVE_META,\n COMMENT_CLICK_META,\n COMMENT_GUTTER_META,\n commentAnchorExtension,\n} from '../commentAnchor/commentAnchorPlugin';\nimport {\n applyCommentAnchorsToEditor,\n collectCommentIds,\n focusComment,\n nextComment,\n} from '../commentAnchor/commentAnchorController';\nimport type {\n CommentClickPayload,\n CommentRef,\n} from '../commentAnchor/commentTypes';\nimport { baseExtensions, lowlight } from '../extensions';\nimport { isComposingKeyEvent } from '../findReplace';\nimport { FindReplaceBar } from './FindReplaceBar';\nimport { ImportPlaceholder } from '../importPlaceholder';\nimport { ImageUploadNode, type ImageUploadConfig } from '../imageUpload';\nimport {\n imageAlignAttribute,\n imageCaptionAttribute,\n imageFigureParseRule,\n imageMarkdownTokenizer,\n imageNodeHtml,\n imageParseMarkdown,\n imageRenderMarkdown,\n normalizeImageCaption,\n} from '../imageMarkdown';\nimport { setImageAlign } from '../imageAlign';\nimport { configureEmojiSuggestion } from '../emoji/EmojiSuggestion';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport { usePublishEditorLocale } from '../i18n/store';\nimport { countWords } from '../characterCount';\nimport { SlashMenu } from '../slashMenu/SlashMenuExtension';\nimport { ShortcutPanel } from '../shortcuts/ShortcutPanel';\nimport { KeyboardIcon } from '../icons';\nimport { MarkdownFileDrop } from '../markdownFileDrop';\nimport {\n pendingAnchorExtension,\n setPendingAnchors,\n type PendingAnchor,\n} from '../pendingAnchor';\nimport { selectionKind, type SelectionKind } from '../selectionKind';\nimport { MarkdownPaste } from '../markdownPaste';\nimport { parseCodeTokenAsChartOrCodeBlock } from '../chart/codeBlockChartParse';\nimport { createChart } from '../chart/createChart';\nimport { prepareChartMarkdown } from '../chart/prepareChartMarkdown';\nimport '../styles/chart.css';\nimport '../styles/pending.css';\nimport '../styles/find.css';\nimport styles from '../styles/content.module.css';\nimport type { TocItem } from '../toc/extractToc';\nimport { makeTocGetId } from '../toc/tocSlug';\nimport { CodeBlockView } from './CodeBlockView';\nimport { BlockDragHandle } from './BlockDragHandle';\nimport { EditorToc } from './EditorToc';\nimport { useIsMobile } from '../hooks/useIsMobile';\n\n// 带语言选择器的代码块(自定义 React NodeView)。base 不含代码块,此增强版由编辑器注入。\n// 文案从编辑器 locale store 读取,不写进扩展 options。\nconst CodeBlock = CodeBlockLowlight.extend({\n // Built-in `code` tokens often win over chart's custom fence tokenizer;\n // upgrade ```tmr-chart here so the mid-column card editor renders ChartView.\n parseMarkdown: (token, helpers) =>\n parseCodeTokenAsChartOrCodeBlock(token, helpers, 'codeBlock'),\n addNodeView() {\n return ReactNodeViewRenderer(CodeBlockView);\n },\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { state } = this.editor;\n const { selection } = state;\n const { empty, $anchor } = selection;\n\n // 1) 光标在「非空代码块」内容最前面:拦截,防止拆块漏文字(空块放行可删)\n if (\n empty &&\n $anchor.parent.type.name === this.name &&\n $anchor.parentOffset === 0 &&\n $anchor.parent.content.size > 0\n ) {\n return true;\n }\n\n // 2) 光标在「代码块后方块」的开头:先选中整个代码块(二次确认),不直接删\n if (empty && $anchor.parentOffset === 0) {\n const before = $anchor.before($anchor.depth);\n const prevNode = state.doc.resolve(before).nodeBefore;\n if (prevNode?.type.name === this.name) {\n const codeBlockPos = before - prevNode.nodeSize;\n this.editor.chain().setNodeSelection(codeBlockPos).run();\n return true;\n }\n }\n\n return false;\n },\n };\n },\n});\n\n// 块级图片:Backspace 二次确认删除(与代码块一致)+ 可缩放(官方 ResizableNodeView)\n// + 尺寸保真的 markdown 往返(见 imageMarkdown.ts)。导出供测试与自建管线使用。\nexport const ImageWithConfirmDelete = Image.extend({\n addAttributes() {\n return {\n ...(Image.config.addAttributes?.call(this) ?? {}),\n align: imageAlignAttribute,\n caption: imageCaptionAttribute,\n };\n },\n\n parseHTML() {\n // figure 规则要排在 img 前面:带描述(图注)的图整体归 figure\n return [\n imageFigureParseRule(),\n {\n tag: this.options.allowBase64\n ? 'img[src]'\n : 'img[src]:not([src^=\"data:\"])',\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return imageNodeHtml({\n attrs: node.attrs,\n htmlAttributes: mergeAttributes(\n this.options.HTMLAttributes,\n HTMLAttributes,\n ),\n });\n },\n\n renderMarkdown: imageRenderMarkdown,\n parseMarkdown: imageParseMarkdown,\n markdownTokenizer: imageMarkdownTokenizer,\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { state } = this.editor;\n const { selection } = state;\n const { empty, $anchor } = selection;\n if (empty) {\n const nodeBefore = $anchor.nodeBefore;\n if (nodeBefore?.type.name === this.name) {\n const imagePos = $anchor.pos - nodeBefore.nodeSize;\n this.editor.chain().setNodeSelection(imagePos).run();\n return true;\n }\n }\n return false;\n },\n // 图片对齐:键位对齐官方 image-align-button(L 左 / E 中 / R 右),\n // 选中图片或光标紧邻图片时生效。工具函数见 imageAlign.ts。\n 'Alt-Shift-l': () => setImageAlign(this.editor, 'left'),\n 'Alt-Shift-e': () => setImageAlign(this.editor, 'center'),\n 'Alt-Shift-r': () => setImageAlign(this.editor, 'right'),\n };\n },\n\n addNodeView() {\n // 官方实现:resize 未启用 / SSR(无 document)时返回 null,落到默认 DOM 渲染\n const createView = Image.config.addNodeView?.call(this);\n if (!createView) return null;\n\n // 本库扩展的缩放上限:官方 `Image` 的 resize 配置只有 min、没有 max 入口,但\n // ResizableNodeView 有公开的 maxSize 字段(applyConstraints 每次拖拽都会读)。\n // maxWidth / maxHeight 由 <MarkdownWysiwygEditor> 展开进 resize 对象传到这里。\n const resizeOptions = this.options.resize as\n | { maxWidth?: number; maxHeight?: number }\n | false\n | undefined;\n const explicitMaxWidth =\n resizeOptions && typeof resizeOptions === 'object'\n ? resizeOptions.maxWidth\n : undefined;\n const explicitMaxHeight =\n resizeOptions && typeof resizeOptions === 'object'\n ? resizeOptions.maxHeight\n : undefined;\n\n return (props: NodeViewRendererProps) => {\n const nodeView = createView(props);\n if (!nodeView) return nodeView;\n\n // removeHandles / element / maxSize 是 ResizableNodeView 的成员——官方升级若动\n // 它们,「只读无手柄 / 程序化改尺寸视觉同步 / 缩放上下限」测试会先炸。\n const resizable = nodeView as unknown as {\n element?: HTMLElement;\n maxSize?: { width?: number; height?: number };\n removeHandles?: () => void;\n };\n\n // 「打开就是只读」的文档:官方只在收到第一个 update 事件后才同步 editable(摘手柄),\n // 等不到事件的手柄会一直露着、还能拖出残影(只读态拖了不提交,样式却改了)。\n // 创建时补摘一次;之后切回编辑态,官方自己的 update 监听会把手柄挂回来。\n if (!props.editor.isEditable) resizable.removeHandles?.();\n\n // 缩放上限:显式配置优先;没配则跟随内容区宽度(图片拖不出布局)。\n // 注意两点:其一,NodeView 是在 editor.view 构造**过程中**创建的,此刻\n // `editor.view` 还不可访问(tiptap 会抛 \"view is not available\")——所以初始\n // 只设显式上限,动态上限推迟到 mousedown;其二,监听挂在容器上、capture 阶段,\n // 先于官方的手柄 mousedown 处理跑,每次拖拽开始前刷新,窗口变化后也能跟上。\n const applyMaxSize = () => {\n resizable.maxSize =\n explicitMaxWidth || explicitMaxHeight\n ? { width: explicitMaxWidth, height: explicitMaxHeight }\n : { width: props.editor.view.dom.clientWidth || undefined };\n };\n if (explicitMaxWidth || explicitMaxHeight) applyMaxSize();\n\n // 图注(描述):挂进 wrapper(宽度 = 图片宽)→ text-align:center 就是\n // 「图片底部中间」。wrapper 因此变高,而 left/right 手柄的 top:50% 是相对\n // wrapper 的(会偏到描述上)——把图片的真实高度写进 --tmr-image-h,\n // CSS 那边用 calc 的降级写法取它(见 content.module.css)。\n let syncCaption: ((attrs: Record<string, unknown>) => void) | null = null;\n if (nodeView.dom instanceof HTMLElement && resizable.element) {\n const { element } = resizable;\n const container = nodeView.dom;\n const captionEl = document.createElement('div');\n captionEl.className = 'tmr-image-caption';\n captionEl.dataset.imageCaption = '';\n (element.parentElement ?? container).appendChild(captionEl);\n\n const syncHandleTop = () => {\n const height = element.offsetHeight;\n if (height > 0) {\n container.style.setProperty('--tmr-image-h', `${height}px`);\n } else {\n // 高度拿不到(图片未加载 / 无布局环境)→ 撤掉变量,CSS 落回 50%\n container.style.removeProperty('--tmr-image-h');\n }\n };\n syncCaption = (attrs) => {\n const caption = normalizeImageCaption(attrs.caption) ?? '';\n captionEl.textContent = caption;\n captionEl.hidden = caption === '';\n syncHandleTop();\n };\n syncCaption(props.node.attrs);\n element.addEventListener('load', syncHandleTop);\n container.addEventListener('mousedown', applyMaxSize, true);\n }\n\n // 官方 onUpdate 把 width/height 划进「拖拽自己管」的集合直接跳过,于是程序化改\n // 尺寸(resetImageSize 清掉、宿主 updateAttributes)后数据变了、视觉不动。\n // 这里在 update 后把 style 对齐 attrs(图注文案也顺带同步);拖拽路径不经过\n // update,互不干扰。\n if (resizable.element) {\n const { element } = resizable;\n const originalUpdate = nodeView.update?.bind(nodeView);\n if (originalUpdate) {\n nodeView.update = (...args) => {\n const ok = originalUpdate(...args);\n if (ok) {\n const attrs = (args[0] as { attrs: Record<string, unknown> }).attrs;\n element.style.width = attrs.width ? `${attrs.width}px` : '';\n element.style.height = attrs.height ? `${attrs.height}px` : '';\n syncCaption?.(attrs);\n }\n return ok;\n };\n }\n }\n\n return nodeView;\n };\n },\n});\n\n/** 浮动条相对其定位上下文的偏移(数字按 px)。内部路径的上下文是编辑器,portal 路径是宿主容器。 */\nexport interface FindBarOffset {\n top?: number | string;\n right?: number | string;\n bottom?: number | string;\n left?: number | string;\n}\n\nconst DEFAULT_FIND_BAR_OFFSET: FindBarOffset = { top: 4, right: 4 };\n\nfunction findBarOffsetStyle(offset: FindBarOffset): CSSProperties {\n const px = (v: number | string | undefined) =>\n typeof v === 'number' ? `${v}px` : v;\n return {\n position: 'absolute',\n top: px(offset.top),\n right: px(offset.right),\n bottom: px(offset.bottom),\n left: px(offset.left),\n };\n}\n\n/**\n * 全文统计条。数字来自官方 CharacterCount 的 storage,`useEditorState` 只在\n * 字符数 / 词数变化时重渲染。\n */\nfunction CharacterCountStatus({\n editor,\n locale,\n}: {\n editor: Editor;\n locale: LocaleCode;\n}) {\n const t = getLocaleBundle(locale).characterCount;\n const counts = useEditorState({\n editor,\n selector: ({ editor: current }) => {\n const storage = current?.storage.characterCount;\n if (!storage) return { characters: 0, words: 0 };\n return {\n characters: storage.characters(),\n words: storage.words(),\n };\n },\n });\n\n return (\n <div\n className={styles.charCount}\n role=\"group\"\n aria-label={t.label}\n data-characters={counts.characters}\n data-words={counts.words}\n >\n <span>{t.characters(counts.characters)}</span>\n <span className={styles.charCountSep} aria-hidden=\"true\">\n ·\n </span>\n <span>{t.words(counts.words)}</span>\n </div>\n );\n}\n\n/**\n * 这次按键归本编辑器管吗。Cmd/Ctrl+F 与 Esc 共用同一套边界:\n * - 焦点在本编辑器内 → 管\n * - 焦点不在任何输入控件里(body)且页面上只有本编辑器 → 也管(单编辑器页面点完工具栏能直接按)\n * - 其余一律不管:同页多编辑器不会一起抢键,宿主的输入框与其它浮层也不会被夺走按键。\n */\nfunction editorOwnsKeyboard(editor: Editor): boolean {\n const active = document.activeElement;\n const inEditor = !!active && editor.view.dom.contains(active);\n if (inEditor || editor.isFocused) return true;\n const nothingFocused = active === null || active === document.body;\n return nothingFocused && document.querySelectorAll('.ProseMirror').length <= 1;\n}\n\nexport interface MarkdownWysiwygEditorHandle {\n /** 取当前正文的 markdown 字符串。 */\n getMarkdown: () => string;\n /** 取当前正文的 HTML 字符串。 */\n getHTML: () => string;\n /** 取当前正文的 Tiptap JSON。 */\n getJSON: () => Record<string, unknown>;\n /** 底层 Tiptap Editor 实例(可能为 null,未就绪时)。 */\n getEditor: () => Editor | null;\n /** 聚焦评论:设 active + 滚动 + 光标落到区间起点。找不到返回 false。 */\n focusComment: (commentId: string) => boolean;\n /** 按文档顺序跳转评论(next / prev),返回目标 commentId。 */\n nextComment: (dir?: 'next' | 'prev') => string | null;\n /** 当前 doc 内已锚定的去重 commentId 列表(文档顺序)。 */\n getCommentIds: () => string[];\n /** 打开编辑器自带的查找浮动条(宿主自己绑快捷键/按钮时用)。findBar={false} 时不生效。 */\n openFind: () => void;\n /** 收起自带浮动条,清空高亮并把焦点还给编辑器。findBar={false} 时不生效。 */\n closeFind: () => void;\n /**\n * 反转自带浮动条:关着就开、开着就关。工具栏那个带 active 态的放大镜接它\n * (`onSearch={() => handle.current?.toggleFind()}`)。findBar={false} 时不生效。\n */\n toggleFind: () => void;\n}\n\n/** 图片缩放的透传选项(官方 `@tiptap/extension-image` 的 resize 配置)。 */\nexport interface ImageResizeOptions {\n /**\n * 手柄方向,默认左右两条竖条(对齐官方 image-upload-node demo 的形态)。\n * 例:`['top-right', 'bottom-right']` 只留右侧两角;八个方向全给则四角 + 四边。\n */\n directions?: ResizableNodeViewDirection[];\n /** 最小宽(px),默认 80。 */\n minWidth?: number;\n /** 最小高(px),默认 80。 */\n minHeight?: number;\n /**\n * 最大宽(px)。**默认跟随编辑器内容区宽度**(拖不出布局,窗口变化时在下次拖拽前刷新\n * 上限);显式传值则以此为准。官方 `Image` 的 resize 没有 max 入口,由本库注入\n * `ResizableNodeView.maxSize`(该字段官方是公开的,拖拽约束每次都会读)。\n */\n maxWidth?: number;\n /** 最大高(px)。默认不单独限制(等比缩放时高度随宽度走)。 */\n maxHeight?: number;\n /**\n * 是否始终锁定宽高比,默认 true(本库默认)。等比下\"编辑态精确尺寸\"与只读渲染的\n * 按比例自适应(CSS `height: auto`)数学等价;显式关掉后,非等比拖出的图在只读\n * 渲染里会按原图比例回弹——markdown 里存的仍是精确值。\n */\n alwaysPreserveAspectRatio?: boolean;\n}\n\nexport interface MarkdownWysiwygEditorProps {\n /** 初始 markdown 内容。 */\n initialMarkdown?: string;\n /**\n * 是否可编辑,默认 true。false = 只读渲染,与编辑态同源同路径:\n * 同一套扩展 / NodeView / 样式,交互件(代码块语言选择、删除等)自动收起。\n * 只读态不应用评论锚定(comments 被忽略),与 MarkdownPreview 语义一致。\n */\n editable?: boolean;\n /** 内置界面语言,默认英文。工具栏等并列组件需各自传同一个值。目录跟着编辑器。 */\n locale?: LocaleCode;\n /**\n * 初始脚注来源:按 index 对齐 `[^n]`,写入 citationRef 的 url/title。\n * 仅影响初始 content;后续插入请用 `insertMarkdown(editor, md, sources)`。\n */\n sources?: SourceRef[];\n /**\n * 脚注圆标 NodeView 插槽。消费方用 Popover 包住 `defaultDom`,\n * 用 `index` 查自己的数据源。仅初始化时生效。\n */\n renderCitation?: RenderCitation;\n placeholder?: string;\n /** editor 实例就绪 / 销毁时回调,供外部工具栏使用。 */\n onEditorReady?: (editor: Editor | null) => void;\n /**\n * 目录变化回调(正文标题增删改时)。编辑器自己画左侧目录,\n * 这个回调只把同一份 `TocItem[]` 交给还想自己用数据的宿主。\n */\n onTocChange?: (items: TocItem[]) => void;\n /**\n * 编辑器内目录(默认 true)。有标题时在左缘显示短横线,悬停向右展开,\n * 点击滚到对应标题。宽度小于 768px 时不显示。\n */\n toc?: boolean;\n /**\n * 粘贴纯文本时启发式检测 markdown 并自动转富文本(Shift+粘贴保持纯文本)。\n * 默认 true。仅初始化时生效。\n */\n markdownPaste?: boolean;\n /**\n * 支持把 .md / .markdown 文件拖拽或粘贴进编辑器,解析后插入到落点/光标处。\n * 默认 true。仅初始化时生效。\n */\n markdownFileDrop?: boolean;\n /** 追加的 Tiptap 扩展(在内置扩展之后注册)。 */\n extraExtensions?: AnyExtension[];\n /**\n * 「这段正在被 AI 改写」的高亮:宿主在提交请求时给上、回填或冲突时传空数组清空。\n * 走 decoration,**不进 markdown、不进 undo 历史**,能盖住表格与图表这类块级节点。\n */\n pendingAnchors?: PendingAnchor[];\n /** 点击待改写高亮区域时回调,给出锚点 id(宿主可用来跳回/取消)。 */\n onAnchorClick?: (id: string) => void;\n /**\n * 选区变化。宿主用它做「选中即出现引用」——塌缩成光标也会报(`empty: true`),\n * 好让引用跟着消失;`kind` 区分普通文字 / 表格单元格 / 整节点(图表)。\n */\n onSelectionChange?: (selection: {\n from: number;\n to: number;\n empty: boolean;\n kind: SelectionKind;\n }) => void;\n /** 附加到滚动容器的 class。 */\n className?: string;\n /**\n * 编辑态评论锚定:评论列表(含已解码 segments)。装载时映射为 commentAnchor\n * mark 并高亮;markdown 导出自动剥离,只读态(MarkdownPreview)不渲染评论。\n * 注意:数组引用变化会触发重新铺 mark,宿主应 memo 该数组。\n */\n comments?: CommentRef[];\n /** 受控 active 评论 id(sidebar 联动)。null = 无 active。 */\n activeCommentId?: string | null;\n /** 编辑器内点击 mark / gutter 时回调。 */\n onCommentClick?: (payload: CommentClickPayload) => void;\n /** active 变化回调(点击 mark / gutter / nextComment 时)。 */\n onActiveCommentChange?: (commentId: string | null) => void;\n /** 是否在 block 左缘渲染评论 gutter 气泡,默认 true。 */\n showCommentGutter?: boolean;\n /**\n * 编辑器内点评论 mark 是否可交互(设 active / 上报 click),默认 true。\n * 只靠侧栏单项驱动滚动定位时传 false。\n */\n commentInteractive?: boolean;\n /**\n * 启用查找替换(默认 true):注册官方 `@tiptap/extension-find-and-replace`\n * (自带命令 `setSearchTerm / replace / replaceAll / goToNextResult …` 与\n * `editor.storage.findAndReplace`)。传 false 则连扩展一起不注册——此时命令与 storage 都不存在,\n * 宿主的自绘面板也无从驱动。\n */\n findReplace?: boolean;\n /**\n * 是否由编辑器渲染浮动条(默认跟随 {@link findReplace})。传 false = **定位交给宿主**:\n * 扩展照旧注册,但编辑器不出条子,宿主自己在任意位置渲染 `<FindReplaceBar editor={…} />`\n * (与工具栏同一套分工——组件归库,摆位归宿主)。此时 {@link findShortcut} 与\n * `handle.openFind/closeFind` 一并失效,因为内部没有条子可开。\n */\n findBar?: boolean;\n /**\n * 把自带的浮动条挂到宿主的容器里(popup container 模式):给元素或返回元素的函数\n * 即可,开合状态与 Cmd/Ctrl+F 仍归库——只换挂载点。适合条子被 `overflow: hidden` 祖先裁掉、\n * 或想让它落在自己的头部 / 侧栏里。挂进去后**定位由宿主负责**(库不再加绝对定位),\n * 且容器若在主题子树之外,记得把 `--tmr-*` 变量也带到那里。\n * 返回 null / 不传时回落到编辑器内自带的浮动条。\n */\n findBarContainer?: HTMLElement | null | (() => HTMLElement | null);\n /**\n * 浮动条相对其定位上下文的偏移,默认 `{ top: 4, right: 4 }`(右上角)。数字按 px,也收 CSS 字符串。\n * 内部路径的上下文是编辑器(粘在滚动容器顶部的那层锚点),`findBarContainer` 路径的上下文是那个容器\n * ——那条路径下库会保证容器是定位上下文(computed position 为 static 时补 `position: relative`)。\n */\n findBarOffset?: FindBarOffset;\n /**\n * 焦点在编辑器内时接管 Cmd/Ctrl+F 打开**编辑器自带的**浮动条(默认 true;`findBar={false}`\n * 时不接管,免得抢了键却不弹东西)。传 false 则只保留 `handle.openFind()`——例如宿主想\n * 保留浏览器原生查找,或自己摆入口。焦点不在编辑器内时不接管,同页多编辑器不会互相抢。\n */\n findShortcut?: boolean;\n /**\n * 浮动条开合状态变化的通知(条子开着时 `handle.openFind()`、Esc / × 关掉都会触发)。\n * 典型用法是驱动别处的入口按钮:`<EditorToolbar onSearch={open} searchActive={isOpen} />`。\n * 只在真正变化时回调,挂载时不会先报一次 `false`;报的是「条子真的在屏幕上」——\n * `findBar={false}`(宿主自己摆条子)时恒为 `false`,不会因为 `openFind()` 空转就亮起入口。\n */\n onFindOpenChange?: (open: boolean) => void;\n /**\n * 启用快捷键抽屉:编辑器右下角出现键盘悬浮按钮,点开为\n * 「格式 / 快捷键 / Markdown」三列对照抽屉(默认 true,只读态隐藏)。\n */\n shortcutPanel?: boolean;\n /**\n * 把快捷键悬浮键挂到宿主的容器里(与 `findBarContainer` 同一套):\n * 给元素或返回元素的函数即可,开合状态与抽屉归库——只换按钮落点。\n * 挂进去后**定位由宿主负责**(库不再给按钮加粘性定位与右下角外边距),\n * 适合把按钮叠在宿主自己的浮层控件旁边(如「回到顶部」按钮上方)。\n * 返回 null / 不传时回落到编辑器内容右下的粘性按钮。\n */\n shortcutFabContainer?: HTMLElement | null | (() => HTMLElement | null);\n /**\n * 启用斜杠菜单:键入 `/`(行首或空白后)唤起块级插入弹窗(默认 true)。\n * 代码块内不触发,表格单元格内触发;只读态不激活。\n * 追加自定义命令项请传 `extraExtensions` 注册 `SlashMenu.configure({ items })`。\n */\n slashMenu?: boolean;\n /**\n * 智能标点(官方 `@tiptap/extension-typography`,MIT,不是 Pro)。\n * 键入时把直引号收成弯引号、`--` 收成破折号、`...` 收成省略号,以及箭头、©、分数等。\n * 代码块和行内代码里不触发;Backspace 撤销刚发生的替换。`---` 仍是分隔线。\n * 默认 true。仅初始化时生效。\n */\n typography?: boolean;\n /**\n * 表情建议列表:键入 `:`(行首或空白后)弹出,选中后插入 emoji 节点。\n * Markdown 存成 `:shortcode:`。传 false 只是不弹列表——节点、完整短码输入和往返仍在。\n * 只读态不弹出。默认 true。仅初始化时生效。\n */\n emojiSuggestion?: boolean;\n /**\n * 块拖拽(对齐 Tiptap Drag Context Menu):悬停块时左侧出现手柄,拖动重排,\n * 点击打开菜单(转成别的块 / 复制 / 再制一份 / 删除,图片还可下载)。\n * 手柄旁的「+」在块首插入 `/`,唤起斜杠菜单(`slashMenu={false}` 时不显示加号)。\n * 默认 true。只读态和窄屏(宽度小于 768px)不显示,并收起左侧留白。\n */\n blockDrag?: boolean;\n /**\n * 图片上传(对齐官方 ImageUploadNode 的交互):配置后工具栏的图片按钮变成\n * 「插入上传块」——文档里出现拖拽 / 点击的占位块,上传进度就地显示,全部成功后就地\n * 替换为图片。`upload` 是唯一必填项(网络 I/O 归宿主),签名 `(file, onProgress,\n * signal) => Promise<url>`;收 `signal` 后宿主应在移除 / 清空 / 占位块被删时中断请求。\n *\n * 挂在这里而不是工具栏的 `onImageUpload`:按钮的行为由「编辑器里有没有注册\n * imageUpload 扩展」决定,两处都传会以本配置为准。未配置时工具栏走旧的直传路径\n * (点按钮直接开文件框 → 上传 → 插图片)。仅初始化时生效。\n */\n imageUpload?: ImageUploadConfig;\n /**\n * 图片缩放(默认开):拖手柄改尺寸,松手把整数像素写进节点 attrs;markdown 里带尺寸的\n * 图以 `<img src alt width height>` 保真(没拖过的图保持标准 `![alt](url)`,见\n * imageMarkdown.ts)。传 `false` 关闭;传对象透传官方 resize 配置。仅初始化时生效。\n */\n imageResize?: boolean | ImageResizeOptions;\n /**\n * 全文统计(默认关):传 `true` 才注册 MIT 包 `@tiptap/extensions` 的 CharacterCount\n * (开源扩展,不是付费的 `@tiptap-pro`),在编辑器底部实时显示字符数与词数。\n * 统计条是本库自绘。词数把每个汉字算作 1 个词(该扩展默认按空格切,整段中文会是 1)。\n * 数字也可从 `editor.storage.characterCount` 读取。仅初始化时生效。\n */\n characterCount?: boolean;\n}\n\nfunction sameTocItems(a: TocItem[], b: TocItem[]): boolean {\n if (a.length !== b.length) return false;\n return a.every(\n (item, index) =>\n item.id === b[index].id &&\n item.level === b[index].level &&\n item.text === b[index].text,\n );\n}\n\n/**\n * 所见即所得 Markdown 编辑器(基于 Tiptap v3 + 官方 @tiptap/markdown)。\n * 内容进出均为 markdown 字符串。工具栏由外部渲染——通过 onEditorReady 拿到 editor 实例。\n */\nexport const MarkdownWysiwygEditor = forwardRef<\n MarkdownWysiwygEditorHandle,\n MarkdownWysiwygEditorProps\n>(function MarkdownWysiwygEditor(\n {\n initialMarkdown = '',\n editable = true,\n locale = 'en',\n sources,\n renderCitation,\n placeholder,\n onEditorReady,\n onTocChange,\n toc = true,\n markdownPaste = true,\n markdownFileDrop = true,\n extraExtensions,\n className,\n comments,\n activeCommentId,\n onCommentClick,\n onActiveCommentChange,\n showCommentGutter = true,\n commentInteractive = true,\n pendingAnchors,\n onAnchorClick,\n onSelectionChange,\n findReplace = true,\n findBar = findReplace,\n findBarContainer,\n findBarOffset = DEFAULT_FIND_BAR_OFFSET,\n findShortcut = true,\n onFindOpenChange,\n slashMenu = true,\n typography = true,\n emojiSuggestion = true,\n blockDrag = true,\n shortcutPanel = true,\n shortcutFabContainer,\n imageUpload,\n imageResize = true,\n characterCount = false,\n },\n ref,\n) {\n const preparedInitial = prepareChartMarkdown(\n enrichMarkdownCitations(initialMarkdown, sources ?? []),\n );\n\n const blockDragMobile = useIsMobile(768);\n const showBlockDrag = blockDrag && editable && !blockDragMobile;\n\n const [findOpen, setFindOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n const [tocItems, setTocItems] = useState<TocItem[]>([]);\n const findInputRef = useRef<HTMLInputElement | null>(null);\n const onTocChangeRef = useRef(onTocChange);\n onTocChangeRef.current = onTocChange;\n const tocItemsRef = useRef<TocItem[]>([]);\n\n const editor = useEditor({\n extensions: [\n ...baseExtensions.map((ext) =>\n ext.name === 'emoji'\n ? configureEmojiSuggestion(ext, emojiSuggestion)\n : ext,\n ),\n CodeBlock.configure({ lowlight }),\n ImageWithConfirmDelete.configure({\n inline: false,\n // 图片缩放(默认开、默认等比、默认左右两条手柄):false 关掉;对象透传官方配置\n // ——等比与手柄方向都有本库默认,显式传值可覆盖。\n resize:\n imageResize === false\n ? false\n : {\n enabled: true,\n alwaysPreserveAspectRatio: true,\n directions: ['left', 'right'] as ResizableNodeViewDirection[],\n // 默认下限。上限默认跟随内容区宽度(maxWidth/maxHeight 由 addNodeView\n // 包装读取并注入 maxSize,见 ImageWithConfirmDelete)。\n minWidth: 80,\n minHeight: 80,\n ...(typeof imageResize === 'object' ? imageResize : null),\n },\n }),\n // 图片上传占位块:空态拖拽 / 点选 → 进度就地 → 成功原地换成 image。\n // 不配置 imageUpload 就不注册(工具栏退回旧的直传路径)。\n ...(imageUpload ? [ImageUploadNode.configure(imageUpload)] : []),\n ImportPlaceholder,\n // 空文档占位符:官方 Placeholder 把 data-placeholder 属性与\n // is-empty / is-editor-empty 类打在**空文本块节点**上——CSS\n // (styles/content.module.css)正是在节点上 attr(data-placeholder)\n // 取值。早先只把属性挂在根 div 上,选择器永远匹配不到,占位符从没显示过。\n // showOnlyWhenEditable 默认 true,只读态自动不显示。\n ...(placeholder ? [Placeholder.configure({ placeholder })] : []),\n createChart({ editable: false }),\n createCitationRef({ renderCitation }),\n CommentMark,\n commentAnchorExtension({\n showGutter: showCommentGutter,\n interactive: commentInteractive,\n }),\n Markdown,\n pendingAnchorExtension,\n // 官方查找替换:只出命令 / storage / 装饰,UI 自绘(见 FindReplaceBar)。\n // - injectCSS 关掉:它默认会往页面插一个 <style>,绕开宿主的 --tmr-* 主题与 style.css,\n // 命中样式改由 src/styles/find.css 承担。\n // - searchDebounceMs 关掉:官方防抖走 setTimeout,抛错会落在异步回调里兜不住;\n // 防抖改由浮动条自己做(见 findReplace.ts)。\n ...(findReplace\n ? [FindAndReplace.configure({ injectCSS: false, searchDebounceMs: 0 })]\n : []),\n // 失焦选区:MIT 的 Selection。非空文本选区失焦后打上 .tmr-blur-selection,\n // 样式在 content.module.css。节点选区(图片、代码块整块)官方不保留。\n Selection.configure({ className: 'tmr-blur-selection' }),\n // 字数统计:MIT 的 @tiptap/extensions CharacterCount(不是 @tiptap-pro)。\n // 只调用它的 storage,统计条是本库自绘。无 limit,不裁剪正文。\n // 词数换成自写的 countWords,否则整段中文会被默认的按空格切分算成 1 个词。\n ...(characterCount\n ? [CharacterCount.configure({ wordCounter: countWords })]\n : []),\n TableOfContents.configure({\n getId: makeTocGetId(),\n // 官方扩展在滚动时也会 onUpdate(只为刷新 isActive)。目录项没变就不通知,\n // 否则每次滚动都 setState。回调走 ref:扩展只在创建时绑一次。\n onUpdate: (anchors) => {\n const next = anchors\n .filter((a) => a.originalLevel <= 6 && a.textContent.trim())\n .map((a) => ({\n id: a.id,\n level: a.originalLevel,\n text: a.textContent,\n }));\n if (sameTocItems(tocItemsRef.current, next)) return;\n tocItemsRef.current = next;\n setTocItems(next);\n onTocChangeRef.current?.(next);\n },\n }),\n ...(markdownPaste ? [MarkdownPaste] : []),\n ...(markdownFileDrop ? [MarkdownFileDrop] : []),\n // 斜杠菜单(键入 / 唤起):默认开,只读态由 suggestion 状态机自身短路\n ...(slashMenu ? [SlashMenu] : []),\n // 智能标点:输入规则,不增加节点,所以不进 baseExtensions。\n ...(typography ? [Typography] : []),\n ...(extraExtensions ?? []),\n ],\n content: preparedInitial,\n contentType: 'markdown',\n editable,\n // Next.js SSR:服务端不立即渲染,避免 hydration 不一致\n immediatelyRender: false,\n editorProps: {\n attributes: {\n class: styles.editorContent,\n },\n },\n });\n\n usePublishEditorLocale(editor, locale);\n\n useEffect(() => {\n onEditorReady?.(editor);\n return () => onEditorReady?.(null);\n }, [editor, onEditorReady]);\n\n // 运行时切换编辑/只读(同一实例,NodeView 交互件随 editor.isEditable 收起)。\n useEffect(() => {\n editor?.setEditable(editable);\n }, [editor, editable]);\n\n /**\n * 打开查找条。带着「再按一次」的语义:已经开着时聚焦并全选当前查询(首次打开由条子自己的\n * autoFocus 负责,此时查询多半是空串,`select()` 是空操作)。\n */\n const openFind = useCallback(() => {\n setFindOpen(true);\n requestAnimationFrame(() => findInputRef.current?.select());\n }, []);\n\n const closeFind = useCallback(() => {\n setFindOpen(false);\n // 焦点归还:Tiptap 的 focus 命令内部就是 requestAnimationFrame 延后执行的\n // (`delayedFocus`),正好落在 React 卸载浮动条之后——否则被卸载的输入框会把焦点丢给 body。\n editor?.commands.focus();\n }, [editor]);\n\n /**\n * 反转查找条:关着就开、开着就关。工具栏那个放大镜接的是它——带 active 态的按钮点一下得能灭。\n * `openFind()` 保持「已开着时重聚焦并全选查询」的语义,和 Cmd/Ctrl+F(同一个手势)一致。\n */\n const toggleFind = useCallback(() => {\n if (findOpen) closeFind();\n else openFind();\n }, [findOpen, closeFind, openFind]);\n\n // 开合状态上报宿主(回调走 ref 防 stale)。报的是「条子真的在屏幕上」——`findBar={false}`\n // 时宿主自己摆条子,`findOpen` 翻起来也不该点亮别处的入口。只在真正变化时回调,\n // 挂载时不先报一次 false。\n const findBarOpen = findOpen && findBar && findReplace;\n const onFindOpenChangeRef = useRef(onFindOpenChange);\n onFindOpenChangeRef.current = onFindOpenChange;\n // 初值取 `false`(`findOpen` 的初值):挂载那次比较相等,不会回调一次「本来就是 false」。\n const notifiedFindOpenRef = useRef(false);\n useEffect(() => {\n if (notifiedFindOpenRef.current === findBarOpen) return;\n notifiedFindOpenRef.current = findBarOpen;\n onFindOpenChangeRef.current?.(findBarOpen);\n }, [findBarOpen]);\n\n // 条子随本实例一起消失时(换 key、换文档、切路由),宿主的入口不该继续亮着——新实例的\n // `notifiedFindOpenRef` 初值是 false、`findBarOpen` 也是 false,两者相等就不会回调,于是\n // 上一实例报过的那个 `true` 没人撤销。所以卸载时补发一次。StrictMode 的模拟卸载不会误报\n // (那时条子并没开着,值就是 false)。\n const findBarOpenRef = useRef(findBarOpen);\n findBarOpenRef.current = findBarOpen;\n useEffect(\n () => () => {\n if (findBarOpenRef.current) onFindOpenChangeRef.current?.(false);\n },\n [],\n );\n\n // Cmd/Ctrl+F → 打开查找条。接管边界见 editorOwnsKeyboard()。\n // 只有容器里真有那条浮动条(findBar)时才绑:否则会白抢浏览器原生查找,却什么都不弹。\n useEffect(() => {\n if (!editor || !findReplace || !findBar || !findShortcut) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) {\n return;\n }\n if (event.key !== 'f' && event.key !== 'F') return;\n if (!editorOwnsKeyboard(editor)) return;\n event.preventDefault();\n openFind();\n };\n document.addEventListener('keydown', onKeyDown, true);\n return () => document.removeEventListener('keydown', onKeyDown, true);\n }, [editor, findReplace, findBar, findShortcut, openFind]);\n\n // Esc → 关掉查找条。条子自己那份 onKeyDown 只在焦点位于条子内时才收得到,用户点回正文继续\n // 编辑后想收条子就只能去点 ×;这里按同一套边界(editorOwnsKeyboard)补上「焦点在正文 /\n // 页面只有本编辑器且焦点在 body」的情况。消费掉这次 Esc(preventDefault + stopPropagation),\n // 免得宿主的浮动层跟着一起关;输入法组合中的 Esc 是「取消候选词」,放行。\n //\n // 注意**不要**用 `event.defaultPrevented` 判断「内层已经处理过、让给它」:实测在真实编辑器里\n // Escape 一定已被 preventDefault——prosemirror-view 的 keydown 一旦有 handleKeyDown 返回 true\n // 就 preventDefault(`node_modules/prosemirror-view/src/input.ts:136`),斜杠菜单等就在这条路上。\n // 那样写这条分支永远不会走。边界交给 editorOwnsKeyboard:焦点在别的浮层 / 宿主的输入框里时\n // 它本来就是 false,不会去抢。\n useEffect(() => {\n if (!editor || !findBarOpen) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n if (isComposingKeyEvent(event)) return;\n if (!editorOwnsKeyboard(editor)) return;\n event.preventDefault();\n event.stopPropagation();\n closeFind();\n };\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [editor, findBarOpen, closeFind]);\n\n // 评论列表 → 铺 mark。用 commentId 签名做防抖:宿主每次渲染传新数组引用时\n // 不会反复清/铺(清空再重铺会丢掉 mark 随编辑移动后的位置)。\n const commentsSignatureRef = useRef<string>('');\n useEffect(() => {\n // 只读态不接评论(与库的「评论锚定 = 编辑会话专属」契约一致)。\n if (!editor || !editable) return;\n const signature = (comments ?? [])\n .map((c) => `${c.commentId}:${c.segments.length}`)\n .join('|');\n if (signature === commentsSignatureRef.current) return;\n commentsSignatureRef.current = signature;\n applyCommentAnchorsToEditor(editor, comments ?? []);\n }, [editor, editable, comments]);\n\n // active 同步到插件(mark/block 高亮装饰);滚动定位走 handle.focusComment。\n useEffect(() => {\n if (!editor) return;\n editor.view.dispatch(\n editor.state.tr.setMeta(\n COMMENT_ACTIVE_META,\n activeCommentId ?? null,\n ),\n );\n }, [editor, activeCommentId]);\n\n // 待改写高亮:会话级装饰,不进 markdown / 不进 undo(宿主推入,回填或冲突时清空)。\n useEffect(() => {\n if (!editor) return;\n setPendingAnchors(editor, pendingAnchors ?? []);\n }, [editor, pendingAnchors]);\n\n // 点击待改写高亮 → 上报锚点 id。走 DOM 监听而不是插件 meta:这里只要「点了哪一段」。\n const onAnchorClickRef = useRef(onAnchorClick);\n useEffect(() => {\n onAnchorClickRef.current = onAnchorClick;\n }, [onAnchorClick]);\n useEffect(() => {\n if (!editor) return;\n const dom = editor.view.dom;\n const onClick = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n const id = target?.closest?.('[data-pending-id]')?.getAttribute('data-pending-id');\n if (id) onAnchorClickRef.current?.(id);\n };\n dom.addEventListener('click', onClick);\n return () => dom.removeEventListener('click', onClick);\n }, [editor]);\n\n // 选区变化:宿主据此做「选中即出现引用 / 引用跟着选区走」。\n const onSelectionChangeRef = useRef(onSelectionChange);\n useEffect(() => {\n onSelectionChangeRef.current = onSelectionChange;\n }, [onSelectionChange]);\n useEffect(() => {\n if (!editor) return;\n const onSelectionUpdate = () => {\n const { from, to, empty } = editor.state.selection;\n onSelectionChangeRef.current?.({\n from,\n to,\n empty,\n kind: selectionKind(editor.state.selection),\n });\n };\n editor.on('selectionUpdate', onSelectionUpdate);\n return () => {\n editor.off('selectionUpdate', onSelectionUpdate);\n };\n }, [editor]);\n\n // mark / gutter 点击 → 通过 transaction meta 上报宿主(回调走 ref 防 stale)。\n const onCommentClickRef = useRef(onCommentClick);\n const onActiveCommentChangeRef = useRef(onActiveCommentChange);\n onCommentClickRef.current = onCommentClick;\n onActiveCommentChangeRef.current = onActiveCommentChange;\n useEffect(() => {\n if (!editor) return;\n const onTransaction = ({\n transaction,\n }: {\n transaction: { getMeta: (key: string) => unknown };\n }) => {\n const click = transaction.getMeta(COMMENT_CLICK_META) as\n | CommentClickPayload\n | undefined;\n if (click) {\n onCommentClickRef.current?.(click);\n onActiveCommentChangeRef.current?.(click.commentIds[0] ?? null);\n }\n const gutter = transaction.getMeta(COMMENT_GUTTER_META) as\n | { commentIds: string[]; pos: number }\n | undefined;\n if (gutter) {\n onCommentClickRef.current?.({\n commentIds: gutter.commentIds,\n anchorEl: null,\n pos: gutter.pos,\n });\n onActiveCommentChangeRef.current?.(gutter.commentIds[0] ?? null);\n }\n };\n editor.on('transaction', onTransaction);\n return () => {\n editor.off('transaction', onTransaction);\n };\n }, [editor]);\n\n useImperativeHandle(\n ref,\n () => ({\n getMarkdown: () => editor?.getMarkdown?.() ?? '',\n getHTML: () => editor?.getHTML?.() ?? '',\n getJSON: () => (editor?.getJSON?.() as Record<string, unknown>) ?? {},\n getEditor: () => editor ?? null,\n focusComment: (commentId: string) =>\n editor ? focusComment(editor, commentId) : false,\n nextComment: (dir?: 'next' | 'prev') =>\n editor ? nextComment(editor, dir ?? 'next') : null,\n getCommentIds: () => (editor ? collectCommentIds(editor) : []),\n openFind: () => openFind(),\n closeFind,\n toggleFind: () => toggleFind(),\n }),\n [editor, closeFind, openFind, toggleFind],\n );\n\n // 宿主指定了容器就把条子 portal 进去;解析不到则回落到编辑器内的粘性锚点。\n const findBarTarget =\n typeof findBarContainer === 'function'\n ? findBarContainer()\n : (findBarContainer ?? null);\n\n // 条子是绝对定位的:容器得先是定位上下文。宿主容器是 static 时补一个 relative——\n // 不补的话条子会以「最近的定位祖先」为基准(可能是页面),位置会莫名其妙。\n useEffect(() => {\n if (!findBarTarget) return;\n if (getComputedStyle(findBarTarget).position === 'static') {\n findBarTarget.style.position = 'relative';\n }\n }, [findBarTarget]);\n\n const findBarNode =\n editor && findBar && findOpen ? (\n <FindReplaceBar\n editor={editor}\n locale={locale}\n onClose={closeFind}\n inputRef={findInputRef}\n className={styles.findBar}\n style={findBarOffsetStyle(findBarOffset)}\n />\n ) : null;\n\n const shortcutT = getLocaleBundle(locale).shortcut;\n\n // 宿主指定了容器就把悬浮键 portal 进去(定位归宿主),解析不到回落编辑器内粘性按钮。\n // 用 layout effect(同步于提交后、绘制前)解析:函数形式读 ref 时首渲染拿到的是\n // null,等 DOM 提交后 ref 才就绪——这里在绘制前再解析一次,避免闪一帧编辑器内按钮;\n // setState 同值会 bail out,不会死循环。\n const [shortcutFabTarget, setShortcutFabTarget] = useState<HTMLElement | null>(\n null,\n );\n const useIsoLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n useIsoLayoutEffect(() => {\n const target =\n typeof shortcutFabContainer === 'function'\n ? shortcutFabContainer()\n : (shortcutFabContainer ?? null);\n setShortcutFabTarget((prev) => (prev === target ? prev : target));\n });\n\n const shortcutFabNode =\n shortcutPanel && editable ? (\n <button\n type=\"button\"\n className={\n shortcutFabTarget\n ? `${styles.shortcutFab} ${styles.shortcutFabHosted}`\n : styles.shortcutFab\n }\n title={shortcutT.panelTitle}\n aria-label={shortcutT.panelTitle}\n aria-expanded={shortcutsOpen}\n onClick={() => setShortcutsOpen((v) => !v)}\n >\n <KeyboardIcon size={18} />\n </button>\n ) : null;\n\n return (\n <div\n className={styles.editorHost}\n data-block-drag-gutter={showBlockDrag ? '' : undefined}\n >\n {/* 粘性锚点排在正文之前:浮动条跟着最近的滚动容器走,正文滚动时不会被卷上去 */}\n {findBarNode && !findBarTarget ? (\n <div className={styles.findBarAnchor}>{findBarNode}</div>\n ) : null}\n {findBarNode && findBarTarget\n ? createPortal(findBarNode, findBarTarget)\n : null}\n {editor && toc && !blockDragMobile && tocItems.length > 0 ? (\n <EditorToc\n items={tocItems}\n editorDom={editor.view.dom}\n locale={locale}\n />\n ) : null}\n <EditorContent\n editor={editor}\n className={\n [\n styles.editorScroll,\n showBlockDrag ? styles.withBlockDrag : '',\n toc && !blockDragMobile && tocItems.length > 0 ? styles.withToc : '',\n className ?? '',\n ]\n .filter(Boolean)\n .join(' ')\n }\n />\n {editor && showBlockDrag ? (\n <BlockDragHandle editor={editor} />\n ) : null}\n {/* 统计开着时,计数与键盘悬浮键共用一条贴在右下的 sticky 底栏。\n 统计关着时悬浮键保持自己的粘性定位,不套这层——外层再包一个不粘的盒子会让 sticky 失效。 */}\n {editor && characterCount ? (\n <div className={styles.editorStatus}>\n <CharacterCountStatus editor={editor} locale={locale} />\n {shortcutFabNode && !shortcutFabTarget ? shortcutFabNode : null}\n </div>\n ) : shortcutFabNode && !shortcutFabTarget ? (\n shortcutFabNode\n ) : null}\n {/* 快捷键抽屉入口:宿主给了 shortcutFabContainer 就 portal 进去,定位归宿主。只读态隐藏 */}\n {shortcutFabNode && shortcutFabTarget\n ? createPortal(shortcutFabNode, shortcutFabTarget)\n : null}\n {shortcutPanel && editable ? (\n <ShortcutPanel\n open={shortcutsOpen}\n onClose={() => setShortcutsOpen(false)}\n locale={locale}\n />\n ) : null}\n </div>\n );\n});\n","'use client';\n\nimport { Markdown } from '@tiptap/markdown';\nimport { EditorContent, ReactNodeViewRenderer, useEditor } from '@tiptap/react';\nimport { useEffect, useMemo } from 'react';\nimport { type LocaleCode } from '../i18n';\nimport { usePublishEditorLocale } from '../i18n/store';\nimport { createChart } from '../chart/createChart';\nimport { prepareChartMarkdown } from '../chart/prepareChartMarkdown';\nimport {\n enrichMarkdownCitations,\n type SourceRef,\n} from '../citationUtils';\nimport type { RenderCitation } from '../citationTypes';\nimport { createCitationRef } from '../createCitationRef';\nimport { baseExtensions, pureCodeBlock, pureImage } from '../extensions';\nimport { CodeBlockView } from './CodeBlockView';\nimport '../styles/chart.css';\nimport styles from '../styles/content.module.css';\n\nexport interface MarkdownPreviewProps {\n /** 要预览的 markdown 字符串。 */\n markdown: string;\n /**\n * 脚注来源列表:按 `index` 与正文 `[^n]` 对齐,写入节点的 url/title。\n * 业务数据与 Popover 仍由消费方通过 {@link renderCitation} 完成。\n */\n sources?: SourceRef[];\n /**\n * 脚注圆标 NodeView 插槽。用 Popover 等包住 `defaultDom`;\n * 用 `index` / `attrs` 查消费方自己的数据源。\n */\n renderCitation?: RenderCitation;\n className?: string;\n /** 内置界面语言,默认英文。代码块与图表文案跟着变。 */\n locale?: LocaleCode;\n}\n\n/**\n * 轻量只读 markdown 预览(Tiptap + 官方 @tiptap/markdown,editable:false)。\n * 与 MarkdownWysiwygEditor 共用同一套 base 扩展与正文样式类,渲染同源——\n * 预览所见即编辑/插入后所得。用纯版 CodeBlock/Image(无 React 视图 / 删除快捷键);\n * 图表用只读 NodeView(可渲染,不可编辑弹层)。\n *\n * 这是「客户端只读预览」;若需 SEO / 无 JS 静态渲染,请改用 server 入口的\n * renderReportHtml + ReportContent / ReportContentWithCharts。\n */\nexport function MarkdownPreview({\n markdown,\n sources,\n renderCitation,\n className,\n locale = 'en',\n}: MarkdownPreviewProps) {\n const prepared = useMemo(\n () =>\n prepareChartMarkdown(\n enrichMarkdownCitations(markdown, sources ?? []),\n ),\n [markdown, sources],\n );\n\n // 预览代码块挂只读 NodeView(语言标签 + 复制 + 展开收起 + 行号槽)。\n // 仅客户端入口挂;server 入口(renderReportHtml)保持纯版,静态 HTML 零 React 依赖。\n const previewCodeBlock = useMemo(\n () =>\n pureCodeBlock.extend({\n addNodeView() {\n return ReactNodeViewRenderer(CodeBlockView);\n },\n }),\n [],\n );\n\n const extensions = useMemo(\n () => [\n ...baseExtensions,\n previewCodeBlock,\n pureImage,\n createChart({ editable: false }),\n createCitationRef({ renderCitation }),\n Markdown,\n ],\n [renderCitation],\n );\n\n const editor = useEditor(\n {\n extensions,\n content: prepared,\n contentType: 'markdown',\n editable: false,\n immediatelyRender: false,\n editorProps: {\n attributes: { class: styles.editorContent },\n },\n },\n [extensions],\n );\n\n usePublishEditorLocale(editor, locale);\n\n // 文档尾随空段不在这里处理:它是 StarterKit 的 TrailingNode 补的(文末是代码块/\n // 表格/图表时),事后删节点会被它重建;改成 appendTransaction 里删则会与它互相\n // 触发形成死循环。只读预览由消费方用 CSS 隐藏该空段即可。\n\n useEffect(() => {\n if (!editor) return;\n editor.commands.setContent(prepared, { contentType: 'markdown' });\n }, [editor, prepared]);\n\n return (\n <EditorContent\n editor={editor}\n className={className ?? styles.editorScroll}\n />\n );\n}\n","import type { ToolbarLabels } from './labels';\n\n/** 包内唯一识别的扩展名:命中即本地读文本,其余一律交给宿主回调。 */\nexport const MD_FILE_RE = /\\.(md|markdown)$/i;\nexport const MD_ACCEPT = '.md,.markdown,text/markdown';\n\n/** 主栏导入下拉里的一项。`accept` 写到隐藏 file input。 */\nexport interface ImportMenuItem {\n key: string;\n label: string;\n accept: string;\n}\n\nexport function resolveImportMenuItems({\n labels,\n onImportDocument,\n importAccept,\n importMenuItems,\n}: {\n labels: ToolbarLabels;\n onImportDocument?: unknown;\n importAccept?: string;\n importMenuItems?: ImportMenuItem[];\n}): ImportMenuItem[] {\n if (importMenuItems?.length) return importMenuItems;\n const markdown: ImportMenuItem = {\n key: 'markdown',\n label: labels.importMarkdown,\n accept: MD_ACCEPT,\n };\n if (onImportDocument && importAccept) {\n return [\n markdown,\n {\n key: 'document',\n label: labels.importDocumentOther,\n accept: importAccept,\n },\n ];\n }\n return [markdown];\n}\n","import '@tiptap/markdown';\nimport type { Editor } from '@tiptap/react';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\nimport {\n enrichMarkdownCitations,\n type SourceRef,\n} from './citationUtils';\n\n/**\n * 在光标处插入 markdown 片段(封装 contentType: 'markdown')。\n * 传入 `sources` 时会先 enrich `[^n]` → 带 url 的 citation-ref,再插入。\n *\n * 整段过不了 schema(嵌套 bold、块级图进 listItem 等)时按块重试,\n * 单块仍挂则剥标记当段落。某一处脏输出不至于整次插入失败。\n */\nexport function insertMarkdown(\n editor: Editor,\n markdown: string,\n sources: SourceRef[] = [],\n): void {\n const prepared = prepareChartMarkdown(\n enrichMarkdownCitations(markdown, sources),\n );\n if (!prepared) return;\n if (tryInsertMarkdown(editor, prepared)) return;\n for (const block of splitTopLevelBlocks(prepared)) {\n if (tryInsertMarkdown(editor, block)) continue;\n insertPlainParagraph(editor, stripToPlainText(block));\n }\n}\n\nfunction tryInsertMarkdown(editor: Editor, markdown: string): boolean {\n try {\n return editor\n .chain()\n .focus()\n .insertContent(markdown, { contentType: 'markdown' })\n .run();\n } catch {\n return false;\n }\n}\n\nfunction insertPlainParagraph(editor: Editor, text: string): void {\n if (!text) return;\n editor\n .chain()\n .focus()\n .insertContent({\n type: 'paragraph',\n content: [{ type: 'text', text }],\n })\n .run();\n}\n\n/** 围栏代码整块留下,其余按空行切。 */\nexport function splitTopLevelBlocks(markdown: string): string[] {\n const out: string[] = [];\n for (const [i, part] of markdown.split(/(```[\\s\\S]*?```)/).entries()) {\n if (!part) continue;\n if (i % 2 === 1) {\n out.push(part);\n continue;\n }\n for (const block of part.split(/\\n{2,}/)) {\n if (block.trim()) out.push(block);\n }\n }\n return out;\n}\n\nexport function stripToPlainText(markdown: string): string {\n return markdown\n .replace(/!\\[[^\\]]*\\]\\([^)]+\\)/g, '')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/^#{1,6}\\s+/gm, '')\n .replace(/^[ \\t]*[-*+]\\s+/gm, '')\n .replace(/^[ \\t]*\\d+\\.\\s+/gm, '')\n .replace(/[*_~`]+/g, '')\n .replace(/<[^>]+>/g, '')\n .replace(/\\|/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n","'use client';\n\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport styles from '../styles/colorPalette.module.css';\n\n/**\n * 色板矩阵:10 个色相 × 6 个明度。\n * 第 1 行为纯色(最饱和),下方 5 行为同色相的明→暗变体。\n */\nconst PALETTE: string[][] = [\n ['#8b0000', '#ff0000', '#ff9100', '#ffeb00', '#00e000', '#00e5ff', '#4a86e8', '#0000ff', '#9900ff', '#ff00ff'],\n ['#dba0a0', '#e8b4c0', '#f5d9b8', '#f7f0c0', '#cfe8c4', '#bcd9d4', '#c4d4ec', '#cdd4e8', '#d6c4e0', '#e8c4dc'],\n ['#d28a8a', '#dd8c9c', '#eec49a', '#f0e29c', '#a9d4a0', '#94c0bc', '#a0bce4', '#a4b0d8', '#bca0d0', '#d49cc0'],\n ['#cc4444', '#d4506c', '#e89a5c', '#e8d05c', '#7cc06c', '#5ca09c', '#5c90e0', '#6478c8', '#9460c0', '#c45c98'],\n ['#a02020', '#b01030', '#c87410', '#c8a810', '#449030', '#2c706c', '#3060c0', '#1850a0', '#6428a0', '#a02868'],\n ['#5c1010', '#700818', '#7c4808', '#7c6808', '#205818', '#10403c', '#183c78', '#0c3060', '#3c1460', '#601838'],\n];\n\n// THEME 品牌色\nconst THEME_COLORS = ['#ff6719', '#ff5500'];\n\nexport interface ColorPaletteProps {\n /** 当前已应用的颜色(用于打勾),无则不选中。 */\n value?: string;\n /** 点击某色块。null 表示「None」(清除)。 */\n onPick: (color: string | null) => void;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n}\n\n/** 颜色面板:None 清除行 + 色板网格 + THEME 品牌色。 */\nexport function ColorPalette({ value, onPick, locale = 'en' }: ColorPaletteProps) {\n const t = getLocaleBundle(locale).palette;\n const current = value?.toLowerCase();\n\n return (\n <div className={styles.palette}>\n <button\n type=\"button\"\n className={styles.noneRow}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => onPick(null)}\n >\n <span className={styles.noneIcon} aria-hidden />\n <span className={styles.noneLabel}>{t.none}</span>\n </button>\n\n <div className={styles.grid}>\n {PALETTE.flat().map((c) => (\n <button\n key={c}\n type=\"button\"\n className={`${styles.swatch} ${\n current === c.toLowerCase() ? styles.swatchActive : ''\n }`}\n style={{ background: c }}\n title={c}\n aria-label={c}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => onPick(c)}\n />\n ))}\n </div>\n\n <div className={styles.themeLabel}>{t.theme}</div>\n <div className={styles.themeRow}>\n {THEME_COLORS.map((c) => (\n <button\n key={c}\n type=\"button\"\n className={`${styles.swatch} ${\n current === c.toLowerCase() ? styles.swatchActive : ''\n }`}\n style={{ background: c }}\n title={c}\n aria-label={c}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => onPick(c)}\n />\n ))}\n </div>\n </div>\n );\n}\n","import type { Editor } from '@tiptap/core';\nimport { findImagePos } from './imageAlign';\nimport { normalizeImageCaption } from './imageMarkdown';\n\n/**\n * 按文档位置设置图片描述(图注)——悬停工具条用(鼠标飘过图片时图片未必被\n * 选中,走不了 selection;也不该为此改变选区)。空串 / null 清除描述。\n */\nexport function setImageCaptionAtPos(\n editor: Editor,\n pos: number,\n caption: string | null,\n): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return false;\n const next = normalizeImageCaption(caption);\n const current = normalizeImageCaption(node.attrs.caption);\n if (current === next) return true; // 未变化:不写空事务\n editor.view.dispatch(editor.state.tr.setNodeAttribute(pos, 'caption', next));\n return true;\n}\n\n/**\n * 设置「当前图片」(选中 / 光标紧邻)的描述。attrs 存 `caption`;markdown 里带描述的\n * 图以 `<figure><img …><figcaption>…</figcaption></figure>` 保真(见 imageMarkdown.ts)。\n * 返回是否找到图片。\n */\nexport function setImageCaption(editor: Editor, caption: string | null): boolean {\n const pos = findImagePos(editor);\n if (pos == null) return false;\n return setImageCaptionAtPos(editor, pos, caption);\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/core';\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ComponentType,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n AlignCenterIcon,\n AlignLeftIcon,\n AlignRightIcon,\n CaptionsIcon,\n type IconProps,\n} from '../icons';\nimport type { ToolbarLabels } from '../labels';\nimport { setImageCaptionAtPos } from '../imageCaption';\nimport { setImageAlignAtPos } from '../imageAlign';\nimport { normalizeImageAlign, normalizeImageCaption, type ImageAlign } from '../imageMarkdown';\nimport styles from '../styles/imageAlignTools.module.css';\n\nexport interface ImageAlignToolsProps {\n /** 编辑器实例。只读态不出工具条。 */\n editor: Editor;\n /** 已合并默认值的工具栏文案(工具条与三个按钮的 aria-label 都取自它)。 */\n labels: ToolbarLabels;\n}\n\n/** 悬停图片时的几何与位置(工具条 fixed 定位,直接用视口坐标)。 */\ninterface HoverImage {\n /**\n * 图片节点在文档里的位置:attrs 的读写都走它,**不要长期持有 img 元素引用**——\n * attrs 更新时 prosemirror 会替换裸渲染的 img(旧引用脱离文档、读不到新值)。\n */\n pos: number;\n /** 当前渲染的元素(只用于量几何;每次重测都按 pos 重取)。 */\n img: HTMLImageElement;\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\n/** 工具条高度(含内边距)与它和图片之间的间距;上方放不下就翻到图片下方。 */\nconst TOOLBAR_HEIGHT = 34;\nconst TOOLBAR_GAP = 8;\n/**\n * 指针离开图片后工具条再活一会儿(毫秒):走向工具条的路上有一段「既不在图片上、\n * 也不在工具条上」的缝隙,立刻收起就会点不到(与表格手柄同一套悬停意图)。\n */\nconst HOVER_KEEP_MS = 160;\n\nconst ALIGNS: ImageAlign[] = ['left', 'center', 'right'];\n\nconst ICONS: Record<ImageAlign, ComponentType<IconProps>> = {\n left: AlignLeftIcon,\n center: AlignCenterIcon,\n right: AlignRightIcon,\n};\n\nfunction measureImage(editor: Editor, img: HTMLImageElement): HoverImage | null {\n if (!img.isConnected) return null;\n let pos: number;\n try {\n pos = editor.view.posAtDOM(img, 0);\n } catch {\n return null;\n }\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return null;\n const rect = img.getBoundingClientRect();\n return {\n pos,\n img,\n left: rect.left,\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n };\n}\n\n/**\n * 图片对齐工具条(悬停式,与表格悬停手柄同一套交互):鼠标落在图片上时,图片上方\n * (放不下就下方)浮出一排左 / 中 / 右按钮,点一下即对齐,当前对齐方向高亮。\n *\n * fixed 定位 + portal 到 body:它要贴着图片边缘,而工具栏并不在编辑器 DOM 内。\n * 滚动 / 缩放时重新量悬停的那张图让工具条跟着走。\n *\n * 点击走 {@link setImageAlignAt}(按 DOM 位置改 attrs):鼠标只是飘过图片时图片\n * 并未被选中,不能像快捷键那样走 selection。\n */\nexport function ImageAlignTools({ editor, labels }: ImageAlignToolsProps) {\n const [hover, setHover] = useState<HoverImage | null>(null);\n const hoverRef = useRef<HoverImage | null>(null);\n /** 描述编辑态:null = 未在编辑;string = 输入中的草稿。 */\n const [captionDraft, setCaptionDraft] = useState<string | null>(null);\n const captionDraftRef = useRef<string | null>(null);\n captionDraftRef.current = captionDraft;\n\n const clearHover = useCallback(() => {\n if (!hoverRef.current) return;\n hoverRef.current = null;\n setHover(null);\n setCaptionDraft(null);\n }, []);\n\n /** 重新量一遍悬停的那张图(滚动 / 缩放 / 属性更新后坐标与元素都会变样)。 */\n const remeasureHover = useCallback(() => {\n const current = hoverRef.current;\n if (!current) return;\n // 按 pos 重取元素:attrs 更新会替换裸渲染的 img,旧引用已脱离文档。\n // 开缩放时图片有 NodeView,nodeDOM 返回的是容器([data-resize-container]),\n // 要从容器里再找 img。\n const el = editor.view.nodeDOM(current.pos);\n const img =\n el instanceof HTMLImageElement\n ? el\n : el instanceof HTMLElement\n ? el.querySelector('img')\n : null;\n const next = img ? measureImage(editor, img) : null;\n hoverRef.current = next;\n setHover(next);\n }, [editor]);\n\n const clearTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const cancelPendingClear = useCallback(() => {\n if (clearTimerRef.current === null) return;\n clearTimeout(clearTimerRef.current);\n clearTimerRef.current = null;\n }, []);\n\n useEffect(() => {\n const onMove = (event: MouseEvent) => {\n if (!editor.isEditable) return;\n const target = event.target as HTMLElement | null;\n\n // 指针已经到工具条上:留着,并取消待收起\n if (target?.closest?.('[data-image-align]')) {\n cancelPendingClear();\n return;\n }\n\n const img =\n target && editor.view.dom.contains(target)\n ? (target.closest('img') as HTMLImageElement | null)\n : null;\n\n if (!img) {\n // 正在输入描述:不自动收(用户可能在拖选文字 / 鼠标短暂移开)\n if (captionDraftRef.current !== null) return;\n // 既不在图片也不在工具条上:宽限一段再收(指针可能正走向工具条)\n if (hoverRef.current && clearTimerRef.current === null) {\n clearTimerRef.current = setTimeout(() => {\n clearTimerRef.current = null;\n clearHover();\n }, HOVER_KEEP_MS);\n }\n return;\n }\n\n cancelPendingClear();\n if (hoverRef.current?.img === img) return;\n const next = measureImage(editor, img);\n hoverRef.current = next;\n setHover(next);\n };\n const onBlurWindow = () => {\n cancelPendingClear();\n clearHover();\n };\n document.addEventListener('mousemove', onMove);\n document.addEventListener('mouseleave', onBlurWindow);\n window.addEventListener('scroll', remeasureHover, true);\n window.addEventListener('resize', remeasureHover);\n return () => {\n cancelPendingClear();\n document.removeEventListener('mousemove', onMove);\n document.removeEventListener('mouseleave', onBlurWindow);\n window.removeEventListener('scroll', remeasureHover, true);\n window.removeEventListener('resize', remeasureHover);\n };\n }, [editor, clearHover, cancelPendingClear, remeasureHover]);\n\n useEffect(() => {\n // 文档任何变化(点对齐按钮 / 快捷键 / undo)都可能让图片挪位:重新量一遍,\n // 工具条跟着走(无 hover 时 remeasureHover 会直接返回,不进热路径)。\n const onUpdate = () => remeasureHover();\n editor.on('update', onUpdate);\n return () => {\n editor.off('update', onUpdate);\n };\n }, [editor, remeasureHover]);\n\n if (typeof document === 'undefined') return null;\n if (!hover || !editor.isEditable) return null;\n\n // 位置:默认贴图片左缘、浮在上方;顶上放不下(图片贴着视口上沿)就翻到下方\n const above = hover.top - TOOLBAR_HEIGHT - TOOLBAR_GAP >= 8;\n // active 从文档状态读(不读 DOM 属性:attrs 更新会换元素,DOM 引用不可靠)\n const nodeAttrs = editor.state.doc.nodeAt(hover.pos)?.attrs;\n const active = normalizeImageAlign(nodeAttrs?.align);\n const caption = normalizeImageCaption(nodeAttrs?.caption);\n const labelOf: Record<ImageAlign, string> = {\n left: labels.imageAlignLeft,\n center: labels.imageAlignCenter,\n right: labels.imageAlignRight,\n };\n\n const commitCaption = () => {\n if (captionDraft === null) return;\n setImageCaptionAtPos(editor, hover.pos, captionDraft);\n setCaptionDraft(null);\n // 描述显示在图片下方 → 几何变了,重新量一遍让工具条归位\n remeasureHover();\n };\n\n return createPortal(\n <div\n data-image-align=\"\"\n className={styles.bar}\n role=\"toolbar\"\n aria-label={labels.imageAlign}\n style={{\n left: hover.left,\n top: above\n ? hover.top - TOOLBAR_HEIGHT - TOOLBAR_GAP\n : hover.bottom + TOOLBAR_GAP,\n }}\n // 保住编辑器选区:点工具条不该先把光标丢掉;输入框例外(要能聚焦)\n onMouseDown={(e) => {\n if ((e.target as HTMLElement | null)?.closest?.('input')) return;\n e.preventDefault();\n }}\n >\n {ALIGNS.map((align) => {\n const Icon = ICONS[align];\n return (\n <button\n key={align}\n type=\"button\"\n data-image-align-action={align}\n className={`${styles.btn} ${active === align ? styles.btnActive : ''}`}\n title={labelOf[align]}\n aria-label={labelOf[align]}\n aria-pressed={active === align}\n onClick={() => {\n setImageAlignAtPos(editor, hover.pos, align);\n // 对齐后图片会挪位:立即重测一遍让工具条跟过去(顺带刷新 active 态)\n remeasureHover();\n }}\n >\n <Icon size={14} />\n </button>\n );\n })}\n\n <span className={styles.divider} aria-hidden />\n\n <button\n type=\"button\"\n data-image-align-action=\"caption\"\n className={`${styles.btn} ${caption ? styles.btnActive : ''}`}\n title={labels.imageCaption}\n aria-label={labels.imageCaption}\n aria-pressed={!!caption}\n onClick={() => {\n // 已打开就交给输入框的 blur 提交;这里只管打开\n if (captionDraftRef.current !== null) return;\n setCaptionDraft(caption ?? '');\n }}\n >\n <CaptionsIcon size={14} />\n </button>\n\n {captionDraft !== null ? (\n <div className={styles.captionRow}>\n <input\n className={styles.captionInput}\n value={captionDraft}\n placeholder={labels.imageCaptionPlaceholder}\n aria-label={labels.imageCaptionPlaceholder}\n autoFocus\n onChange={(e) => setCaptionDraft(e.target.value)}\n onKeyDown={(e) => {\n // 按键别冒进编辑器:Enter 提交、Esc 取消\n e.stopPropagation();\n if (e.key === 'Enter') commitCaption();\n else if (e.key === 'Escape') setCaptionDraft(null);\n }}\n onBlur={commitCaption}\n />\n </div>\n ) : null}\n </div>,\n document.body,\n );\n}\n","/**\n * 链接编辑的适配层。\n *\n * 逻辑按官方 UI Components 的 `useLinkPopover` 搬(MIT),外壳自绘。相对原来的\n * `window.prompt` 增量也都在这里:\n *\n * - 改地址时把范围扩到**整条链接**(`extendMarkRange`),而不是只作用于光标那一段;\n * - 选区为空时把地址本身当文本插进去(空选区上没有文字可以套 mark);\n * - 移除时带上 `preventAutolink` 元数据(与官方同款),免得自动链接插件把它加回来;\n * - 「在新窗口打开」前过一遍协议白名单,`javascript:` 之类直接拒掉;\n * - 裸域名补协议。官方面板不补,但 Tiptap 的 `setLink` 是把值**原样**存下来的,而它自己的\n * 自动链接(linkify)会按 `defaultProtocol` 补——两边不一致的结果是:手打 `example.com`\n * 存成相对链接,markdown 导出的就是 `[x](example.com)`。这里按扩展配的 `defaultProtocol`\n * 补齐,让「打字」和「粘贴」落到同一个地址上。\n *\n * 放在这里而不是组件里,是为了能脱离 DOM 直接跑单测(同 replaceRange / insertMarkdown)。\n */\n\nimport type { Editor } from '@tiptap/react';\n\nimport { isComposingKeyEvent } from './findReplace';\n\n/** 「在新窗口打开」允许的协议(对齐官方 `sanitizeUrl` / Tiptap Link 的默认允准集)。 */\nconst OPENABLE_PROTOCOLS = new Set([\n 'http:',\n 'https:',\n 'ftp:',\n 'ftps:',\n 'mailto:',\n 'tel:',\n 'callto:',\n 'sms:',\n 'cid:',\n 'xmpp:',\n]);\n\n/** 已经有协议了(`https:` / `mailto:` …),或者是个站内相对地址(`/x`、`#x`、`?x`)。 */\nconst HAS_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;\nconst SITE_RELATIVE_RE = /^[/#?]/;\n/**\n * 主机:端口 形态(`localhost:3000`、`127.0.0.1:8080`、`[::1]:3000`)。它们**看着像协议**\n * (`localhost:` 也满足 `HAS_SCHEME_RE`),但显然是主机,要按主机补协议——否则会被当成\n * 未知协议,Tiptap 的 `isAllowedUri` 也会直接拒掉。\n */\nconst HOST_PORT_RE = /^(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|\\[[0-9a-f:]+\\]):\\d+($|[/#?])/i;\n/** 第一段里带点的裸域名(`example.com/path`)。 */\nconst BARE_HOST_RE = /^[^/?#]*\\.[^/?#]*/;\n\n/** 链接可编辑吗:只读态、或当前选区不允许套 link mark(例如整块选中)时为 false。 */\nexport function canSetLink(editor: Editor | null | undefined): boolean {\n if (!editor || editor.isEditable === false) return false;\n try {\n return editor.can().setMark('link');\n } catch {\n return false;\n }\n}\n\n/** 光标 / 选区是否落在链接里。 */\nexport function isLinkActive(editor: Editor | null | undefined): boolean {\n if (!editor) return false;\n return editor.isActive('link');\n}\n\n/** 读当前链接的 href(不在链接里时是空串)。 */\nexport function readLinkHref(editor: Editor | null | undefined): string {\n if (!editor) return '';\n const { href } = editor.getAttributes('link');\n return typeof href === 'string' ? href : '';\n}\n\n/** 扩展上配的默认协议(`StarterKit` 里是 `http`),读不到就按 http 算。 */\nfunction defaultProtocol(editor: Editor): string {\n const link = editor.extensionManager?.extensions.find(\n (extension) => extension.name === 'link',\n );\n const configured = (link?.options as { defaultProtocol?: string } | undefined)\n ?.defaultProtocol;\n return configured || 'http';\n}\n\n/**\n * 把用户输入整理成要存进 href 的值。\n *\n * 原样保留:空、站内相对地址(`/docs`、`#sec`、`?q=1`)、已带协议的(`https:`、`mailto:`)。\n * 补协议:裸域名(`example.com`、`example.com/a`)与主机:端口(`localhost:3000`)——补的是\n * 扩展配置的 `defaultProtocol`,与 Tiptap 自动链接同一套,避免手打和粘贴落到不同地址。\n */\nexport function normalizeLinkHref(editor: Editor, raw: string): string {\n const value = raw.trim();\n if (!value) return '';\n if (SITE_RELATIVE_RE.test(value)) return value;\n if (HOST_PORT_RE.test(value)) return `${defaultProtocol(editor)}://${value}`;\n if (HAS_SCHEME_RE.test(value)) return value;\n if (!BARE_HOST_RE.test(value)) return value;\n return `${defaultProtocol(editor)}://${value}`;\n}\n\n/**\n * 应用链接:把地址套到**整条**链接(或选区)上;选区为空且原本不在链接里时,把地址当文本插进去。\n * 返回扩展是否接受了这个地址——`setLink` 会走 Tiptap 的 `isAllowedUri`。\n *\n * 两处与官方那版不同,都是单测逼出来的:\n * 1. 先 `can().setLink()` 干跑一遍。官方把 `insertContent` 挂在失败的 `setLink` 后面直接\n * `run()`,结果是地址被拒时正文仍被改(链接文字被换成那个地址)。\n * 2. 「当文本插入」的判据是「选区为空**且光标不在已有链接里**」。官方只看选区是否为空,\n * 于是「光标在链接里、改一个地址」会把整条链接的文字替换成地址——而这正是这个浮层的主用途。\n */\nexport function applyLink(editor: Editor | null | undefined, raw: string): boolean {\n if (!editor) return false;\n const href = normalizeLinkHref(editor, raw);\n if (!href) return false;\n if (!editor.can().setLink({ href })) return false;\n\n const isEmpty = editor.state.selection.empty;\n const insideLink = isLinkActive(editor);\n const chain = editor\n .chain()\n .focus()\n .extendMarkRange('link')\n .setLink({ href });\n\n if (isEmpty && !insideLink) chain.insertContent({ type: 'text', text: href });\n\n return chain.run();\n}\n\n/** 移除链接:范围同样扩到整条,并挡住自动链接立刻加回来。 */\nexport function removeLink(editor: Editor | null | undefined): boolean {\n if (!editor) return false;\n return editor\n .chain()\n .focus()\n .extendMarkRange('link')\n .unsetLink()\n .setMeta('preventAutolink', true)\n .run();\n}\n\n/**\n * 把待打开的地址解析成绝对地址;解析不了、或协议不在白名单里时返回 `'#'` 表示「别打开」。\n *\n * 与官方 `sanitizeUrl(inputUrl, baseUrl)` 同语义:先按 base 解析成绝对地址(相对地址因此也\n * 能用),再查协议白名单。调用方拿到 `'#'` 就不开新窗口。\n */\nexport function sanitizeLinkUrl(input: string, baseUrl: string): string {\n try {\n const url = new URL(input, baseUrl);\n if (OPENABLE_PROTOCOLS.has(url.protocol)) return url.href;\n } catch {\n // 解析失败 = 非法地址\n }\n return '#';\n}\n\n/**\n * 打开地址(新窗口 + `noopener,noreferrer`)。空输入或非法地址都不开窗——调用方在此之前\n * 已经用 `sanitizeLinkUrl` 判过一次,这里再判一次是为了让这个入口单独用也安全。\n */\nexport function openLinkUrl(input: string, baseUrl: string): boolean {\n if (!input.trim()) return false;\n const safe = sanitizeLinkUrl(input, baseUrl);\n if (safe === '#') return false;\n window.open(safe, '_blank', 'noopener,noreferrer');\n return true;\n}\n\n/** 输入框的回车是否该当作「应用」(输入法组合中的回车是提交候选词,放行)。 */\nexport function isApplyKey(event: {\n key: string;\n nativeEvent: Pick<KeyboardEvent, 'isComposing' | 'keyCode'>;\n}): boolean {\n if (event.key !== 'Enter') return false;\n return !isComposingKeyEvent(event.nativeEvent);\n}\n","'use client';\n\nimport * as Popover from '@radix-ui/react-popover';\nimport type { Editor } from '@tiptap/react';\nimport {\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from 'react';\nimport { CheckIcon, ExternalLinkIcon, TrashIcon } from '../icons';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport type { LinkPopoverLabels } from '../labels';\nimport {\n applyLink,\n canSetLink,\n isApplyKey,\n normalizeLinkHref,\n openLinkUrl,\n readLinkHref,\n removeLink,\n} from '../linkEditing';\nimport styles from '../styles/linkPopover.module.css';\n\nexport interface LinkPopoverProps {\n /** 编辑器实例。 */\n editor: Editor;\n /**\n * 触发器:传宿主自己的按钮元素。Radix 的 `asChild` 要接管它的点击 / ref / aria——\n * 传函数组件时得先 `forwardRef`(本库的 `<EditorToolbar>` 就是这么接的)。\n */\n trigger: ReactNode;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n /** 挂到浮层根节点上的 class / 内联样式(定位与层级归宿主,Radix 只管浮层方向)。 */\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * 链接编辑浮层:输入地址 + 应用 / 在新窗口打开 / 移除。\n *\n * 逻辑全在 `linkEditing.ts`(从官方 UI Components 的 `useLinkPopover` 搬的),这里只负责输入、\n * 按钮与状态。取代表驱动器的 `window.prompt`:能**编辑已有链接**(打开即预填当前地址,\n * 而旧实现是「在链接里点一下就删除」)、能打开、能移除,且 `window.prompt` 在 Electron /\n * 部分 WebView 里根本不可用。\n *\n * 地址被编辑器拒掉时(Tiptap 的 `isAllowedUri`)浮层不关,输入框转成危险色并带上 title,\n * 让用户改——静默什么都不发生是最难查的。\n */\nexport function LinkPopover({\n editor,\n trigger,\n locale = 'en',\n className,\n style,\n}: LinkPopoverProps) {\n const [open, setOpen] = useState(false);\n\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger asChild>{trigger}</Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n sideOffset={4}\n align=\"start\"\n // 关闭后不要把焦点夺回触发器:应用后该留在编辑器里(applyLink 里已经 focus 过)\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <LinkPanel\n editor={editor}\n locale={locale}\n className={className}\n style={style}\n onClose={() => setOpen(false)}\n />\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\nfunction LinkPanel({\n editor,\n locale,\n className,\n style,\n onClose,\n}: {\n editor: Editor;\n locale: LocaleCode;\n className?: string;\n style?: CSSProperties;\n onClose: () => void;\n}) {\n const t: LinkPopoverLabels = getLocaleBundle(locale).link;\n // Radix 关闭时会卸载内容,所以这里的初值就是「每次打开读一次当前地址」——光标在链接里\n // 打开时预填它的 href,便于改地址而不是先删再加。\n const [url, setUrl] = useState(() => readLinkHref(editor));\n const [invalid, setInvalid] = useState(false);\n\n const canEdit = canSetLink(editor);\n const hasUrl = url.trim().length > 0;\n const keepFocus = (e: ReactMouseEvent) => e.preventDefault();\n\n const apply = () => {\n if (!applyLink(editor, url)) {\n setInvalid(true);\n return;\n }\n setInvalid(false);\n onClose();\n };\n\n const openInNewWindow = () => {\n // 先补协议再交给白名单:裸域名不该按「相对本页」打开\n openLinkUrl(normalizeLinkHref(editor, url), window.location.href);\n };\n\n const remove = () => {\n removeLink(editor);\n onClose();\n };\n\n const onInputKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n // 输入法组合中的回车是「提交候选词」,放行(isApplyKey 里判)\n if (!isApplyKey(e)) return;\n e.preventDefault();\n apply();\n };\n\n return (\n <div\n className={className ? `${styles.panel} ${className}` : styles.panel}\n style={style}\n data-link-popover=\"\"\n role=\"dialog\"\n aria-label={t.field}\n >\n <span className={`${styles.field}${invalid ? ` ${styles.fieldInvalid}` : ''}`}>\n <input\n className={styles.input}\n data-link-field=\"\"\n data-link-invalid={invalid ? '' : undefined}\n type=\"url\"\n value={url}\n onChange={(e) => {\n setUrl(e.target.value);\n setInvalid(false);\n }}\n onKeyDown={onInputKeyDown}\n placeholder={t.field}\n aria-label={t.field}\n aria-invalid={invalid || undefined}\n title={invalid ? t.invalid : undefined}\n autoFocus\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n />\n </span>\n\n <button\n type=\"button\"\n className={`${styles.btn} ${styles.apply}`}\n data-link-action=\"apply\"\n title={t.apply}\n aria-label={t.apply}\n disabled={!canEdit || !hasUrl}\n onMouseDown={keepFocus}\n onClick={apply}\n >\n <CheckIcon size={16} />\n </button>\n\n <span className={styles.divider} aria-hidden=\"true\" />\n\n <button\n type=\"button\"\n className={styles.btn}\n data-link-action=\"open\"\n title={t.open}\n aria-label={t.open}\n disabled={!hasUrl}\n onMouseDown={keepFocus}\n onClick={openInNewWindow}\n >\n <ExternalLinkIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-link-action=\"remove\"\n title={t.remove}\n aria-label={t.remove}\n disabled={!canEdit}\n onMouseDown={keepFocus}\n onClick={remove}\n >\n <TrashIcon size={16} />\n </button>\n </div>\n );\n}\n","import type { Editor } from '@tiptap/core';\nimport type { Node as PMNode } from '@tiptap/pm/model';\nimport type { EditorState } from '@tiptap/pm/state';\nimport {\n cellAround,\n CellSelection,\n isInTable,\n rowIsHeader,\n selectedRect,\n TableMap,\n} from '@tiptap/pm/tables';\n\nexport interface TableSelectionInfo {\n rowCount: number;\n colCount: number;\n /** 选区跨越多行或多列时为 true(用于带 N 的文案)。 */\n isMultiCell: boolean;\n includesHeaderRow: boolean;\n coversAllRows: boolean;\n coversAllCols: boolean;\n}\n\n/** 当前若不在表格内返回 null。 */\nexport function getTableSelectionInfo(\n state: EditorState,\n): TableSelectionInfo | null {\n if (!isInTable(state)) return null;\n\n const rect = selectedRect(state);\n const rowCount = Math.max(1, rect.bottom - rect.top);\n const colCount = Math.max(1, rect.right - rect.left);\n\n let includesHeaderRow = false;\n for (let row = rect.top; row < rect.bottom; row++) {\n if (rowIsHeader(rect.map, rect.table, row)) {\n includesHeaderRow = true;\n break;\n }\n }\n\n return {\n rowCount,\n colCount,\n isMultiCell: rowCount > 1 || colCount > 1,\n includesHeaderRow,\n coversAllRows: rect.top === 0 && rect.bottom === rect.map.height,\n coversAllCols: rect.left === 0 && rect.right === rect.map.width,\n };\n}\n\n/** 文档坐标是否落在 CellSelection 覆盖的某个单元格内(含单元格节点本身)。 */\nexport function isPosInCellSelection(\n selection: CellSelection,\n pos: number,\n): boolean {\n let found = false;\n selection.forEachCell((node, cellPos) => {\n if (pos >= cellPos && pos <= cellPos + node.nodeSize) {\n found = true;\n }\n });\n return found;\n}\n\n\nexport function addRowsBefore(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addRowBefore()) return false;\n }\n return true;\n}\n\nexport function addRowsAfter(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addRowAfter()) return false;\n }\n return true;\n}\n\nexport function addColumnsBefore(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addColumnBefore()) return false;\n }\n return true;\n}\n\nexport function addColumnsAfter(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addColumnAfter()) return false;\n }\n return true;\n}\n\n/** 删行:全表行被选中时改为删整表;含表头时调用方应先禁用。 */\nexport function deleteSelectedRows(editor: Editor): boolean {\n const info = getTableSelectionInfo(editor.state);\n if (!info) return false;\n if (info.coversAllRows) return editor.commands.deleteTable();\n return editor.commands.deleteRow();\n}\n\n/** 删列:选区覆盖全部列且同时覆盖全部行时删整表;仅覆盖全部列时失败(由 UI 禁用)。 */\nexport function deleteSelectedColumns(editor: Editor): boolean {\n const info = getTableSelectionInfo(editor.state);\n if (!info) return false;\n if (info.coversAllCols) {\n if (info.coversAllRows) return editor.commands.deleteTable();\n return false;\n }\n return editor.commands.deleteColumn();\n}\n\n/** 结构属性:改它们等于改变表格形状,合并 / 拆分有专门的命令,重置样式时不碰。 */\nconst STRUCTURAL_CELL_ATTRS = new Set(['colspan', 'rowspan']);\n\n/**\n * 选区覆盖到的单元格:CellSelection 的全部单元格;若只是个文本光标,就取它所在的那一格。\n * (菜单里永远是 CellSelection;带光标这条是为了让命令单独用也说得通。)\n */\nfunction selectedCellEntries(\n editor: Editor,\n): Array<{ pos: number; node: PMNode }> {\n const { selection } = editor.state;\n const entries: Array<{ pos: number; node: PMNode }> = [];\n\n if (selection instanceof CellSelection) {\n selection.forEachCell((node, pos) => {\n entries.push({ pos, node });\n });\n return entries;\n }\n\n const $cell = cellAround(selection.$from);\n if (!$cell) return entries;\n const pos = $cell.before($cell.depth);\n const node = editor.state.doc.nodeAt(pos);\n if (node) entries.push({ pos, node });\n return entries;\n}\n\n/**\n * 清空选中单元格的**内容**,保留单元格本身与它的属性(表头行因此不会被降级)。\n *\n * 单元格必须至少留一个块,所以内容替换成一个空段落。已经空的单元格跳过——不改动、\n * 也不产生多余的撤销步。返回是否真的改了文档。\n */\nexport function clearSelectedCellContents(editor: Editor): boolean {\n const entries = selectedCellEntries(editor);\n if (!entries.length) return false;\n const paragraph = editor.state.schema.nodes.paragraph;\n if (!paragraph) return false;\n\n const tr = editor.state.tr;\n // 从后往前改:替换内容会改变 nodeSize,先算好后面的位置才不会位移串味\n for (const { pos, node } of [...entries].sort((a, b) => b.pos - a.pos)) {\n const isEmpty =\n node.childCount === 1 &&\n !!node.firstChild?.isTextblock &&\n node.firstChild.content.size === 0;\n if (isEmpty) continue;\n tr.replaceWith(pos + 1, pos + node.nodeSize - 1, paragraph.createAndFill()!);\n }\n\n if (!tr.docChanged) return false;\n editor.view.dispatch(tr.scrollIntoView());\n return true;\n}\n\n/**\n * 把选中单元格的**样式属性**复位(列宽、对齐等,按各属性的 schema 默认值)。\n *\n * 不动 `colspan` / `rowspan`:那是结构,复位它们会让表格形状不合法(缺格)。\n * (扩展有 `mergeCells` / `splitCell`,但本库不暴露入口——GFM 没有 colspan,合并导出成\n * `| 1<br>2 | |` 且回不去,见 README。)\n */\nexport function resetSelectedCellStyles(editor: Editor): boolean {\n const entries = selectedCellEntries(editor);\n if (!entries.length) return false;\n\n const tr = editor.state.tr;\n for (const { pos, node } of entries) {\n const attrs = (node.type.spec.attrs ?? {}) as Record<\n string,\n { default?: unknown }\n >;\n const next: Record<string, unknown> = { ...node.attrs };\n let changed = false;\n for (const [key, spec] of Object.entries(attrs)) {\n if (STRUCTURAL_CELL_ATTRS.has(key)) continue;\n const fallback = spec?.default ?? null;\n if (next[key] !== fallback) {\n next[key] = fallback;\n changed = true;\n }\n }\n if (changed) tr.setNodeMarkup(pos, undefined, next);\n }\n\n if (!tr.docChanged) return false;\n editor.view.dispatch(tr);\n return true;\n}\n\n/** 找当前要操作的那张表:优先用光标 / 选区所在的那张(同页多表时才不会改错),否则文档里的第一张。 */\nfunction findTable(editor: Editor): { pos: number; node: PMNode } | null {\n const $from = editor.state.selection.$from;\n for (let d = $from.depth; d > 0; d -= 1) {\n if ($from.node(d).type.name === 'table') {\n return { pos: $from.before(d), node: $from.node(d) };\n }\n }\n\n let found: { pos: number; node: PMNode } | null = null;\n editor.state.doc.descendants((node, pos) => {\n if (found !== null) return false;\n if (node.type.name === 'table') {\n found = { pos, node };\n return false;\n }\n return true;\n });\n return found;\n}\n\n/** 把第 index 行 / 列整体选成 CellSelection(末尾追加这类操作要先「瞄准」)。 */\nfunction selectLineAt(\n editor: Editor,\n table: { pos: number; node: PMNode },\n kind: 'row' | 'col',\n index: number,\n): void {\n const map = TableMap.get(table.node);\n const start = table.pos + 1;\n const anchorCell = start + map.positionAt(kind === 'row' ? index : 0, kind === 'col' ? index : 0, table.node);\n const headCell =\n start +\n map.positionAt(\n kind === 'row' ? index : map.height - 1,\n kind === 'col' ? index : map.width - 1,\n table.node,\n );\n editor.view.dispatch(\n editor.state.tr.setSelection(\n new CellSelection(\n editor.state.doc.resolve(anchorCell),\n editor.state.doc.resolve(headCell),\n ),\n ),\n );\n}\n\n/**\n * 在表格末尾追加一行(表底那个 `+`)。\n * 先把最后一行选成 CellSelection——扩展的 `addRowAfter` 是按选区办事的。\n */\nexport function appendRowToTable(editor: Editor): boolean {\n const table = findTable(editor);\n if (!table) return false;\n selectLineAt(editor, table, 'row', TableMap.get(table.node).height - 1);\n return addRowsAfter(editor, 1);\n}\n\n/**\n * 在表格末尾追加一列(表格右侧那个 `+`)。\n * 同理先把最后一列选成 CellSelection。\n */\nexport function appendColumnToTable(editor: Editor): boolean {\n const table = findTable(editor);\n if (!table) return false;\n selectLineAt(editor, table, 'col', TableMap.get(table.node).width - 1);\n return addColumnsAfter(editor, 1);\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/core';\nimport { CellSelection, cellAround, TableMap } from '@tiptap/pm/tables';\nimport {\n Fragment,\n useCallback,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n ColumnAfterIcon,\n ColumnBeforeIcon,\n ColumnDeleteIcon,\n EraserIcon,\n RotateCcwIcon,\n RowAfterIcon,\n RowBeforeIcon,\n RowDeleteIcon,\n TableDeleteIcon,\n} from '../icons';\nimport type { ToolbarLabels } from '../labels';\nimport {\n addColumnsAfter,\n addColumnsBefore,\n appendColumnToTable,\n appendRowToTable,\n addRowsAfter,\n addRowsBefore,\n clearSelectedCellContents,\n deleteSelectedColumns,\n deleteSelectedRows,\n resetSelectedCellStyles,\n getTableSelectionInfo,\n isPosInCellSelection,\n} from '../tableSelection';\nimport styles from '../styles/tableHandles.module.css';\n\nexport interface TableHandlesProps {\n /** 编辑器实例。只读态不出手柄。 */\n editor: Editor;\n /** 已合并默认值的工具栏文案(行列菜单项与两个手柄的 aria-label 都取自它)。 */\n labels: ToolbarLabels;\n}\n\n/** 悬停到某个单元格时量出来的几何:手柄贴边 + 建 CellSelection 用。 */\ninterface HoverGeom {\n /** 当前悬停的单元格元素(用来跳过同一格的重复测量)。 */\n cell: HTMLElement;\n rowTop: number;\n rowHeight: number;\n colLeft: number;\n colWidth: number;\n tableLeft: number;\n tableTop: number;\n /** 表格右缘 / 下缘:末尾追加行列的两个 `+` 手柄贴在它们外侧。 */\n tableRight: number;\n tableBottom: number;\n /** 悬停的这格是不是表格的最后一行 / 最后一列——决定要不要出对应的 `+`。 */\n onLastRow: boolean;\n onLastCol: boolean;\n}\n\n/**\n * 悬停的这格落在表格的最后一行 / 最后一列吗(按 TableMap 算,colspan 也算得对)。\n * 末尾追加的 `+` 只在贴着那两行/列时才出现,否则鼠标停在表格中间也会冒出两个加号。\n */\nfunction edgeFlags(\n editor: Editor,\n cell: HTMLElement,\n): { onLastRow: boolean; onLastCol: boolean } {\n try {\n const $cell = cellAround(editor.state.doc.resolve(editor.view.posAtDOM(cell, 0)));\n if (!$cell) return { onLastRow: false, onLastCol: false };\n // 官方(prosemirror-tables)自己的换算写法:findCell($pos.pos - $pos.start(-1))。\n // 注意别用 $pos.before($pos.depth)——cellAround 给的是「行层」的 ResolvedPos,\n // before(depth) 拿到的是行首的位置,不是这一格的。\n const map = TableMap.get($cell.node(-1));\n const rect = map.findCell($cell.pos - $cell.start(-1));\n return {\n onLastRow: rect.bottom === map.height,\n onLastCol: rect.right === map.width,\n };\n } catch {\n return { onLastRow: false, onLastCol: false };\n }\n}\n\n/** 菜单挂在哪个手柄(multi = 右键多格选区)上。 */\ninterface HandleMenu {\n kind: 'row' | 'col' | 'multi';\n x: number;\n y: number;\n}\n\n/**\n * 行手柄拉满行高,列手柄拉满列宽。GAP 是胶囊外侧到表格边缘的距离,\n * 减去厚度后仍留一段空隙,靠伪元素补桥才能把鼠标移过去。\n */\nconst COL_HANDLE_GAP = 18;\nconst COL_HANDLE_HEIGHT = 12;\nconst ROW_HANDLE_GAP = 20;\nconst ROW_HANDLE_WIDTH = 12;\n\n/** 末尾追加行列的 `+` 手柄贴在表格右缘 / 下缘外侧的间距。 */\nconst EXTEND_GAP = 6;\n/** `+` 手柄的粗细,与行 / 列手柄一致。长度仍跟着整表走。 */\nconst EXTEND_THICKNESS = 12;\n/**\n * 指针离开单元格后手柄再活一会儿(毫秒):走向手柄的路上有一段「既非单元格也非手柄」的空隙,\n * 立刻收起就会点不到。见下面悬停追踪那段注释。\n */\nconst HOVER_KEEP_MS = 160;\n\n/** 量单元格与它所在行 / 表格的视口矩形(手柄是 fixed 定位,直接用视口坐标)。 */\nfunction measureCell(editor: Editor, cell: HTMLElement): HoverGeom | null {\n const table = cell.closest('table');\n const row = cell.closest('tr');\n if (!table || !row) return null;\n // 校验这个 DOM 还对应文档里的位置(视图可能刚重建过)\n try {\n editor.view.posAtDOM(cell, 0);\n } catch {\n return null;\n }\n const cellRect = cell.getBoundingClientRect();\n const rowRect = row.getBoundingClientRect();\n const tableRect = table.getBoundingClientRect();\n const edges = edgeFlags(editor, cell);\n return {\n cell,\n ...edges,\n rowTop: rowRect.top,\n rowHeight: rowRect.height,\n colLeft: cellRect.left,\n colWidth: cellRect.width,\n tableLeft: tableRect.left,\n tableTop: tableRect.top,\n tableRight: tableRect.right,\n tableBottom: tableRect.bottom,\n };\n}\n\n/** 把整行 / 整列选成 CellSelection:菜单里的命令都按选区办事,选中即「瞄准」。 */\nfunction selectRowOrColumn(\n editor: Editor,\n cell: HTMLElement,\n kind: 'row' | 'col',\n): boolean {\n let pos: number;\n try {\n pos = editor.view.posAtDOM(cell, 0);\n } catch {\n return false;\n }\n const $cell = cellAround(editor.state.doc.resolve(pos));\n if (!$cell) return false;\n const selection =\n kind === 'row'\n ? CellSelection.rowSelection($cell)\n : CellSelection.colSelection($cell);\n editor.view.dispatch(editor.state.tr.setSelection(selection));\n return true;\n}\n\ninterface MenuItemSpec {\n key: string;\n label: string;\n icon: ReactNode;\n /** 分组:组变了就插一条分隔线(列操作 / 行操作 / 单元格操作 / 删除表格)。 */\n group: 'col' | 'row' | 'cells';\n disabled?: boolean;\n run: () => void;\n}\n\n/** 按手柄种类组装菜单项:行手柄只管行、列手柄只管列,右键多格选区两样都给。 */\nfunction menuItemsFor(\n editor: Editor,\n labels: ToolbarLabels,\n kind: HandleMenu['kind'],\n): MenuItemSpec[] {\n const info = getTableSelectionInfo(editor.state);\n const rowN = info?.rowCount ?? 1;\n const colN = info?.colCount ?? 1;\n // 「几行几列」的文案**只**给多格选区用:点行手柄时选区是「整行 × 全部列」,\n // 按 isMultiCell 判会得到「插入 1 行」这种拗口文案(而且列数会被算进去)。\n const multi = kind === 'multi';\n const items: MenuItemSpec[] = [];\n\n if (kind === 'col' || kind === 'multi') {\n items.push(\n {\n key: 'col-before',\n group: 'col',\n label: multi ? labels.tableAddColumnBeforeN(colN) : labels.tableAddColumnBefore,\n icon: <ColumnBeforeIcon />,\n run: () => addColumnsBefore(editor, colN),\n },\n {\n key: 'col-after',\n group: 'col',\n label: multi ? labels.tableAddColumnAfterN(colN) : labels.tableAddColumnAfter,\n icon: <ColumnAfterIcon />,\n run: () => addColumnsAfter(editor, colN),\n },\n {\n key: 'col-delete',\n group: 'col',\n label: multi ? labels.tableDeleteColumnN(colN) : labels.tableDeleteColumn,\n icon: <ColumnDeleteIcon />,\n // 只选了全部列、却没选全部行:删列会把表格删没,禁掉\n disabled: !!info?.coversAllCols && !info?.coversAllRows,\n run: () => deleteSelectedColumns(editor),\n },\n );\n }\n\n if (kind === 'row' || kind === 'multi') {\n items.push(\n {\n key: 'row-before',\n group: 'row',\n label: multi ? labels.tableAddRowBeforeN(rowN) : labels.tableAddRowBefore,\n icon: <RowBeforeIcon />,\n // 上方是表头行时插行没意义\n disabled: !!info?.includesHeaderRow,\n run: () => addRowsBefore(editor, rowN),\n },\n {\n key: 'row-after',\n group: 'row',\n label: multi ? labels.tableAddRowAfterN(rowN) : labels.tableAddRowAfter,\n icon: <RowAfterIcon />,\n run: () => addRowsAfter(editor, rowN),\n },\n {\n key: 'row-delete',\n group: 'row',\n label: multi ? labels.tableDeleteRowN(rowN) : labels.tableDeleteRow,\n icon: <RowDeleteIcon />,\n disabled: !!info?.includesHeaderRow,\n run: () => deleteSelectedRows(editor),\n },\n );\n }\n\n // 单元格级操作:清空内容、重置样式。两者都不改变表格形状,所以行 / 列 / 多格选区都给。\n items.push(\n {\n key: 'cells-clear',\n group: 'cells',\n label: labels.tableClearContent,\n icon: <EraserIcon />,\n run: () => clearSelectedCellContents(editor),\n },\n {\n key: 'cells-reset',\n group: 'cells',\n label: labels.tableResetCellStyles,\n icon: <RotateCcwIcon />,\n run: () => resetSelectedCellStyles(editor),\n },\n );\n\n return items;\n}\n\n/**\n * 表格的悬停手柄(Notion 式):鼠标落在单元格上时,行左缘出一条竖胶囊(⋮)、表格上方出一条\n * 横胶囊(⋯),点开就是该行 / 该列的操作菜单。\n *\n * 这是原来「表内右键出菜单」的替代——右键只保留**多格选区**的批量增删(手柄只覆盖单行单列,\n * 那个场景手柄表达不了)。命令全部复用 `tableSelection.ts`,与工具栏的「插入表格」共用一套。\n *\n * 手柄是 fixed 定位、渲染在 body 的 portal 里:它要贴着单元格边缘,而工具栏并不在编辑器 DOM\n * 内。滚动 / 缩放时重新量一遍悬停的那颗单元格,让手柄跟着走。\n */\nexport function TableHandles({ editor, labels }: TableHandlesProps) {\n const [hover, setHover] = useState<HoverGeom | null>(null);\n const [menu, setMenu] = useState<HandleMenu | null>(null);\n\n const hoverRef = useRef<HoverGeom | null>(null);\n const menuRef = useRef<HandleMenu | null>(null);\n menuRef.current = menu;\n\n const clearHover = useCallback(() => {\n if (!hoverRef.current) return;\n hoverRef.current = null;\n setHover(null);\n }, []);\n\n const closeMenu = useCallback(() => setMenu(null), []);\n\n /** 重新量一遍悬停的那颗单元格(滚动 / 缩放 / 追加行列后手柄要跟着走)。 */\n const remeasureHover = useCallback(() => {\n const cell = hoverRef.current?.cell;\n if (!cell) return;\n const next = cell.isConnected ? measureCell(editor, cell) : null;\n hoverRef.current = next;\n setHover(next);\n }, [editor]);\n /** 离开后的收起定时器(悬停意图,见下面的注释)。 */\n const clearTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const cancelPendingClear = useCallback(() => {\n if (clearTimerRef.current === null) return;\n clearTimeout(clearTimerRef.current);\n clearTimerRef.current = null;\n }, []);\n\n // 悬停追踪挂在 document 上。\n //\n // 关键点:**离开单元格 ≠ 离开**。指针从单元格走向手柄时,中间必然经过一小段既不是单元格、\n // 也不在手柄上的地方(手柄贴在表格外侧,本身只有 28px 高,斜着移动更是会绕过它),\n // 那一刻若立刻收起,手柄就在你点到之前消失了。所以这里用「悬停意图」:不在单元格 / 手柄上时\n // 只挂一个 ~160ms 的收起定时器,指针在这段时间内碰到手柄或单元格就取消。\n useEffect(() => {\n const onMove = (event: MouseEvent) => {\n if (!editor.isEditable || menuRef.current) return;\n const target = event.target as HTMLElement | null;\n\n // 指针已经到手柄上:留着,并取消待收起\n if (target?.closest?.('[data-table-handle], [data-table-extend]')) {\n cancelPendingClear();\n return;\n }\n\n const cell =\n target && editor.view.dom.contains(target)\n ? (target.closest('td, th') as HTMLElement | null)\n : null;\n\n if (!cell) {\n // 既不在单元格也不在手柄上:宽限一段再收(指针可能正走向手柄)\n if (hoverRef.current && clearTimerRef.current === null) {\n clearTimerRef.current = setTimeout(() => {\n clearTimerRef.current = null;\n clearHover();\n }, HOVER_KEEP_MS);\n }\n return;\n }\n\n cancelPendingClear();\n if (hoverRef.current?.cell === cell) return;\n const next = measureCell(editor, cell);\n hoverRef.current = next;\n setHover(next);\n };\n const onBlurWindow = () => {\n cancelPendingClear();\n clearHover();\n };\n // 滚动 / 缩放后手柄的 fixed 坐标会失真:重新量那颗单元格让手柄跟着走,而不是直接收起\n // (窗口一动手柄就没了很烦)。单元格已经不在文档里了才收起。\n document.addEventListener('mousemove', onMove);\n document.addEventListener('mouseleave', onBlurWindow);\n window.addEventListener('scroll', remeasureHover, true);\n window.addEventListener('resize', remeasureHover);\n return () => {\n cancelPendingClear();\n document.removeEventListener('mousemove', onMove);\n document.removeEventListener('mouseleave', onBlurWindow);\n window.removeEventListener('scroll', remeasureHover, true);\n window.removeEventListener('resize', remeasureHover);\n };\n }, [editor, clearHover, cancelPendingClear, remeasureHover]);\n\n // 右键:只服务多格选区(单行 / 单列交给手柄)\n useEffect(() => {\n const dom = editor.view.dom;\n const onContextMenu = (event: MouseEvent) => {\n const info = getTableSelectionInfo(editor.state);\n const { selection } = editor.state;\n if (!info?.isMultiCell || !(selection instanceof CellSelection)) return;\n const coords = editor.view.posAtCoords({\n left: event.clientX,\n top: event.clientY,\n });\n if (!coords || !isPosInCellSelection(selection, coords.pos)) return;\n event.preventDefault();\n hoverRef.current = null;\n setHover(null);\n setMenu({ kind: 'multi', x: event.clientX, y: event.clientY });\n };\n dom.addEventListener('contextmenu', onContextMenu);\n return () => dom.removeEventListener('contextmenu', onContextMenu);\n }, [editor]);\n\n // 菜单开着时:点外面 / Esc 收起(菜内点击不关,交给菜单项自己收)\n useEffect(() => {\n if (!menu) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') closeMenu();\n };\n const onDown = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n if (target?.closest?.('[data-table-menu]')) return;\n closeMenu();\n };\n document.addEventListener('keydown', onKey, true);\n document.addEventListener('mousedown', onDown);\n return () => {\n document.removeEventListener('keydown', onKey, true);\n document.removeEventListener('mousedown', onDown);\n };\n }, [menu, closeMenu]);\n\n if (typeof document === 'undefined') return null;\n\n const openHandleMenu = (kind: 'row' | 'col') => {\n if (!hover) return;\n if (!selectRowOrColumn(editor, hover.cell, kind)) return;\n setHover(null);\n hoverRef.current = null;\n setMenu(\n kind === 'row'\n ? { kind, x: hover.tableLeft + 6, y: hover.rowTop }\n : { kind, x: hover.colLeft, y: hover.tableTop + 6 },\n );\n };\n\n const items = menu ? menuItemsFor(editor, labels, menu.kind) : [];\n const showHandles = !!hover && !menu && editor.isEditable;\n\n return createPortal(\n <>\n {showHandles && hover ? (\n <>\n <button\n type=\"button\"\n data-table-handle=\"row\"\n className={`${styles.handle} ${styles.handleRow}`}\n style={{\n left: hover.tableLeft - ROW_HANDLE_GAP,\n top: hover.rowTop,\n width: ROW_HANDLE_WIDTH,\n height: hover.rowHeight,\n }}\n title={labels.tableRowMenu}\n aria-label={labels.tableRowMenu}\n // 保住编辑器选区:点手柄不该先把光标丢掉\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => openHandleMenu('row')}\n >\n <span aria-hidden>⋮</span>\n </button>\n <button\n type=\"button\"\n data-table-handle=\"col\"\n className={`${styles.handle} ${styles.handleCol}`}\n style={{\n left: hover.colLeft,\n top: hover.tableTop - COL_HANDLE_GAP,\n width: hover.colWidth,\n height: COL_HANDLE_HEIGHT,\n }}\n title={labels.tableColumnMenu}\n aria-label={labels.tableColumnMenu}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => openHandleMenu('col')}\n >\n <span aria-hidden>⋯</span>\n </button>\n\n {/* 末尾追加:只有悬停贴着最后一列 / 最后一行时才出现对应的一条 `+`\n (鼠标停在表格中间不该冒加号)。长度跟着表格尺寸走,点完重新量一次。 */}\n {hover.onLastCol ? (\n <button\n type=\"button\"\n data-table-extend=\"col\"\n className={styles.extend}\n style={{\n left: hover.tableRight + EXTEND_GAP,\n top: hover.tableTop,\n width: EXTEND_THICKNESS,\n height: Math.max(EXTEND_THICKNESS, hover.tableBottom - hover.tableTop),\n }}\n title={labels.tableAppendColumn}\n aria-label={labels.tableAppendColumn}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n appendColumnToTable(editor);\n remeasureHover();\n }}\n >\n <span aria-hidden>+</span>\n </button>\n ) : null}\n {hover.onLastRow ? (\n <button\n type=\"button\"\n data-table-extend=\"row\"\n className={styles.extend}\n style={{\n left: hover.tableLeft,\n top: hover.tableBottom + EXTEND_GAP,\n width: Math.max(EXTEND_THICKNESS, hover.tableRight - hover.tableLeft),\n height: EXTEND_THICKNESS,\n }}\n title={labels.tableAppendRow}\n aria-label={labels.tableAppendRow}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n appendRowToTable(editor);\n remeasureHover();\n }}\n >\n <span aria-hidden>+</span>\n </button>\n ) : null}\n </>\n ) : null}\n\n {menu ? (\n <div\n className={styles.tableBubble}\n data-table-menu=\"\"\n style={{ left: menu.x, top: menu.y }}\n role=\"menu\"\n onMouseDown={(e) => e.stopPropagation()}\n onContextMenu={(e) => e.preventDefault()}\n >\n {items.map((item, index) => (\n <Fragment key={item.key}>\n {index > 0 && item.group !== items[index - 1].group ? (\n <span className={styles.tableMenuDivider} aria-hidden />\n ) : null}\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.tableMenuItem}\n disabled={item.disabled}\n onClick={() => {\n item.run();\n closeMenu();\n }}\n >\n <span className={styles.tableMenuItemIcon} aria-hidden>\n {item.icon}\n </span>\n <span className={styles.tableMenuItemLabel}>{item.label}</span>\n </button>\n </Fragment>\n ))}\n <span className={styles.tableMenuDivider} aria-hidden />\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.tableMenuItem}\n onClick={() => {\n editor.commands.deleteTable();\n closeMenu();\n }}\n >\n <span className={styles.tableMenuItemIcon} aria-hidden>\n <TableDeleteIcon />\n </span>\n <span className={styles.tableMenuItemLabel}>\n {labels.tableDeleteTable}\n </span>\n </button>\n </div>\n ) : null}\n </>,\n document.body,\n );\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/react';\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type RefObject,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { renderMathInto, type MathKind } from '../math';\nimport styles from '../styles/toolbar.module.css';\n\nexport interface MathEditorPopoverProps {\n editor: Editor;\n kind: MathKind;\n mode: 'insert' | 'edit';\n pos?: number;\n latex: string;\n /** 工具栏新插入:取消时删掉占位节点 */\n isNew?: boolean;\n newLabel: string;\n placeholder: string;\n doneLabel: string;\n onConfirm: (latex: string) => void;\n onCancel: () => void;\n}\n\nfunction resolveNodeEl(editor: Editor, pos: number): HTMLElement | null {\n const dom = editor.view.nodeDOM(pos);\n if (dom instanceof HTMLElement) return dom;\n if (dom instanceof Node) return dom.parentElement;\n return null;\n}\n\nfunction mathRenderTarget(el: HTMLElement, isBlock: boolean): HTMLElement {\n if (!isBlock) return el;\n return (el.querySelector('.block-math-inner') as HTMLElement | null) ?? el;\n}\n\nfunction getMathLayerStyle(\n editor: Editor,\n kind: MathKind,\n pos?: number,\n): CSSProperties {\n const host = editor.view.dom.parentElement;\n if (!host) return {};\n const hostRect = host.getBoundingClientRect();\n const editorRect = editor.view.dom.getBoundingClientRect();\n const gap = 6;\n const blockWidth = editor.view.dom.clientWidth;\n\n if (pos != null) {\n const el = resolveNodeEl(editor, pos);\n if (el) {\n const r = el.getBoundingClientRect();\n const top = r.bottom - hostRect.top + gap;\n if (kind === 'block') {\n return {\n left: editorRect.left - hostRect.left,\n top,\n width: blockWidth,\n };\n }\n return {\n left: r.left - hostRect.left,\n top,\n };\n }\n }\n\n const coords = editor.view.coordsAtPos(editor.state.selection.from);\n if (kind === 'block') {\n return {\n left: editorRect.left - hostRect.left,\n top: coords.bottom - hostRect.top + gap,\n width: blockWidth,\n };\n }\n return {\n left: coords.left - hostRect.left,\n top: coords.bottom - hostRect.top + gap,\n };\n}\n\nexport function MathEditorPopover({\n editor,\n kind,\n mode,\n pos,\n latex,\n isNew = false,\n newLabel,\n placeholder,\n doneLabel,\n onConfirm,\n onCancel,\n}: MathEditorPopoverProps) {\n const [draft, setDraft] = useState(latex);\n const [layerStyle, setLayerStyle] = useState<CSSProperties>(() =>\n getMathLayerStyle(editor, kind, pos),\n );\n const titleId = useId();\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n const rootRef = useRef<HTMLDivElement>(null);\n const isBlock = kind === 'block';\n const host = editor.view.dom.parentElement;\n\n useEffect(() => {\n setDraft(latex);\n }, [latex]);\n\n useEffect(() => {\n const update = () => setLayerStyle(getMathLayerStyle(editor, kind, pos));\n update();\n window.addEventListener('resize', update);\n return () => window.removeEventListener('resize', update);\n }, [editor, kind, pos, draft]);\n\n useEffect(() => {\n inputRef.current?.focus();\n }, []);\n\n useEffect(() => {\n if (pos == null) return;\n const el = resolveNodeEl(editor, pos);\n if (!el) return;\n el.classList.add('tmr-math-editing');\n return () => {\n el.classList.remove('tmr-math-editing');\n };\n }, [editor, pos]);\n\n useEffect(() => {\n if (pos == null) return;\n const el = resolveNodeEl(editor, pos);\n if (!el) return;\n const target = mathRenderTarget(el, isBlock);\n const src = draft.trim();\n if (src) renderMathInto(target, src, isBlock);\n else target.textContent = '';\n }, [draft, pos, editor, isBlock]);\n\n useEffect(() => {\n if (pos == null || isNew) return;\n const el = resolveNodeEl(editor, pos);\n if (!el) return;\n const target = mathRenderTarget(el, isBlock);\n return () => {\n const restore = latex.trim();\n if (restore) renderMathInto(target, restore, isBlock);\n else target.textContent = '';\n };\n }, [pos, editor, isBlock, latex, isNew]);\n\n useEffect(() => {\n const onPtr = (e: PointerEvent) => {\n const target = e.target;\n if (!(target instanceof Node)) return;\n if (rootRef.current?.contains(target)) return;\n if (pos != null) {\n const el = resolveNodeEl(editor, pos);\n if (el?.contains(target)) return;\n }\n onCancel();\n };\n document.addEventListener('pointerdown', onPtr);\n return () => document.removeEventListener('pointerdown', onPtr);\n }, [onCancel, editor, pos]);\n\n const canSubmit = draft.trim().length > 0;\n const showHint = !canSubmit && (mode === 'insert' || isNew);\n\n const submit = () => {\n if (!canSubmit) return;\n onConfirm(draft.trim());\n };\n\n const onKeyDown = (\n e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>,\n ) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n onCancel();\n return;\n }\n if (e.key !== 'Enter') return;\n if (isBlock && !(e.metaKey || e.ctrlKey)) return;\n e.preventDefault();\n submit();\n };\n\n const field = isBlock ? (\n <textarea\n ref={inputRef as RefObject<HTMLTextAreaElement>}\n className={styles.mathInput}\n value={draft}\n placeholder={placeholder}\n rows={Math.max(3, draft.split('\\n').length)}\n aria-labelledby={titleId}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n />\n ) : (\n <input\n ref={inputRef as RefObject<HTMLInputElement>}\n className={styles.mathInput}\n value={draft}\n placeholder={placeholder}\n aria-labelledby={titleId}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n />\n );\n\n if (!host) return null;\n\n return createPortal(\n <div\n ref={rootRef}\n className={styles.mathLayer}\n style={layerStyle}\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby={titleId}\n >\n <div\n className={\n isBlock\n ? `${styles.mathStack} ${styles.mathStackBlock}`\n : styles.mathStack\n }\n >\n {showHint ? (\n <div\n id={titleId}\n className={\n isBlock\n ? `${styles.mathHint} ${styles.mathHintBlock}`\n : styles.mathHint\n }\n >\n <span className={styles.mathHintIcon} aria-hidden>\n {isBlock ? 'TeX' : '√x'}\n </span>\n {newLabel}\n </div>\n ) : null}\n <div\n id={showHint ? undefined : titleId}\n className={\n isBlock\n ? `${styles.mathField} ${styles.mathFieldBlock}`\n : styles.mathField\n }\n >\n {field}\n <button\n type=\"button\"\n className={styles.mathDone}\n disabled={!canSubmit}\n title={\n isBlock ? `${doneLabel} (⌘/Ctrl+Enter)` : `${doneLabel} (Enter)`\n }\n onClick={submit}\n >\n {doneLabel}\n <span className={styles.mathDoneKbd} aria-hidden>\n ↵\n </span>\n </button>\n </div>\n </div>\n </div>,\n host,\n );\n}\n","'use client';\n\nimport * as Popover from '@radix-ui/react-popover';\nimport { useEditorState, type Editor } from '@tiptap/react';\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ButtonHTMLAttributes,\n type ReactNode,\n} from 'react';\nimport {\n AlignCenterIcon,\n AlignJustifyIcon,\n AlignLeftIcon,\n AlignRightIcon,\n BoldIcon,\n ChevronDownIcon,\n CodeIcon,\n DetailsIcon,\n ImageIcon,\n IndentDecreaseIcon,\n IndentIncreaseIcon,\n ItalicIcon,\n LineHeightIcon,\n LinkIcon,\n ListChecksIcon,\n ListOrderedIcon,\n ListUnorderedIcon,\n RedoIcon,\n SearchIcon,\n StrikethroughIcon,\n UnderlineIcon,\n UndoIcon,\n} from '../icons';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport type { ToolbarLabels } from '../labels';\nimport {\n applyNumberingFormat,\n NUMBERING_FORMAT_IDS,\n NUMBERING_FORMAT_LABELS,\n NUMBERING_FORMAT_PREVIEWS,\n readCurrentNumberingFormat,\n type NumberingFormat,\n} from '../numberingFormat';\nimport {\n MD_FILE_RE,\n resolveImportMenuItems,\n type ImportMenuItem,\n} from '../importMenu';\nimport type { SourceRef } from '../citationUtils';\nimport { hasImageUpload } from '../imageUpload';\nimport { insertMarkdown } from '../insertMarkdown';\nimport { subscribeMathClick, type MathKind } from '../math';\nimport styles from '../styles/toolbar.module.css';\nimport { ColorPalette } from './ColorPalette';\nimport { ImageAlignTools } from './ImageAlignTools';\nimport { LinkPopover } from './LinkPopover';\nimport { TableHandles } from './TableHandles';\nimport { MathEditorPopover } from './MathEditorPopover';\n\n/** `onImportDocument` 的成功返回。给字符串等价于 `{ markdown }`。 */\nexport interface ImportDocumentResult {\n markdown: string;\n sources?: SourceRef[];\n}\n\n/** 导入进度。包用来更新光标占位文案并禁用导入按钮。 */\nexport interface ImportDocumentProgress {\n /** 0–1。通常只有上传阶段可测;转换阶段可省略。 */\n ratio?: number;\n phase: 'upload' | 'convert';\n}\n\nexport interface ImportDocumentContext {\n /** 用户点取消或组件卸载时 abort,宿主据此中断请求。 */\n signal: AbortSignal;\n onProgress?: (p: ImportDocumentProgress) => void;\n}\n\n/** More 菜单里注入的自定义项(宿主用它扩展工具栏,如「导入项目报告」)。 */\nexport interface ExtraToolbarItem {\n key: string;\n /** 左侧图标插槽(可选,文字或 SVG 皆可)。 */\n icon?: ReactNode;\n label: string;\n disabled?: boolean;\n /** 点击回调,拿到 editor 实例自行操作(如 insertContent)。 */\n onSelect: (editor: Editor) => void;\n}\n\nexport interface EditorToolbarProps {\n editor: Editor;\n /**\n * 图片上传回调:收到文件,返回可访问的图片 URL。未提供则不显示图片按钮。\n * 抛错会转交 onError(..., 'image')。\n */\n onImageUpload?: (file: File) => Promise<string>;\n /**\n * 副作用错误回调(图片上传 / Markdown 导入失败),供宿主弹自己的提示。\n * `source` 区分来源,避免一律显示「图片上传失败」。\n */\n onError?: (err: unknown, source?: 'image' | 'markdown' | 'import') => void;\n /**\n * 非 Markdown 文件的转换:收到文件,返回可插入的 Markdown。未提供则导入只吃 .md。\n * 包内不校验扩展名——格式白名单由宿主的 `importAccept` 与本回调自己负责。\n * 抛错转交 onError(..., 'import');`AbortError` 视为用户取消,不报错。\n */\n onImportDocument?: (\n file: File,\n ctx: ImportDocumentContext,\n ) => Promise<ImportDocumentResult | string>;\n /**\n * 追加到导入 input 的 accept(默认已含 `.md,.markdown,text/markdown`)。\n * 例:`.doc,.docx,.csv,.pdf`。未传 `importMenuItems` 且传了 `onImportDocument` 时,\n * 下拉会多一项「Document」。\n */\n importAccept?: string;\n /**\n * 主栏导入下拉的选项。不传则 Markdown;再加 `onImportDocument` + `importAccept`\n * 时多一项 Document。宿主要拆成 Word / PDF 时自己传入。\n */\n importMenuItems?: ImportMenuItem[];\n /** 是否显示导入下拉,默认 `true`。卡片等场景可关掉。 */\n showImport?: boolean;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n /**\n * 工具栏最右端的搜索入口(放大镜)。**不传就不渲染**——条子本身归\n * `<MarkdownWysiwygEditor>`,这里只给一个入口,宿主接上自己的编辑器句柄即可:\n * `onSearch={() => handle.current?.openFind()}`。\n *\n * 条子归编辑器渲染(`findBar` 开)时才接;`findBar={false}` 的宿主自己摆条子,\n * 这个入口点了也不会弹东西。只读态照样可查,所以不随 `editable` 灰显。\n */\n onSearch?: () => void;\n /** 条子当前是否开着(驱动按钮的 aria-pressed / 高亮)。不传恒为未激活。 */\n searchActive?: boolean;\n /** 追加到 More 菜单末尾的自定义项。 */\n extraToolbarItems?: ExtraToolbarItem[];\n className?: string;\n}\n\nexport type { ImportMenuItem };\n\n/**\n * 单个工具栏按钮:激活态高亮,disabled 时灰显。\n *\n * forwardRef + 透传其余属性,是为了让它能当 Radix 的触发器(`<LinkPopover>` 的 `asChild`\n * 要挂 ref,并把 `aria-expanded` / `data-state` 传下来)。\n */\nconst ToolbarButton = forwardRef<\n HTMLButtonElement,\n {\n title: string;\n active?: boolean;\n disabled?: boolean;\n busy?: boolean;\n onClick?: () => void;\n children: ReactNode;\n } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'title' | 'onClick' | 'children'>\n>(function ToolbarButton(\n { title, active, disabled, busy, onClick, children, className, ...rest },\n ref,\n) {\n return (\n <button\n {...rest}\n ref={ref}\n type=\"button\"\n title={title}\n aria-label={title}\n aria-pressed={active}\n aria-busy={busy}\n disabled={disabled}\n className={`${styles.btn} ${active ? styles.btnActive : ''}${className ? ` ${className}` : ''}`}\n // mousedown + preventDefault 防止点击工具栏时编辑器失焦丢选区\n onMouseDown={(e) => e.preventDefault()}\n onClick={onClick}\n >\n {children}\n </button>\n );\n});\n\nfunction Divider() {\n return <span className={styles.divider} aria-hidden />;\n}\n\n/** 行高下拉的档位(对齐语雀 / Notion 的习惯值;不含\"默认\")。 */\nconst LINE_HEIGHTS = ['1', '1.15', '1.5', '2', '2.5', '3'] as const;\n\n/** 段落级样式读自段落或标题(光标所在处)——对齐 / 缩进 / 行高共用。 */\nfunction readParaAttr<T>(editor: Editor, name: string): T | null {\n return (\n ((editor.getAttributes('paragraph')[name] ??\n editor.getAttributes('heading')[name]) as T | undefined) ?? null\n );\n}\n\n// 关闭当前菜单的回调(由 MenuPopover 注入,MenuItem 选中后调用以收起)。\nconst MenuCloseCtx = createContext<() => void>(() => {});\n\n/**\n * 菜单气泡:用 Radix Popover 而非 DropdownMenu——后者会强制抢焦点(menu 语义),\n * 打开即让编辑器 blur、丢选区;Popover 可 onOpenAutoFocus preventDefault 保持编辑器焦点。\n * 菜单项为普通 button(onMouseDown preventDefault 不夺焦),行为与原生菜单一致。\n */\nfunction MenuPopover({\n trigger,\n children,\n contentClassName,\n align = 'start',\n}: {\n trigger: ReactNode;\n children: ReactNode;\n contentClassName?: string;\n align?: 'start' | 'center' | 'end';\n}) {\n const [open, setOpen] = useState(false);\n const close = useCallback(() => setOpen(false), []);\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger asChild>{trigger}</Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n className={\n contentClassName ? `${styles.menu} ${contentClassName}` : styles.menu\n }\n sideOffset={4}\n align={align}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <MenuCloseCtx.Provider value={close}>\n {children}\n </MenuCloseCtx.Provider>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\nfunction MenuItem({\n selected,\n disabled,\n onSelect,\n children,\n}: {\n selected?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n children: ReactNode;\n}) {\n const close = useContext(MenuCloseCtx);\n return (\n <button\n type=\"button\"\n disabled={disabled}\n className={`${styles.menuItem} ${selected ? styles.menuItemSelected : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onSelect();\n close();\n }}\n >\n {children}\n </button>\n );\n}\n\n/** 编号格式:单列预览卡片,每张是五行嵌套编号。 */\nfunction NumberingFormatGrid({\n current,\n onSelect,\n}: {\n current: NumberingFormat | null;\n onSelect: (format: NumberingFormat) => void;\n}) {\n const close = useContext(MenuCloseCtx);\n return (\n <div className={styles.numberingGrid} role=\"group\" aria-label=\"Numbering formats\">\n {NUMBERING_FORMAT_IDS.map((format) => {\n const active = current === format;\n return (\n <button\n key={format}\n type=\"button\"\n role=\"menuitem\"\n aria-label={NUMBERING_FORMAT_LABELS[format]}\n aria-pressed={active}\n className={`${styles.numberingOption}${active ? ` ${styles.numberingOptionActive}` : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onSelect(format);\n close();\n }}\n >\n <span className={styles.numberingPreview}>\n {NUMBERING_FORMAT_PREVIEWS[format].map((row, index) => (\n <span\n key={`${format}-${index}`}\n className={styles.numberingPreviewRow}\n data-depth={row.depth}\n >\n <span className={styles.numberingPreviewMarker}>{row.marker}</span>\n <span className={styles.numberingPreviewLine} />\n </span>\n ))}\n </span>\n </button>\n );\n })}\n </div>\n );\n}\n\nconst TABLE_PICKER_COLS = 10;\nconst TABLE_PICKER_ROWS = 8;\n\n/**\n * More 菜单里的「插入表格」:二级面板用网格悬停选行列,点击插入。\n * 保持 Popover(不夺编辑器焦点),与 MenuItem 一致。\n */\nfunction TableInsertItem({\n disabled,\n labels,\n onInsert,\n}: {\n disabled?: boolean;\n labels: Pick<ToolbarLabels, 'tableInsert' | 'tableSizeSelected'>;\n onInsert: (rows: number, cols: number) => void;\n}) {\n const closeMenu = useContext(MenuCloseCtx);\n const [open, setOpen] = useState(false);\n const [hover, setHover] = useState({ cols: 0, rows: 0 });\n\n const resetHover = useCallback(() => setHover({ cols: 0, rows: 0 }), []);\n\n return (\n <Popover.Root\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) resetHover();\n }}\n >\n <Popover.Trigger asChild>\n <button\n type=\"button\"\n disabled={disabled}\n className={`${styles.menuItem} ${open ? styles.menuItemSelected : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>⊞</span>\n {labels.tableInsert}\n </span>\n <ChevronDownIcon size={12} className={styles.tableInsertCaret} />\n </button>\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n className={styles.tableSizePicker}\n side=\"right\"\n align=\"start\"\n sideOffset={6}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <div\n className={styles.tableSizeGrid}\n style={{\n gridTemplateColumns: `repeat(${TABLE_PICKER_COLS}, 1fr)`,\n }}\n onMouseLeave={resetHover}\n >\n {Array.from({ length: TABLE_PICKER_ROWS * TABLE_PICKER_COLS }, (_, i) => {\n const col = (i % TABLE_PICKER_COLS) + 1;\n const row = Math.floor(i / TABLE_PICKER_COLS) + 1;\n const active = hover.cols > 0 && col <= hover.cols && row <= hover.rows;\n return (\n <button\n key={i}\n type=\"button\"\n aria-label={labels.tableSizeSelected(col, row)}\n className={`${styles.tableSizeCell} ${active ? styles.tableSizeCellActive : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n onMouseEnter={() => setHover({ cols: col, rows: row })}\n onClick={() => {\n onInsert(row, col);\n setOpen(false);\n resetHover();\n closeMenu();\n }}\n />\n );\n })}\n </div>\n <div className={styles.tableSizeLabel}>\n {hover.cols > 0\n ? labels.tableSizeSelected(hover.cols, hover.rows)\n : labels.tableInsert}\n </div>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** 颜色气泡:点色块即应用并关闭。 */\nfunction ColorPopover({\n title,\n glyph,\n bar,\n value,\n onPick,\n locale,\n}: {\n title: string;\n glyph: string;\n bar: string;\n value?: string;\n onPick: (color: string | null) => void;\n locale: LocaleCode;\n}) {\n const [open, setOpen] = useState(false);\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger asChild>\n <button\n type=\"button\"\n className={styles.btn}\n title={title}\n aria-label={title}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.colorIcon}>\n <span className={styles.colorIconGlyph}>{glyph}</span>\n <span className={styles.colorIconBar} style={{ background: bar }} />\n </span>\n </button>\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n className={styles.popoverContent}\n sideOffset={4}\n align=\"start\"\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <ColorPalette\n value={value}\n onPick={(c) => {\n onPick(c);\n setOpen(false);\n }}\n locale={locale}\n />\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\nconst HEADING_LEVELS = [1, 2, 3, 4, 5, 6] as const;\nconst FONT_SIZES = ['12px', '14px', '16px', '18px', '20px', '24px', '30px'] as const;\n\n/**\n * 顶部操作栏(零 UI 框架依赖,Radix Popover 菜单 + 内联 SVG 图标)。位置由父级控制,\n * 这里只渲染按钮 + 反映 editor 激活态。不含业务耦合,扩展项经 extraToolbarItems 注入。\n */\nexport function EditorToolbar({\n editor,\n onImageUpload,\n onError,\n onImportDocument,\n importAccept,\n importMenuItems,\n showImport = true,\n locale = 'en',\n onSearch,\n searchActive = false,\n extraToolbarItems,\n className,\n}: EditorToolbarProps) {\n const t: ToolbarLabels = getLocaleBundle(locale).toolbar;\n\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n bold: e.isActive('bold'),\n italic: e.isActive('italic'),\n underline: e.isActive('underline'),\n strike: e.isActive('strike'),\n code: e.isActive('code'),\n link: e.isActive('link'),\n taskList: e.isActive('taskList'),\n headingLevel: ([1, 2, 3, 4, 5, 6] as const).find((lv) =>\n e.isActive('heading', { level: lv }),\n ),\n fontSize: (e.getAttributes('textStyle').fontSize as string) || undefined,\n isParagraph: e.isActive('paragraph'),\n bulletList: e.isActive('bulletList'),\n orderedList: e.isActive('orderedList'),\n numberingFormat: readCurrentNumberingFormat(e),\n blockquote: e.isActive('blockquote'),\n details: e.isActive('details'),\n textColor: (e.getAttributes('textStyle').color as string) || undefined,\n highlightColor:\n (e.getAttributes('highlight').color as string) || undefined,\n superscript: e.isActive('superscript'),\n subscript: e.isActive('subscript'),\n textAlign: readParaAttr<string>(e, 'textAlign'),\n indent: readParaAttr<number>(e, 'indent'),\n lineHeight: readParaAttr<number>(e, 'lineHeight'),\n inTable: e.isActive('table'),\n canUndo: e.can().undo(),\n canRedo: e.can().redo(),\n }),\n });\n\n // 编辑器是否聚焦:仅正文聚焦时才允许调字号。Popover 菜单开/关已阻止抢焦点,\n // 故打开下拉不会误触 blur。\n const [editorFocused, setEditorFocused] = useState(() => editor.isFocused);\n useEffect(() => {\n const onFocus = () => setEditorFocused(true);\n const onBlur = () => setEditorFocused(false);\n editor.on('focus', onFocus);\n editor.on('blur', onBlur);\n return () => {\n editor.off('focus', onFocus);\n editor.off('blur', onBlur);\n };\n }, [editor]);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const chain = () => editor.chain().focus();\n\n // ── 表格工具条(右键召唤):右键单元格时在鼠标处弹出,点别处/Esc 隐藏 ──\n // ── 图片:编辑器注册了 imageUpload 扩展就插上传占位块(块内拖拽/点选 → 进度 → 换图,\n // 配置见 <MarkdownWysiwygEditor imageUpload>);否则退回旧的直传路径 ──\n const imageUploadEnabled = hasImageUpload(editor);\n const handlePickImage = () => {\n if (imageUploadEnabled) {\n chain().setImageUploadNode().run();\n return;\n }\n fileInputRef.current?.click();\n };\n const handleImageChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = '';\n if (!file || !onImageUpload) return;\n try {\n const url = await onImageUpload(file);\n if (!url) throw new Error('empty url');\n chain().setImage({ src: url }).run();\n } catch (err) {\n console.error('image upload failed:', err);\n onError?.(err, 'image');\n }\n };\n\n // ── 导入:.md 包内读文本插入;其余原样交给宿主 onImportDocument ──\n const importInputRef = useRef<HTMLInputElement>(null);\n const [importing, setImporting] = useState<ImportDocumentProgress | null>(\n null,\n );\n const importAbortRef = useRef<AbortController | null>(null);\n\n // 卸载(如切路由)中断在飞的导入,避免请求泄漏与卸载后 setState\n useEffect(() => () => importAbortRef.current?.abort(), []);\n\n const handleImportChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = '';\n if (!file) return;\n\n if (MD_FILE_RE.test(file.name)) {\n try {\n insertMarkdown(editor, await file.text());\n } catch (err) {\n console.error('markdown import failed:', err);\n onError?.(err, 'markdown');\n }\n return;\n }\n\n if (!onImportDocument) {\n onError?.(new Error(`unsupported file: ${file.name}`), 'import');\n return;\n }\n\n const ctrl = new AbortController();\n importAbortRef.current = ctrl;\n setImporting({ phase: 'upload', ratio: 0 });\n const labelOf = (p: ImportDocumentProgress) =>\n p.phase === 'convert'\n ? t.importDocumentConverting\n : t.importDocumentUploading(Math.round((p.ratio ?? 0) * 100));\n editor.commands.insertImportPlaceholder(labelOf({ phase: 'upload', ratio: 0 }));\n try {\n const raw = await onImportDocument(file, {\n signal: ctrl.signal,\n onProgress: (p) => {\n if (ctrl.signal.aborted) return;\n setImporting(p);\n editor.commands.updateImportPlaceholder(labelOf(p));\n },\n });\n editor.commands.removeImportPlaceholder();\n const md = typeof raw === 'string' ? raw : raw.markdown;\n const sources = typeof raw === 'string' ? [] : raw.sources ?? [];\n insertMarkdown(editor, md, sources);\n } catch (err) {\n editor.commands.removeImportPlaceholder();\n // 卸载 abort 不算错误\n if (!ctrl.signal.aborted) {\n console.error('document import failed:', err);\n onError?.(err, 'import');\n }\n } finally {\n if (importAbortRef.current === ctrl) importAbortRef.current = null;\n setImporting(null);\n }\n };\n\n const importItems = resolveImportMenuItems({\n labels: t,\n onImportDocument,\n importAccept,\n importMenuItems,\n });\n\n const pickImport = (accept: string) => {\n const input = importInputRef.current;\n if (!input) return;\n input.accept = accept;\n input.value = '';\n input.click();\n };\n\n // 进度写在光标占位上;按钮只区分空闲 / 忙碌。\n const importTitle = importing ? t.importDocumentBusy : t.importDocumentHint;\n\n const applyTextColor = (color: string | null) => {\n if (color) chain().setColor(color).run();\n else chain().unsetColor().run();\n };\n const applyHighlight = (color: string | null) => {\n if (color) chain().setHighlight({ color }).run();\n else chain().unsetHighlight().run();\n };\n\n const setBlockStyle = (level: number) => {\n if (level === 0) chain().setParagraph().run();\n else\n chain()\n .toggleHeading({ level: level as 1 | 2 | 3 | 4 | 5 | 6 })\n .run();\n };\n\n const applyFontSize = (size: string) => {\n if (size) chain().setFontSize(size).run();\n else chain().unsetFontSize().run();\n };\n const canEditFontSize = editorFocused && state.isParagraph;\n\n const [mathEdit, setMathEdit] = useState<{\n kind: MathKind;\n mode: 'insert' | 'edit';\n pos?: number;\n latex: string;\n isNew?: boolean;\n } | null>(null);\n\n useEffect(() => subscribeMathClick((payload) => {\n setMathEdit({\n kind: payload.kind,\n mode: 'edit',\n pos: payload.pos,\n latex: payload.latex,\n });\n }), []);\n\n const openInsertMath = (kind: MathKind) => {\n const { from, to } = editor.state.selection;\n const selected =\n from === to ? '' : editor.state.doc.textBetween(from, to, ' ');\n const type = kind === 'inline' ? 'inlineMath' : 'blockMath';\n const searchFrom = from;\n chain().insertContent({ type, attrs: { latex: selected } }).run();\n let pos: number | null = null;\n editor.state.doc.nodesBetween(\n searchFrom,\n editor.state.doc.content.size,\n (node, nodePos) => {\n if (pos != null) return false;\n if (\n node.type.name === type &&\n String(node.attrs.latex ?? '') === selected\n ) {\n pos = nodePos;\n return false;\n }\n },\n );\n if (pos == null) {\n setMathEdit({ kind, mode: 'insert', latex: selected, isNew: true });\n return;\n }\n setMathEdit({\n kind,\n mode: 'edit',\n pos,\n latex: selected,\n isNew: true,\n });\n };\n\n const applyMath = (latex: string) => {\n if (!mathEdit) return;\n const { kind, mode, pos } = mathEdit;\n setMathEdit(null);\n if (mode === 'edit' && pos != null) {\n if (kind === 'inline') {\n chain().updateInlineMath({ latex, pos }).run();\n } else {\n chain().updateBlockMath({ latex, pos }).run();\n }\n return;\n }\n if (kind === 'inline') {\n chain().insertContent({ type: 'inlineMath', attrs: { latex } }).run();\n } else {\n chain().insertContent({ type: 'blockMath', attrs: { latex } }).run();\n }\n };\n\n const cancelMath = () => {\n if (!mathEdit) return;\n const { isNew, pos } = mathEdit;\n if (isNew && pos != null) {\n const node = editor.state.doc.nodeAt(pos);\n if (\n node &&\n (node.type.name === 'inlineMath' || node.type.name === 'blockMath')\n ) {\n editor\n .chain()\n .focus()\n .deleteRange({ from: pos, to: pos + node.nodeSize })\n .run();\n }\n }\n setMathEdit(null);\n };\n\n const scriptActive = state.superscript\n ? 'superscript'\n : state.subscript\n ? 'subscript'\n : 'normal';\n\n return (\n <>\n <div\n className={\n className ? `${styles.toolbar} ${className}` : styles.toolbar\n }\n >\n <div className={styles.inner}>\n {/* 撤销 / 重做 */}\n <ToolbarButton\n title={t.undo}\n disabled={!state.canUndo}\n onClick={() => chain().undo().run()}\n >\n <UndoIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.redo}\n disabled={!state.canRedo}\n onClick={() => chain().redo().run()}\n >\n <RedoIcon />\n </ToolbarButton>\n\n <Divider />\n\n {/* 段落样式下拉(正文 / 标题 1~6) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={t.style}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>{t.style}</span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={(state.headingLevel ?? 0) === 0}\n onSelect={() => setBlockStyle(0)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>¶</span>\n {t.normalText}\n </span>\n </MenuItem>\n {HEADING_LEVELS.map((lv) => (\n <MenuItem\n key={lv}\n selected={state.headingLevel === lv}\n onSelect={() => setBlockStyle(lv)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{`H${lv}`}</span>\n {t.headingLabel(lv)}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n {/* 正文字号下拉:仅正文可调 */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={t.fontSize}\n aria-label={t.fontSize}\n disabled={!canEditFontSize}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>\n {state.fontSize || t.fontSize}\n </span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={!state.fontSize}\n onSelect={() => applyFontSize('')}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>A</span>\n {t.fontSizeDefault}\n </span>\n </MenuItem>\n {FONT_SIZES.map((size) => (\n <MenuItem\n key={size}\n selected={state.fontSize === size}\n onSelect={() => applyFontSize(size)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{parseInt(size, 10)}</span>\n {size}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n <Divider />\n\n {/* 文字格式 */}\n <ToolbarButton\n title={t.bold}\n active={state.bold}\n onClick={() => chain().toggleBold().run()}\n >\n <BoldIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.italic}\n active={state.italic}\n onClick={() => chain().toggleItalic().run()}\n >\n <ItalicIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.underline}\n active={state.underline}\n onClick={() => chain().toggleUnderline().run()}\n >\n <UnderlineIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.strike}\n active={state.strike}\n onClick={() => chain().toggleStrike().run()}\n >\n <StrikethroughIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.code}\n active={state.code}\n onClick={() => chain().toggleCode().run()}\n >\n <CodeIcon />\n </ToolbarButton>\n\n {/* 文字颜色 */}\n <ColorPopover\n title={t.textColor}\n glyph=\"T\"\n bar={state.textColor || '#ff3b30'}\n value={state.textColor}\n onPick={applyTextColor}\n locale={locale}\n />\n {/* 文字高亮 */}\n <ColorPopover\n title={t.highlight}\n glyph=\"✎\"\n bar={state.highlightColor || '#ffd83d'}\n value={state.highlightColor}\n onPick={applyHighlight}\n locale={locale}\n />\n\n {/* 上标 / 下标下拉(A▾) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.scriptTrigger} ${\n scriptActive !== 'normal' ? styles.btnActive : ''\n }`}\n title={t.scriptMenu}\n aria-label={t.scriptMenu}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.scriptTriggerGlyph}>A</span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={scriptActive === 'normal'}\n onSelect={() => chain().unsetSuperscript().unsetSubscript().run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>A</span>\n {t.scriptNormal}\n </span>\n </MenuItem>\n <MenuItem\n selected={scriptActive === 'superscript'}\n onSelect={() => chain().unsetSubscript().toggleSuperscript().run()}\n >\n <span className={styles.styleItem}>\n <span className={`${styles.styleIcon} ${styles.scriptGlyph}`}>\n x<sup>2</sup>\n </span>\n {t.superscript}\n </span>\n </MenuItem>\n <MenuItem\n selected={scriptActive === 'subscript'}\n onSelect={() => chain().unsetSuperscript().toggleSubscript().run()}\n >\n <span className={styles.styleItem}>\n <span className={`${styles.styleIcon} ${styles.scriptGlyph}`}>\n x<sub>2</sub>\n </span>\n {t.subscript}\n </span>\n </MenuItem>\n </MenuPopover>\n\n <Divider />\n\n {/* 插入媒体。链接走浮层:输入地址 + 应用 / 在新窗口打开 / 移除,\n 光标在链接里打开时预填它的 href(旧实现是 window.prompt,在链接里点一下就删)。 */}\n <LinkPopover\n editor={editor}\n locale={locale}\n trigger={\n <ToolbarButton title={t.link} active={state.link}>\n <LinkIcon />\n </ToolbarButton>\n }\n />\n {imageUploadEnabled || onImageUpload ? (\n <>\n <ToolbarButton title={t.image} onClick={handlePickImage}>\n <ImageIcon />\n </ToolbarButton>\n {!imageUploadEnabled && (\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n hidden\n onChange={handleImageChange}\n />\n )}\n </>\n ) : null}\n {showImport ? (\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={importTitle}\n aria-label={importTitle}\n aria-busy={!!importing}\n disabled={!!importing}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>\n {t.importDocument}\n </span>\n <ChevronDownIcon\n size={12}\n className={styles.styleTriggerCaret}\n />\n </button>\n }\n >\n {importItems.map((item) => (\n <MenuItem\n key={item.key}\n onSelect={() => pickImport(item.accept)}\n >\n {item.label}\n </MenuItem>\n ))}\n </MenuPopover>\n ) : null}\n <ToolbarButton\n title={t.blockquote}\n active={state.blockquote}\n onClick={() => chain().toggleBlockquote().run()}\n >\n <span className={styles.txtIcon}>❝</span>\n </ToolbarButton>\n <ToolbarButton\n title={t.details}\n active={state.details}\n onClick={() => {\n const next = editor.chain().focus();\n if (editor.isActive('details')) next.unsetDetails().run();\n else next.setDetails().run();\n }}\n >\n <DetailsIcon />\n </ToolbarButton>\n\n <Divider />\n\n {/* 段落排版:对齐 / 缩进 / 行高(三组下拉) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.styleTrigger} ${styles.iconTrigger}`}\n title={t.align}\n aria-label={t.align}\n onMouseDown={(e) => e.preventDefault()}\n >\n {state.textAlign === 'center' ? (\n <AlignCenterIcon />\n ) : state.textAlign === 'right' ? (\n <AlignRightIcon />\n ) : state.textAlign === 'justify' ? (\n <AlignJustifyIcon />\n ) : (\n <AlignLeftIcon />\n )}\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n {(\n [\n { value: 'left', label: t.alignLeft, Icon: AlignLeftIcon },\n { value: 'center', label: t.alignCenter, Icon: AlignCenterIcon },\n { value: 'right', label: t.alignRight, Icon: AlignRightIcon },\n { value: 'justify', label: t.alignJustify, Icon: AlignJustifyIcon },\n ] as const\n ).map(({ value, label, Icon }) => (\n <MenuItem\n key={value}\n selected={state.textAlign === value}\n onSelect={() => chain().setTextAlign(value).run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>\n <Icon size={14} />\n </span>\n {label}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.styleTrigger} ${styles.iconTrigger}`}\n title={t.indent}\n aria-label={t.indent}\n onMouseDown={(e) => e.preventDefault()}\n >\n <IndentIncreaseIcon />\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem onSelect={() => chain().increaseIndent().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>\n <IndentIncreaseIcon size={14} />\n </span>\n {t.indentIncrease}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => chain().decreaseIndent().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>\n <IndentDecreaseIcon size={14} />\n </span>\n {t.indentDecrease}\n </span>\n </MenuItem>\n </MenuPopover>\n\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.styleTrigger} ${styles.iconTrigger}`}\n title={t.lineHeight}\n aria-label={t.lineHeight}\n onMouseDown={(e) => e.preventDefault()}\n >\n <LineHeightIcon />\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={!state.lineHeight}\n onSelect={() => chain().setLineHeight(null).run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>—</span>\n {t.lineHeightDefault}\n </span>\n </MenuItem>\n {LINE_HEIGHTS.map((value) => (\n <MenuItem\n key={value}\n selected={state.lineHeight === Number(value)}\n onSelect={() => chain().setLineHeight(Number(value)).run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>≡</span>\n {value}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n <Divider />\n\n {/* 列表。有序列表是「切换 + 编号格式」一组:箭头打开编号预览。 */}\n <ToolbarButton\n title={t.bulletList}\n active={state.bulletList}\n onClick={() => chain().toggleBulletList().run()}\n >\n <ListUnorderedIcon />\n </ToolbarButton>\n <span\n className={`${styles.listSplit}${\n state.orderedList ? ` ${styles.listSplitActive}` : ''\n }`}\n >\n <ToolbarButton\n title={t.orderedList}\n active={state.orderedList}\n onClick={() => chain().toggleOrderedList().run()}\n >\n <ListOrderedIcon />\n </ToolbarButton>\n <MenuPopover\n align=\"end\"\n contentClassName={styles.numberingMenu}\n trigger={\n <button\n type=\"button\"\n className={`${styles.numberingCaret}${\n state.numberingFormat ? ` ${styles.numberingCaretActive}` : ''\n }`}\n title={t.numberingFormat}\n aria-label={t.numberingFormat}\n onMouseDown={(e) => e.preventDefault()}\n >\n <ChevronDownIcon size={12} />\n </button>\n }\n >\n <NumberingFormatGrid\n current={state.numberingFormat}\n onSelect={(format) => applyNumberingFormat(editor, format)}\n />\n </MenuPopover>\n </span>\n <ToolbarButton\n title={t.taskList}\n active={state.taskList}\n onClick={() => chain().toggleTaskList().run()}\n >\n <ListChecksIcon />\n </ToolbarButton>\n\n <Divider />\n\n {/* 查找替换入口:条子归编辑器,这里只开一道门。宿主接 onSearch 时才出现。\n 位置在 More 左侧——More 是「其余都以它收口」的兜底菜单,惯例留在最右;\n 而且行内本来就排满,谁在最后谁就先被挤到下一行。 */}\n {onSearch ? (\n <ToolbarButton\n title={t.search}\n active={searchActive}\n onClick={onSearch}\n >\n <SearchIcon />\n </ToolbarButton>\n ) : null}\n\n {/* More 下拉(代码块、分割线、表格 + 宿主注入项) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={t.more}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>{t.more}</span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem onSelect={() => chain().toggleCodeBlock().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{'{ }'}</span>\n {t.codeBlock}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => openInsertMath('inline')}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>√x</span>\n {t.inlineMath}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => openInsertMath('block')}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>∑</span>\n {t.blockMath}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => chain().setHorizontalRule().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>—</span>\n {t.hr}\n </span>\n </MenuItem>\n <TableInsertItem\n disabled={state.inTable}\n labels={t}\n onInsert={(rows, cols) =>\n chain()\n .insertTable({ rows, cols, withHeaderRow: true })\n .run()\n }\n />\n {extraToolbarItems?.map((item) => (\n <MenuItem\n key={item.key}\n disabled={item.disabled}\n onSelect={() => item.onSelect(editor)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{item.icon}</span>\n {item.label}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n </div>\n </div>\n\n {/* 导入的隐藏文件选择器:accept 由下拉选项写入 */}\n {showImport ? (\n <input\n ref={importInputRef}\n type=\"file\"\n hidden\n onChange={handleImportChange}\n />\n ) : null}\n\n {mathEdit ? (\n <MathEditorPopover\n editor={editor}\n kind={mathEdit.kind}\n mode={mathEdit.mode}\n pos={mathEdit.pos}\n latex={mathEdit.latex}\n isNew={mathEdit.isNew}\n newLabel={\n mathEdit.kind === 'inline' ? t.mathNewInline : t.mathNewBlock\n }\n placeholder={t.mathPlaceholder}\n doneLabel={t.mathDone}\n onConfirm={applyMath}\n onCancel={cancelMath}\n />\n ) : null}\n\n {/* 表格悬停手柄(行左缘 ⋮ / 表格上方 ⋯)+ 它的操作菜单;\n 原来那套「表内右键出菜单」只剩多格选区的批量增删,由这个组件自己接管 */}\n <TableHandles editor={editor} labels={t} />\n {/* 图片对齐工具条(悬停图片时浮出;与表格手柄同一套就地交互) */}\n <ImageAlignTools editor={editor} labels={t} />\n </>\n );\n}\n","import type { Editor } from '@tiptap/core';\nimport { findImagePos } from './imageAlign';\n\n/**\n * 清掉「当前图片」的尺寸(width / height attrs),让它回到标准 `![alt](url)` 语法\n * (对齐若仍在,则回落到 `<img … data-align=\"…\">` 形式)。\n *\n * 拖拽缩放只有入口没有出口:拖过尺寸的图在 markdown 里是 `<img …>`,想在保存形态上\n * 反悔(比如拖歪了、想恢复原图全尺寸)时——undo 之外没有别的路。这个工具补上出口:\n * 宿主可以接自己的按钮 / 菜单(库内暂不渲染 UI)。\n *\n * @returns 是否找到并重置了图片。没找到图(光标不在图旁)时返回 false。\n */\nexport function resetImageSize(editor: Editor): boolean {\n const pos = findImagePos(editor);\n if (pos == null) return false;\n return editor\n .chain()\n .focus()\n .setNodeSelection(pos)\n .updateAttributes('image', { width: null, height: null })\n .run();\n}\n","'use client';\n\nimport * as Popover from '@radix-ui/react-popover';\nimport type { ReactNode } from 'react';\nimport styles from '../styles/comment.module.css';\n\nexport interface CommentPopoverProps {\n open: boolean;\n onOpenChange?: (open: boolean) => void;\n /** 被点击的 mark 元素(from onCommentClick 的 anchorEl)。 */\n anchorEl: HTMLElement | null;\n children: ReactNode;\n side?: 'top' | 'bottom' | 'left' | 'right';\n align?: 'start' | 'center' | 'end';\n sideOffset?: number;\n}\n\n/**\n * 评论浮层(Radix)。库只负责锚定与外观,内容由宿主通过 children 提供\n * (典型:评论正文 + Resolve/Dismiss/Reopen 操作)。\n * 锚点是点击 mark 的动态元素:渲染一个 fixed 1x1 span 挂在 anchorEl 的\n * getBoundingClientRect 位置,Radix 以它为锚。\n */\nexport function CommentPopover({\n open,\n onOpenChange,\n anchorEl,\n children,\n side = 'top',\n align = 'start',\n sideOffset = 6,\n}: CommentPopoverProps) {\n const rect = open && anchorEl ? anchorEl.getBoundingClientRect() : null;\n return (\n <Popover.Root open={open} onOpenChange={onOpenChange}>\n {rect && (\n <Popover.Anchor asChild>\n <span\n className={styles.popoverAnchor}\n style={{ left: rect.left, top: rect.top }}\n />\n </Popover.Anchor>\n )}\n <Popover.Portal>\n <Popover.Content\n className={styles.popoverContent}\n side={side}\n align={align}\n sideOffset={sideOffset}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n {children}\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n","import type { Editor } from '@tiptap/react';\nimport {\n mapCommentAnchors,\n type CommentAnchorStatus,\n type CommentSegment,\n} from './commentAnchor/commentMapper';\n\n/**\n * 内容锚点 → 当前文档里的区间。\n *\n * 用途:宿主在提交一次「圈选改写」时记下选区的内容锚点(`prefix` / `exact` / `suffix`,\n * `blockHash` 可选),等 AI 改完、推送回来时再用它把那段区间找回来——**不能用位置**,\n * 因为编辑器在这期间可能整体重建过(保存、切卡、SWR 刷新都会重挂),位置活不过重建。\n *\n * 锚点类型直接复用评论锚定那套(`commentMapper`),匹配走同一条 fallback 链:\n * `blockHash` → 前后文 → `exact` → 前缀降级(前 20 字)。**这条链是会降级的**,所以:\n *\n * > 调用方必须自己比对返回的 `text` 是否仍等于提交时那一段。本函数只回答「在哪」,\n * > 不回答「还算不算同一段」——后者决定了是原地回填还是弹冲突。\n */\nexport interface AnchorRange {\n /** doc 坐标 */\n from: number;\n to: number;\n /**\n * 区间当前的纯文本。**按匹配器的口径**给出:`commentMapper` 把区间内的 text 节点\n * 直接拼起来(块之间**没有分隔符**),所以这里也是 `textBetween(from, to)`(不传\n * blockSeparator)。调用方拿它跟「建锚点时用的那段文本」比对——两边必须同一口径,\n * 否则多块区间(表格 / 列表)会永远对不上。\n */\n text: string;\n status: CommentAnchorStatus;\n}\n\n/** 探针 id:借 commentMapper 的匹配链路,但我们只要区间,不要评论语义。 */\nconst PROBE_ID = '__anchor-range-probe__';\n\nexport function findRangeByAnchor(\n editor: Editor,\n anchor: CommentSegment | null | undefined,\n): AnchorRange | null {\n if (!anchor || !anchor.exact) return null;\n\n const [result] = mapCommentAnchors(editor.state.doc, [\n { commentId: PROBE_ID, segments: [anchor] },\n ]);\n const range = result?.ranges[0];\n if (!range) return null;\n\n return {\n from: range.from,\n to: range.to,\n // 无 blockSeparator:与 commentMapper 的 `collectText`(text 节点直接拼接)同一口径\n text: editor.state.doc.textBetween(range.from, range.to),\n status: result.status,\n };\n}\n","import '@tiptap/markdown';\nimport type { JSONContent } from '@tiptap/core';\nimport type { Editor } from '@tiptap/react';\n\n/**\n * 取区间对应的 markdown,并给出它在 `editor.getMarkdown()` 输出里的精确偏移。\n *\n * 为什么需要它:后端改卡走的是「在正文里逐字找到这段,替换」(`replace_in_text` +\n * `count=1`),所以发过去的那段必须是**存库正文的逐字子串**;调用方还要知道它在整篇里\n * 的偏移,才能从存库正文取前后文(`ref`)并界定「这次改了哪一段」。官方 OSS 只有整篇\n * `Editor.getMarkdown()`;官方 Pro 的 `getMarkdownRange` 只返回字符串、不给偏移,也不做\n * 块扩展。\n *\n * **默认扩块**(`expandToBlocks: true`):把区间扩展到与它相交的顶层块的首尾。一是序列化\n * 稳定(半句话里跨着加粗,片段序列化会在断口插入多余的 `**`),二是**只有块对齐时偏移才是\n * 算出来的,而不是猜出来的**。\n *\n * 偏移怎么来的:把 doc 的顶层块逐个序列化、用 `\\n\\n` 拼起来,等于整篇序列化(有测试钉住\n * 这条不变量),于是「前 i 块拼接的长度」就是第 i 块的起点偏移。这条不变量若在某篇文档上\n * 不成立,退回按内容在整篇里定位;定位不到或出现多处就返回 `null`——宁可让调用方知道\n * 拿不到偏移,也不要给一个错的。\n */\nexport interface MarkdownRange {\n /** 区间对应的 markdown 片段(扩块后)。 */\n markdown: string;\n /** 片段在 `editor.getMarkdown()` 输出里的起始偏移。 */\n from: number;\n /** 结束偏移(不含)。恒有 `to - from === markdown.length`。 */\n to: number;\n /**\n * **同一段**在 doc 坐标里的范围(扩块之后)。\n *\n * 宿主需要它把「读」和「写」对齐:`from/to` 是 markdown 偏移,而编辑器里的替换要按\n * doc 坐标做。少了它,宿主只能自己拿原始选区去定位,于是「用来定位的那段」和\n * 「按 markdown 偏移算出来的那段」不是同一段——把 A 段写进 B 区间,内容就会重复。\n */\n docFrom: number;\n docTo: number;\n}\n\nexport interface GetMarkdownForRangeOptions {\n /**\n * 是否把区间扩展到与它相交的顶层块(段落 / 表格 / 列表 / 图表 …)的首尾。\n * 默认 `true`:调用方要「存库正文的逐字子串 + 精确偏移」时应该保持默认。\n * 设 `false` 按原区间取,此时偏移依赖内容定位,可能返回 `null`。\n */\n expandToBlocks?: boolean;\n}\n\n/** 顶层块之间的分隔符——与 MarkdownManager 的 doc 级拼接一致。 */\nconst BLOCK_SEPARATOR = '\\n\\n';\n\ninterface TopLevelBlock {\n json: JSONContent;\n /** doc 坐标 */\n from: number;\n to: number;\n}\n\nfunction topLevelBlocks(editor: Editor): TopLevelBlock[] {\n const blocks: TopLevelBlock[] = [];\n editor.state.doc.forEach((node, offset) => {\n blocks.push({\n json: node.toJSON() as JSONContent,\n from: offset,\n to: offset + node.nodeSize,\n });\n });\n return blocks;\n}\n\nexport function getMarkdownForRange(\n editor: Editor,\n from: number,\n to: number,\n options: GetMarkdownForRangeOptions = {},\n): MarkdownRange | null {\n const manager = editor.markdown;\n if (!manager) return null;\n\n const size = editor.state.doc.content.size;\n let start = Math.max(0, Math.min(from, to));\n let end = Math.min(size, Math.max(from, to));\n if (start >= end) return null;\n\n const blocks = topLevelBlocks(editor);\n if (blocks.length === 0) return null;\n\n const expand = options.expandToBlocks !== false;\n\n // 与 [start, end) 相交的顶层块:第一个末端越过 start 的、到最后一个起点早于 end 的。\n // 单元格内、列表项内的选区都会扩到它们所属的顶层块(表格 / 列表整体)。\n let first = 0;\n let last = blocks.length - 1;\n if (expand) {\n first = blocks.findIndex((b) => b.to > start);\n if (first < 0) return null;\n for (let i = blocks.length - 1; i >= first; i -= 1) {\n if (blocks[i].from < end) {\n last = i;\n break;\n }\n }\n start = blocks[first].from;\n end = blocks[last].to;\n }\n\n const parts = blocks.map((b) => manager.serialize({ type: 'doc', content: [b.json] }));\n const full = editor.getMarkdown();\n\n if (expand && parts.join(BLOCK_SEPARATOR) === full) {\n const markdown = parts.slice(first, last + 1).join(BLOCK_SEPARATOR);\n // 空段落拼出来是空串:语义上等于「这段没内容」,交给调用方去判空不如这里就返回 null\n if (!markdown) return null;\n const at =\n first === 0\n ? 0\n : parts.slice(0, first).join(BLOCK_SEPARATOR).length + BLOCK_SEPARATOR.length;\n return { markdown, from: at, to: at + markdown.length, docFrom: start, docTo: end };\n }\n\n // 非块对齐,或整篇拼接与 getMarkdown() 不一致:只能按内容定位,且必须唯一\n const markdown = manager.serialize(\n editor.state.doc.cut(start, end).toJSON() as JSONContent,\n );\n if (!markdown) return null;\n const at = full.indexOf(markdown);\n if (at < 0 || full.indexOf(markdown, at + 1) >= 0) return null;\n return { markdown, from: at, to: at + markdown.length, docFrom: start, docTo: end };\n}\n","import { MarkdownManager } from '@tiptap/markdown';\nimport { CitationRef } from './CitationRef';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\nimport { baseExtensions, pureChart, pureCodeBlock, pureImage } from './extensions';\n\n/**\n * 把一段正文规范成「编辑器读进来再吐出去」的那种写法。\n *\n * **为什么需要它**:编辑器加载 markdown 时要先解析成文档结构,而 markdown 里有些东西是\n * 排版装饰、解析时就丢了(表格列宽的空格、注释 JSON 里的空格、数值尾零 …)。所以\n * 「存库正文」和「编辑器序列化结果」常常**逐字不同**——这与用户改没改无关,加载那一刻就\n * 发生了。而后端改卡走的是「在正文里逐字找到这段再替换」,两边写法不一致就会搜不到。\n * 宿主在要动正文之前(例如圈选改写提交前)先用本函数比对/对齐一次即可。\n *\n * **已知会被规范化的结构**(都是排版层面,内容语义不变):\n *\n * | 结构 | 存库可能是 | 规范化后 |\n * |------|-----------|---------|\n * | GFM 表格 | `\\| 收入 \\| 100 \\|` | 按列内容宽度重排对齐(多/少空格) |\n * | 图表注释 JSON | `<!-- {\"chartType\": \"column\"} -->` | `<!-- {\"chartType\":\"column\"} -->`(去空格,键序按解析结果) |\n * | **图表区**的数值 | `132.0` | `132`(图表把表解析成 payload,数值走 number 再写回) |\n * | 纯空白的正文 | `\" \\n \"` | `\"\"` |\n *\n * 注意第三行**只作用于图表区**:普通 GFM 表格走 marked 的表格解析,单元格是文本,\n * `1,234.50` 这种写法会原样保留(有测试钉住)。\n *\n * 反过来,这些**不该**被改动,同样有测试钉住:标题/段落/加粗斜体/列表/引用、代码块、\n * 公式、图片、脚注 `[^n]`、图表仍是「注释 + 表」的作者形态(不会变成 ```tmr-chart 围栏)。\n *\n * 注意它**不使用** `enrichMarkdownCitations`:脚注的 `[^n]` 由 CitationRef 自带的\n * tokenizer 解析,url/title 只存在于节点属性里、不进 markdown,所以规范化不需要 sources。\n */\nexport function normalizeMarkdown(markdown: string): string {\n if (!markdown) return markdown;\n const manager = getManager();\n return manager.serialize(manager.parse(prepareChartMarkdown(markdown)));\n}\n\n// [perf] 与 renderReportHtml 同样的理由:每次 new MarkdownManager 都会让底层 marked 全局\n// 累积 tokenizer,parse 逐次变慢。这里只做「解析 → 序列化」,无状态,模块级复用即可。\nlet sharedManager: MarkdownManager | null = null;\n\nfunction getManager(): MarkdownManager {\n sharedManager ??= new MarkdownManager({\n extensions: [...baseExtensions, pureCodeBlock, pureImage, pureChart, CitationRef],\n });\n return sharedManager;\n}\n","import '@tiptap/markdown';\nimport type { Editor } from '@tiptap/react';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\nimport { enrichMarkdownCitations, type SourceRef } from './citationUtils';\nimport { splitTopLevelBlocks, stripToPlainText } from './insertMarkdown';\n\nexport interface ReplaceRangeOptions {\n /** 脚注来源:把 `[^n]` enrich 成带 url/title 的 citation-ref(与 `insertMarkdown` 同一套)。 */\n sources?: SourceRef[];\n}\n\n/**\n * 用一段 markdown **替换**一个区间。给「后台回填」用——AI 在别处改完,把结果写回编辑器。\n *\n * 与 `insertMarkdown` 的三处刻意差异(都在注释里点明,改的时候别抹平):\n *\n * 1. **不抢焦点**。`insertMarkdown` 是「用户主动插入」,链式带了 `.focus()`;后台回填时\n * 用户可能正在别处打字,抢焦点等于打断他。所以这里直接调 `insertContentAt`。\n * 2. **按区间替换**,不是往光标处插(`insertContentAt` 收 `{from,to}` 时后者即替换)。\n * 3. **进 undo 历史**。`insertContentAt` 本身是一个 transaction,撤销一步就回去;\n * 与评论锚定的 `addToHistory: false` 相反——正文改动必须能撤。\n *\n * 解析失败(AI 的脏输出过不了 schema:嵌套 bold、块级图进 listItem …)时按块降级,\n * 与 `insertMarkdown` 同一套策略。注意降级路径是**逐块多次 transaction**,撤销要多按几下;\n * 只在整段失败时才走。\n *\n * @returns 是否改动了文档。空 markdown 返回 `false`(不静默删掉用户的内容)。\n */\nexport function replaceRangeWithMarkdown(\n editor: Editor,\n range: { from: number; to: number },\n markdown: string,\n options: ReplaceRangeOptions = {},\n): boolean {\n const prepared = prepareChartMarkdown(\n enrichMarkdownCitations(markdown ?? '', options.sources ?? []),\n );\n if (!prepared.trim()) return false;\n\n let from = Math.max(0, Math.min(range.from, range.to));\n let to = Math.max(range.from, range.to);\n if (from === to) return false;\n\n // ① 整段替换:一个 transaction,撤销一步到位\n if (tryInsertMarkdownAt(editor, from, to, prepared)) return true;\n\n // ② 降级:逐块替换。块插完要往后挪插入点,否则每块都往同一个区间插、只剩最后一块\n for (const block of splitTopLevelBlocks(prepared)) {\n const before = editor.state.doc.content.size;\n const changed =\n tryInsertMarkdownAt(editor, from, to, block) ||\n insertPlainParagraphAt(editor, from, to, stripToPlainText(block));\n if (!changed) continue;\n // 插入内容的 doc 尺寸 = 文档增量 + 被替换掉的宽度\n const inserted = editor.state.doc.content.size - before + (to - from);\n from += inserted;\n to = from;\n }\n return true;\n}\n\n/** 注意:不带 `.focus()`——后台回填不能抢用户的焦点。 */\nfunction tryInsertMarkdownAt(\n editor: Editor,\n from: number,\n to: number,\n markdown: string,\n): boolean {\n try {\n return editor\n .chain()\n .insertContentAt({ from, to }, markdown, { contentType: 'markdown' })\n .run();\n } catch {\n return false;\n }\n}\n\nfunction insertPlainParagraphAt(\n editor: Editor,\n from: number,\n to: number,\n text: string,\n): boolean {\n try {\n return editor\n .chain()\n .insertContentAt(\n { from, to },\n { type: 'paragraph', content: text ? [{ type: 'text', text }] : [] },\n )\n .run();\n } catch {\n return false;\n }\n}\n","import type { Editor, Range } from '@tiptap/core';\nimport { prepareChartMarkdown } from '../chart/prepareChartMarkdown';\nimport { insertMarkdown } from '../insertMarkdown';\n\n/**\n * 斜杠命令项插入 markdown 的共享原语:同一条 chain 内先删触发词再插内容,\n * 撤销是一步。整段过不了 schema 时降级为「按块重试 + 剥标记当段落」\n * (复用 insertMarkdown 的降级路径)——那条路径里删除已单独成事务,\n * 撤销会是多步,但注册表项是受控 markdown,正常不会走到。\n */\nexport function insertSlashMarkdown(\n editor: Editor,\n range: Range,\n markdown: string,\n): void {\n const prepared = prepareChartMarkdown(markdown);\n if (!prepared) return;\n try {\n const ok = editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent(prepared, { contentType: 'markdown' })\n .run();\n if (ok) return;\n } catch {\n // 整段过不了 schema:走降级\n }\n editor.chain().focus().deleteRange(range).run();\n insertMarkdown(editor, prepared);\n}\n"],"names":["m","offset","r","e","b","counter","toggle","styles","Node","dragActive","dragOver","progress","s","clearAll","input","item","isComposing","menu","cell","EmojiSuggestionPopup","_a","text","SlashMenuPopup","header","Fragment","group","MD_FILE_RE","f","anchor","setActive","readonly","folded","pre","list","a","apply","MarkdownWysiwygEditor","Selection","gutter","useIsoLayoutEffect","HOVER_KEEP_MS","remove","row","node","pos","field","ToolbarButton","bar","markdown","at"],"mappings":"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","x_google_ignoreList":[31,32,33]}
1
+ {"version":3,"file":"index.js","sources":["../src/components/CitationRefView.tsx","../src/createCitationRef.ts","../src/commentAnchor/commentAnchorPlugin.ts","../src/commentAnchor/commentAnchorController.ts","../src/findReplace.ts","../src/icons/index.tsx","../src/components/FindReplaceBar.tsx","../src/importPlaceholder.ts","../src/i18n/store.ts","../src/components/ImageUploadView.tsx","../src/imageUpload.ts","../src/imageAlign.ts","../src/emoji/emojiCategories.ts","../src/emoji/filterEmoji.ts","../src/emoji/EmojiSuggestion.tsx","../src/characterCount.ts","../src/slashMenu/items.ts","../src/slashMenu/SlashMenuPopup.tsx","../src/slashMenu/SlashMenuExtension.ts","../src/shortcuts/types.ts","../src/shortcuts/ShortcutPanel.tsx","../src/markdownFileDrop.ts","../src/pendingAnchor.ts","../src/selectionKind.ts","../src/markdownPaste.ts","../src/components/ChartView.tsx","../src/chart/createChart.ts","../src/components/CodeBlockView.tsx","../node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs","../node_modules/@floating-ui/core/dist/floating-ui.core.mjs","../node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs","../src/blockDrag/actions.ts","../src/blockDrag/listDrop.ts","../src/components/BlockDragHandle.tsx","../src/components/MarkdownWysiwygEditor.tsx","../src/components/MarkdownPreview.tsx","../src/importMenu.ts","../src/insertMarkdown.ts","../src/components/ColorPalette.tsx","../src/imageCaption.ts","../src/components/ImageAlignTools.tsx","../src/linkEditing.ts","../src/components/LinkPopover.tsx","../src/tableSelection.ts","../src/components/TableHandles.tsx","../src/components/MathEditorPopover.tsx","../src/components/EditorToolbar.tsx","../src/resetImageSize.ts","../src/commentAnchor/CommentPopover.tsx","../src/anchorRange.ts","../src/markdownRange.ts","../src/normalizeMarkdown.ts","../src/replaceRange.ts","../src/slashMenu/insertSlashMarkdown.ts"],"sourcesContent":["'use client';\n\nimport {\n NodeViewWrapper,\n type NodeViewProps,\n} from '@tiptap/react';\nimport type { RenderCitation } from '../citationTypes';\n\n/**\n * 脚注圆标 React NodeView。\n * 无 `renderCitation` 时渲默认 pill(有 url 则可点);有插槽时把 defaultDom 交给消费方。\n */\nexport function CitationRefView({ node, extension }: NodeViewProps) {\n const index = String(node.attrs.index ?? '');\n const url = (node.attrs.url as string | null) || null;\n const title = (node.attrs.title as string | null) || '';\n const renderCitation = extension.options.renderCitation as\n | RenderCitation\n | undefined;\n\n const pill = (\n <span\n className=\"citation-ref\"\n data-index={index}\n {...(url ? { 'data-url': url } : {})}\n {...(title ? { 'data-title': title } : {})}\n >\n {!renderCitation && url ? (\n <a\n href={url}\n title={title}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {index}\n </a>\n ) : (\n index\n )}\n </span>\n );\n\n const content = renderCitation\n ? renderCitation({\n index,\n attrs: { index, url, title: title || null },\n defaultDom: pill,\n })\n : pill;\n\n return (\n <NodeViewWrapper\n as=\"span\"\n data-citation-ref=\"\"\n style={{ display: 'inline' }}\n >\n {content}\n </NodeViewWrapper>\n );\n}\n","'use client';\n\nimport { ReactNodeViewRenderer } from '@tiptap/react';\nimport { CitationRef, type CitationRefOptions } from './CitationRef';\nimport type { RenderCitation } from './citationTypes';\nimport { CitationRefView } from './components/CitationRefView';\n\nexport type CitationRefViewOptions = CitationRefOptions & {\n renderCitation?: RenderCitation;\n};\n\n/**\n * 客户端增强版 CitationRef:挂 React NodeView,支持 `renderCitation` 插槽。\n * server / SSR 请继续用纯 {@link CitationRef}(无 React)。\n */\nexport function createCitationRef(\n options: { renderCitation?: RenderCitation } = {},\n) {\n return CitationRef.extend<CitationRefViewOptions>({\n addOptions() {\n return {\n HTMLAttributes: {},\n ...this.parent?.(),\n renderCitation: options.renderCitation,\n };\n },\n addNodeView() {\n return ReactNodeViewRenderer(CitationRefView);\n },\n }).configure({\n renderCitation: options.renderCitation,\n });\n}\n","/**\n * 评论锚定客户端插件:\n * - 装饰:block 左缘 gutter 气泡(showGutter)、active 评论的 mark/block 高亮;\n * - 交互:点击 mark / gutter 通过 transaction meta 上报(宿主经 editor.on('transaction')\n * 接收,规避扩展创建时闭包 stale 的问题);\n * - 守卫:粘贴 / 拖拽的 slice 剥离 commentAnchor mark,防止幽灵高亮。\n *\n * 只读态(MarkdownPreview)不注入本插件、不接评论 —— 评论是编辑会话专属。\n */\n\nimport {\n Fragment,\n Slice,\n type MarkType,\n type Node as PMNode,\n} from '@tiptap/pm/model';\nimport { Extension } from '@tiptap/core';\nimport {\n Plugin,\n PluginKey,\n type EditorState,\n} from '@tiptap/pm/state';\nimport {\n Decoration,\n DecorationSet,\n type EditorView,\n} from '@tiptap/pm/view';\nimport type { CommentClickPayload } from './commentTypes';\n\nexport const COMMENT_ACTIVE_META = 'tmr:comment:active';\nexport const COMMENT_CLICK_META = 'tmr:comment:click';\nexport const COMMENT_GUTTER_META = 'tmr:comment:gutter';\n\nexport interface CommentGutterPayload {\n commentIds: string[];\n pos: number;\n}\n\nexport interface CommentAnchorPluginOptions {\n /** 是否在 block 左缘渲染评论 gutter 气泡,默认 true。 */\n showGutter?: boolean;\n /**\n * 编辑器内点 mark 是否参与交互(默认 true:设 active + 上报 click meta)。\n * 设 false 后点击 mark 完全惰性(不设 active、不上报),适合「只靠侧栏单项\n * 驱动滚动定位」的宿主。\n */\n interactive?: boolean;\n}\n\nexport interface CommentAnchorPluginState {\n activeCommentId: string | null;\n}\n\nexport const commentAnchorPluginKey = new PluginKey<CommentAnchorPluginState>(\n 'tmrCommentAnchor',\n);\n\n/** 从 Slice 中剔除 commentAnchor mark(粘贴/拖拽守卫)。 */\nexport function stripCommentAnchorMarks(\n slice: Slice,\n markType: MarkType,\n): Slice {\n const strip = (fragment: Fragment): Fragment => {\n const children: PMNode[] = [];\n fragment.forEach((child) => {\n if (child.isText && child.marks.some((m) => m.type === markType)) {\n children.push(\n child.mark(child.marks.filter((m) => m.type !== markType)),\n );\n } else if (child.content.size > 0) {\n children.push(child.copy(strip(child.content)));\n } else {\n children.push(child);\n }\n });\n return Fragment.fromArray(children);\n };\n return new Slice(strip(slice.content), slice.openStart, slice.openEnd);\n}\n\nfunction sliceHasCommentMarks(slice: Slice, markType: MarkType): boolean {\n let found = false;\n slice.content.descendants((node) => {\n if (node.isText && node.marks.some((m) => m.type === markType)) {\n found = true;\n return false;\n }\n return true;\n });\n return found;\n}\n\n/** 顶层 block 边界。 */\nfunction blockBounds(doc: PMNode): Array<{ start: number; end: number }> {\n const bounds: Array<{ start: number; end: number }> = [];\n doc.forEach((child, offset) => {\n bounds.push({ start: offset, end: offset + child.nodeSize });\n });\n return bounds;\n}\n\n/** commentId → 文本区间(从 doc 内 commentAnchor mark 收集)。 */\nfunction collectCommentRanges(\n doc: PMNode,\n): Map<string, Array<{ from: number; to: number }>> {\n const byId = new Map<string, Array<{ from: number; to: number }>>();\n doc.descendants((node, pos) => {\n if (!node.isText) return true;\n for (const m of node.marks) {\n if (m.type.name !== 'commentAnchor') continue;\n const ids = (m.attrs.commentIds as string[]) ?? [];\n for (const id of ids) {\n const arr = byId.get(id) ?? [];\n arr.push({ from: pos, to: pos + node.nodeSize });\n byId.set(id, arr);\n }\n }\n return true;\n });\n return byId;\n}\n\nfunction rangesIntersectBlock(\n ranges: Array<{ from: number; to: number }>,\n start: number,\n end: number,\n): boolean {\n return ranges.some((r) => r.from < end && r.to > start);\n}\n\nfunction gutterDom(\n commentIds: string[],\n view: EditorView,\n blockPos: number,\n): HTMLElement {\n const el = document.createElement('span');\n el.className = 'tmr-comment-gutter';\n el.setAttribute('data-comment-ids', commentIds.join(' '));\n el.textContent = String(commentIds.length);\n el.title = `${commentIds.length}`;\n // 不抢编辑器焦点/选区\n el.addEventListener('mousedown', (e) => {\n e.preventDefault();\n e.stopPropagation();\n });\n el.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n view.dispatch(\n view.state.tr\n .setMeta(COMMENT_ACTIVE_META, commentIds[0] ?? null)\n .setMeta(COMMENT_GUTTER_META, {\n commentIds,\n pos: blockPos,\n } satisfies CommentGutterPayload),\n );\n });\n return el;\n}\n\nexport function commentAnchorPlugin(\n options: CommentAnchorPluginOptions = {},\n): Plugin<CommentAnchorPluginState> {\n const showGutter = options.showGutter !== false;\n const interactive = options.interactive !== false;\n\n return new Plugin<CommentAnchorPluginState>({\n key: commentAnchorPluginKey,\n state: {\n init: () => ({ activeCommentId: null }),\n apply: (tr, value) => {\n const active = tr.getMeta(COMMENT_ACTIVE_META);\n return active !== undefined\n ? { activeCommentId: active as string | null }\n : value;\n },\n },\n props: {\n decorations(state: EditorState) {\n const byId = collectCommentRanges(state.doc);\n if (byId.size === 0) return DecorationSet.empty;\n\n const decos: Decoration[] = [];\n const bounds = blockBounds(state.doc);\n\n if (showGutter) {\n for (const b of bounds) {\n const idsInBlock: string[] = [];\n for (const [id, ranges] of byId) {\n if (rangesIntersectBlock(ranges, b.start, b.end)) {\n idsInBlock.push(id);\n }\n }\n if (idsInBlock.length > 0) {\n decos.push(\n Decoration.widget(\n b.start,\n (view) => gutterDom(idsInBlock, view, b.start),\n { side: -1, key: `tmr-gutter:${b.start}` },\n ),\n );\n }\n }\n }\n\n const activeId =\n commentAnchorPluginKey.getState(state)?.activeCommentId ?? null;\n if (activeId) {\n const ranges = byId.get(activeId) ?? [];\n for (const r of ranges) {\n decos.push(\n Decoration.inline(r.from, r.to, { class: 'tmr-comment-active' }),\n );\n }\n for (const b of bounds) {\n if (rangesIntersectBlock(ranges, b.start, b.end)) {\n decos.push(\n Decoration.node(b.start, b.end, {\n class: 'tmr-comment-block-active',\n }),\n );\n }\n }\n }\n\n return DecorationSet.create(state.doc, decos);\n },\n\n handleClick(view: EditorView, pos: number, event: MouseEvent) {\n // 惰性模式:编辑器内点击 mark 不产生任何交互(侧栏单项驱动)。\n if (!interactive) return false;\n const target = event.target as Element | null;\n const el = target?.closest?.(\n 'mark.tmr-comment[data-comment-ids]',\n ) as HTMLElement | null;\n if (!el) return false;\n const commentIds = (el.getAttribute('data-comment-ids') ?? '')\n .split(' ')\n .filter(Boolean);\n if (commentIds.length === 0) return false;\n view.dispatch(\n view.state.tr\n .setMeta(COMMENT_ACTIVE_META, commentIds[0] ?? null)\n .setMeta(COMMENT_CLICK_META, {\n commentIds,\n anchorEl: el,\n pos,\n } satisfies CommentClickPayload),\n );\n return true;\n },\n\n // 粘贴 / 拖拽 / 编辑器内拖动的 slice 统一走这里:剥离评论 mark,\n // 防止复制粘贴产生幽灵高亮。正文内容不变,只是丢掉会话级元数据。\n transformPasted(slice: Slice, view: EditorView) {\n const markType = view.state.schema.marks.commentAnchor;\n if (!markType || !sliceHasCommentMarks(slice, markType)) return slice;\n return stripCommentAnchorMarks(slice, markType);\n },\n },\n });\n}\n\n/**\n * Tiptap Extension 包装:把评论锚定 Plugin 挂进 useEditor / Editor 的 extensions。\n * 只读态(MarkdownPreview)不要注入本扩展 —— 评论是编辑会话专属。\n */\nexport function commentAnchorExtension(\n options: CommentAnchorPluginOptions = {},\n) {\n return Extension.create({\n name: 'tmrCommentAnchor',\n addProseMirrorPlugins() {\n return [commentAnchorPlugin(options)];\n },\n });\n}\n","/**\n * 编辑会话内的评论锚定控制器(纯函数,供宿主 / 组件 handle 复用)。\n *\n * 职责:\n * - applyCommentAnchorsToEditor:comments 装载时把 segments 映射成 mark 铺入 doc;\n * - focusComment / nextComment:active 状态 + 滚动 + 选区;\n * - collectCommentIds / commentRangesById:供宿主查询。\n */\n\nimport type { Editor } from '@tiptap/core';\nimport {\n COMMENT_ACTIVE_META,\n commentAnchorPluginKey,\n} from './commentAnchorPlugin';\nimport {\n mapCommentAnchors,\n mergeCommentIntervals,\n type CommentRange,\n} from './commentMapper';\nimport type { CommentRef } from './commentTypes';\n\n/** 先清后铺,幂等;无评论时清空全部 mark。 */\nexport function applyCommentAnchorsToEditor(\n editor: Editor,\n comments: CommentRef[],\n): void {\n // 只读态不接评论(库契约:评论锚定 = 编辑会话专属)。\n if (!editor.isEditable) return;\n const intervals =\n comments.length > 0\n ? mergeCommentIntervals(\n mapCommentAnchors(\n editor.state.doc,\n comments.map((c) => ({ commentId: c.commentId, segments: c.segments })),\n ).map((r) => ({ commentId: r.commentId, ranges: r.ranges })),\n )\n : [];\n const chain = editor.chain().clearCommentAnchors();\n if (intervals.length > 0) chain.applyCommentAnchors(intervals);\n chain.run();\n}\n\n/** 文档顺序去重的 commentId 列表(gutter / 导航顺序)。 */\nexport function collectCommentIds(editor: Editor): string[] {\n const ids: string[] = [];\n editor.state.doc.descendants((node) => {\n if (!node.isText) return true;\n for (const m of node.marks) {\n if (m.type.name !== 'commentAnchor') continue;\n for (const id of (m.attrs.commentIds as string[]) ?? []) {\n if (!ids.includes(id)) ids.push(id);\n }\n }\n return true;\n });\n return ids;\n}\n\nexport function commentRangesById(\n editor: Editor,\n commentId: string,\n): CommentRange[] {\n const ranges: CommentRange[] = [];\n editor.state.doc.descendants((node, pos) => {\n if (!node.isText) return true;\n for (const m of node.marks) {\n if (m.type.name !== 'commentAnchor') continue;\n const ids = (m.attrs.commentIds as string[]) ?? [];\n if (ids.includes(commentId)) {\n ranges.push({ from: pos, to: pos + node.nodeSize });\n }\n }\n return true;\n });\n return ranges;\n}\n\nfunction setActive(editor: Editor, commentId: string | null): void {\n editor.view.dispatch(\n editor.state.tr.setMeta(COMMENT_ACTIVE_META, commentId),\n );\n}\n\nfunction getActive(editor: Editor): string | null {\n return commentAnchorPluginKey.getState(editor.state)?.activeCommentId ?? null;\n}\n\n/**\n * 聚焦某条评论:设置 active(mark/block 高亮)+ 滚动到第一个 mark + 光标落到\n * 区间起点。找不到(outdated)时清空 active 并返回 false。\n */\nexport function focusComment(editor: Editor, commentId: string): boolean {\n const ranges = commentRangesById(editor, commentId);\n if (ranges.length === 0) {\n setActive(editor, null);\n return false;\n }\n setActive(editor, commentId);\n const markEl = editor.view.dom.querySelector<HTMLElement>(\n `mark.tmr-comment[data-comment-ids~=\"${CSS.escape(commentId)}\"]`,\n );\n markEl?.scrollIntoView?.({ block: 'center', behavior: 'smooth' });\n editor.chain().setTextSelection(ranges[0].from).run();\n return true;\n}\n\n/** 按文档顺序在评论间循环跳转(dir: next / prev)。 */\nexport function nextComment(\n editor: Editor,\n dir: 'next' | 'prev' = 'next',\n): string | null {\n const ids = collectCommentIds(editor);\n if (ids.length === 0) return null;\n const active = getActive(editor);\n const current = active ? ids.indexOf(active) : -1;\n const nextIdx =\n dir === 'next'\n ? (current + 1) % ids.length\n : (current - 1 + ids.length) % ids.length;\n const target = ids[nextIdx];\n focusComment(editor, target);\n return target;\n}\n","/**\n * 查找替换的适配层。\n *\n * 匹配 / 高亮 / 替换全部由官方 `@tiptap/extension-find-and-replace` 承担(命令 + storage +\n * decoration),宿主与 AI 侧可以直接用 `editor.commands.setSearchTerm / replaceAll …`。\n * 这个文件只收拾 Tiptap 3.31.3 上的一个坑,以及库内的默认防抖。\n */\n\nimport type { Editor } from '@tiptap/react';\n\n/** 官方扩展在 `extensionManager` 里的注册名(与官方 UI Components 同款判据)。 */\nexport const FIND_EXTENSION_NAME = 'findAndReplace';\n\n/**\n * 输入法组合中的这次按键要不要放行。\n *\n * 中文 / 日文输入法里 Enter 是「提交候选词」、Escape 是「取消候选」,都不是「执行」:不拦的话,\n * 每次确认候选词都会触发一次跳转(替换框则直接替换一处),而按 Esc 想取消候选反而把浮动条关掉。\n * 判据取两路——`isComposing` 覆盖常规路径,`keyCode === 229` 兜住 Safari 在组合收尾时的 keydown\n * (与 slashMenu/SlashMenuExtension.ts 的 isComposing() 同一套;那边还多一路 ProseMirror 的\n * `view.composing`,React 事件里拿不到,调用方传 `event.nativeEvent` 即可)。\n */\nexport function isComposingKeyEvent(\n event: Pick<KeyboardEvent, 'isComposing' | 'keyCode'>,\n): boolean {\n return event.isComposing || event.keyCode === 229;\n}\n\n/**\n * 查找替换扩展是否已注册。\n *\n * 按 `extensionManager.extensions` 里的注册名查,而不是探 `editor.commands.setSearchTerm`\n * 在不在——命令探测分不清「扩展根本没装」和「命令被别的同名扩展覆盖」,也判不出已销毁的实例。\n * 官方 UI Components 的 `isFindAndReplaceAvailable` 用的是同一条判据。\n */\nexport function hasFindReplaceExtension(\n editor: Editor | null | undefined,\n): boolean {\n if (!editor || editor.isDestroyed) return false;\n return (\n editor.extensionManager?.extensions.some(\n (extension) => extension.name === FIND_EXTENSION_NAME,\n ) ?? false\n );\n}\n\n/**\n * 输入防抖(毫秒)。\n *\n * 注意:注册扩展时必须把官方选项 `searchDebounceMs` 设为 **0**。官方防抖走 `setTimeout`,\n * 真正的 dispatch 发生在异步回调里,下面 `runFindCommand` 的守卫就兜不住那次抛错。\n * 防抖改由调用方(浮动条)自己做,语义一样但全程同步。\n */\nexport const FIND_DEBOUNCE_MS = 120;\n\nconst MISMATCH_RE = /Applying a mismatched transaction/;\n\n/**\n * 跑一条官方查找替换命令,兜掉 Tiptap 3.31.3 的一处事务错配。\n *\n * 成因:官方扩展的 `setSearchTerm` 不走 CommandManager 给的 dispatch,而是自己\n * `editor.state.tr` + `view.dispatch`(见扩展 dist 的 `applySearchTerm`)。CommandManager\n * 在命令结束后仍会派发它「命令开始时创建」的那个 tr。若这次派发触发了 TrailingNode 的\n * appendTransaction(**文末是代码块 / 表格 / 图表**时它会补一个空段落,starter-kit 自带),\n * doc 已经前进,那个旧 tr 的 `before` 就对不上当前 doc → `RangeError: Applying a mismatched\n * transaction`。\n *\n * 此时查询其实已经生效(扩展自己那次 dispatch 成功了、尾段落也补上了),只是紧随其后的\n * 第二次派发炸掉。所以捕获这一种错并重试一次即可收敛——重试时文末空段落已在,TrailingNode\n * 不再插手。其它错误照抛,不吞。\n */\nexport function runFindCommand(run: () => void): void {\n try {\n run();\n } catch (error) {\n if (error instanceof RangeError && MISMATCH_RE.test(error.message)) {\n run();\n return;\n }\n throw error;\n }\n}\n","import type { SVGProps } from 'react';\n\n/**\n * 内联 SVG 图标集(0 运行时依赖)。stroke 走 currentColor,尺寸默认 1em 跟随字号,\n * 故颜色/大小随文本与主题自动变化。\n *\n * 通用图标(粗体/斜体/下划线/列表/撤销/查找等,本文件表格图标之前的部分)的 path 取自\n * Lucide,其许可证为 ISC,其中部分图形源自 Feather 的 MIT——完整版权声明见\n * 仓库根目录 THIRD_PARTY_LICENSES.md。表格行列操作图标(Column / Row / TableDelete 系列)为自绘。\n */\nexport interface IconProps extends SVGProps<SVGSVGElement> {\n size?: number | string;\n}\n\nfunction Svg({ size = '1em', children, ...rest }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n {...rest}\n >\n {children}\n </svg>\n );\n}\n\nexport const BoldIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" />\n <path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" />\n </Svg>\n);\n\nexport const ItalicIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"19\" x2=\"10\" y1=\"4\" y2=\"4\" />\n <line x1=\"14\" x2=\"5\" y1=\"20\" y2=\"20\" />\n <line x1=\"15\" x2=\"9\" y1=\"4\" y2=\"20\" />\n </Svg>\n);\n\nexport const UnderlineIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M6 4v6a6 6 0 0 0 12 0V4\" />\n <line x1=\"4\" x2=\"20\" y1=\"20\" y2=\"20\" />\n </Svg>\n);\n\nexport const StrikethroughIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M16 4H9a3 3 0 0 0-2.83 4\" />\n <path d=\"M14 12a4 4 0 0 1 0 8H6\" />\n <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\n </Svg>\n);\n\nexport const CodeIcon = (p: IconProps) => (\n <Svg {...p}>\n <polyline points=\"16 18 22 12 16 6\" />\n <polyline points=\"8 6 2 12 8 18\" />\n </Svg>\n);\n\nexport const LinkIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\" />\n <path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\" />\n </Svg>\n);\n\n/** 橡皮擦:表格菜单的「清空内容」。 */\nexport const EraserIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21\" />\n <path d=\"M22 21H7\" />\n <path d=\"m5 11 9 9\" />\n </Svg>\n);\n\n/** 逆时针回转:表格菜单的「重置单元格样式」。 */\nexport const RotateCcwIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </Svg>\n);\n\n/** 在新窗口打开(链接浮层的「打开」按钮)。 */\nexport const ExternalLinkIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M15 3h6v6\" />\n <path d=\"M10 14 21 3\" />\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n </Svg>\n);\n\nexport const ImageIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\" />\n </Svg>\n);\n\n/**\n * 段落排版图标(对齐四件 / 缩进两件 / 行高):块状 fill 风格,自绘。\n * 细线条(stroke 2)在 14px 下会发虚——这组统一用带圆角的实心块。\n */\nconst BlockSvg = ({ size = '1em', children, ...rest }: IconProps & { children: React.ReactNode }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n stroke=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n {...rest}\n >\n {children}\n </svg>\n);\n\nexport const AlignLeftIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"8.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"19\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const AlignCenterIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"6\" y=\"8.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"6\" y=\"19\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const AlignRightIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"9\" y=\"8.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"9\" y=\"19\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const AlignJustifyIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"3\" y=\"3.4\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"8.6\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"13.8\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"3\" y=\"19\" width=\"18\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const IndentIncreaseIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <path d=\"M3 6.6 8.5 10.8 3 15 Z\" />\n <rect x=\"10.5\" y=\"3.6\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"10.8\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"18\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const IndentDecreaseIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <path d=\"M8.5 6.6 3 10.8 8.5 15 Z\" />\n <rect x=\"10.5\" y=\"3.6\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"10.8\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"10.5\" y=\"18\" width=\"10.5\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\n/** 行高:左侧轴 + 双向块三角,右侧两条块线。 */\nexport const LineHeightIcon = (p: IconProps) => (\n <BlockSvg {...p}>\n <rect x=\"1.8\" y=\"4.4\" width=\"2.4\" height=\"15.2\" rx=\"1.2\" />\n <path d=\"M3 2.4 5.4 5.8 H0.6 Z\" />\n <path d=\"M3 21.6 0.6 18.2 H5.4 Z\" />\n <rect x=\"9\" y=\"5.6\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n <rect x=\"9\" y=\"16\" width=\"12\" height=\"2.4\" rx=\"1.2\" />\n </BlockSvg>\n);\n\nexport const CaptionsIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"18\" height=\"14\" x=\"3\" y=\"5\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 15h4\" />\n <path d=\"M15 15h2\" />\n <path d=\"M7 11h2\" />\n <path d=\"M13 11h4\" />\n </Svg>\n);\n\nexport const ImportIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M12 3v12\" />\n <path d=\"m8 11 4 4 4-4\" />\n <path d=\"M8 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4\" />\n </Svg>\n);\n\nexport const ListUnorderedIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"8\" x2=\"21\" y1=\"6\" y2=\"6\" />\n <line x1=\"8\" x2=\"21\" y1=\"12\" y2=\"12\" />\n <line x1=\"8\" x2=\"21\" y1=\"18\" y2=\"18\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"6\" y2=\"6\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"12\" y2=\"12\" />\n <line x1=\"3\" x2=\"3.01\" y1=\"18\" y2=\"18\" />\n </Svg>\n);\n\nexport const ListOrderedIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"10\" x2=\"21\" y1=\"6\" y2=\"6\" />\n <line x1=\"10\" x2=\"21\" y1=\"12\" y2=\"12\" />\n <line x1=\"10\" x2=\"21\" y1=\"18\" y2=\"18\" />\n <path d=\"M4 6h1v4\" />\n <path d=\"M4 10h2\" />\n <path d=\"M6 18H4c0-1 2-2 2-3s-1-1.5-2-1\" />\n </Svg>\n);\n\n/** 折叠块。自绘,不是 Lucide。 */\nexport const DetailsIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m9 7 4 5-4 5\" />\n <path d=\"M15 7h5\" />\n <path d=\"M15 12h5\" />\n <path d=\"M15 17h5\" />\n </Svg>\n);\n\nexport const ListChecksIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m3 17 2 2 4-4\" />\n <path d=\"m3 7 2 2 4-4\" />\n <path d=\"M13 6h8\" />\n <path d=\"M13 12h8\" />\n <path d=\"M13 18h8\" />\n </Svg>\n);\n\nexport const UndoIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M9 14 4 9l5-5\" />\n <path d=\"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5 5.5 5.5 0 0 1-5.5 5.5H11\" />\n </Svg>\n);\n\nexport const RedoIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m15 14 5-5-5-5\" />\n <path d=\"M20 9H9.5A5.5 5.5 0 0 0 4 14.5 5.5 5.5 0 0 0 9.5 20H13\" />\n </Svg>\n);\n\nexport const ChevronDownIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m6 9 6 6 6-6\" />\n </Svg>\n);\n\nexport const CheckIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M20 6 9 17l-5-5\" />\n </Svg>\n);\n\nexport const TrashIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" />\n <path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\" />\n <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\" />\n </Svg>\n);\n\nexport const LockIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </Svg>\n);\n\nexport const MenuIcon = (p: IconProps) => (\n <Svg {...p}>\n <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\n <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" />\n <line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\n </Svg>\n);\n\nexport const SearchIcon = (p: IconProps) => (\n <Svg {...p}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </Svg>\n);\n\nexport const XIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </Svg>\n);\n\nexport const ChevronUpIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m18 15-6-6-6 6\" />\n </Svg>\n);\n\nexport const ReplaceIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M14 4a1 1 0 0 1 1-1\" />\n <path d=\"M15 10a1 1 0 0 1-1-1\" />\n <path d=\"M21 4a1 1 0 0 0-1-1\" />\n <path d=\"M21 9a1 1 0 0 1-1 1\" />\n <path d=\"m3 7 3 3 3-3\" />\n <path d=\"M6 10V5a2 2 0 0 1 2-2h2\" />\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\" />\n </Svg>\n);\n\nexport const ReplaceAllIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M14 14a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1\" />\n <path d=\"M14 4a1 1 0 0 1 1-1\" />\n <path d=\"M15 10a1 1 0 0 1-1-1\" />\n <path d=\"M19 14a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1\" />\n <path d=\"M21 4a1 1 0 0 0-1-1\" />\n <path d=\"M21 9a1 1 0 0 1-1 1\" />\n <path d=\"m3 7 3 3 3-3\" />\n <path d=\"M6 10V5a2 2 0 0 1 2-2h2\" />\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1\" />\n </Svg>\n);\n\n/* ── 表格行列操作图标(自绘:表格轮廓 + 目标边高亮 + 加号/叉号)── */\n\nexport const ColumnBeforeIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"10\" y=\"4\" width=\"11\" height=\"16\" rx=\"1\" />\n <path d=\"M14 4v16\" />\n <path d=\"M5 9v6\" />\n <path d=\"M2 12h6\" />\n </Svg>\n);\n\nexport const ColumnAfterIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"3\" y=\"4\" width=\"11\" height=\"16\" rx=\"1\" />\n <path d=\"M10 4v16\" />\n <path d=\"M19 9v6\" />\n <path d=\"M16 12h6\" />\n </Svg>\n);\n\nexport const ColumnDeleteIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"1\" />\n <path d=\"M12 4v16\" />\n <path d=\"m8 9 8 6\" />\n <path d=\"m16 9-8 6\" />\n </Svg>\n);\n\nexport const RowBeforeIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"10\" width=\"16\" height=\"11\" rx=\"1\" />\n <path d=\"M4 14h16\" />\n <path d=\"M9 5h6\" />\n <path d=\"M12 2v6\" />\n </Svg>\n);\n\nexport const RowAfterIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"3\" width=\"16\" height=\"11\" rx=\"1\" />\n <path d=\"M4 10h16\" />\n <path d=\"M9 19h6\" />\n <path d=\"M12 16v6\" />\n </Svg>\n);\n\nexport const RowDeleteIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"4\" y=\"4\" width=\"16\" height=\"16\" rx=\"1\" />\n <path d=\"M4 12h16\" />\n <path d=\"m9 8 6 8\" />\n <path d=\"m15 8-6 8\" />\n </Svg>\n);\n\nexport const TableDeleteIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"1\" />\n <path d=\"M3 9h18\" />\n <path d=\"M9 5v14\" />\n <path d=\"M15 5v14\" />\n <path d=\"m7 2 10 20\" />\n </Svg>\n);\n\nexport const CopyIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </Svg>\n);\n\nexport const ChevronsUpDownIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m7 15 5 5 5-5\" />\n <path d=\"m7 9 5-5 5 5\" />\n </Svg>\n);\n\n/** 六点握把:块拖拽手柄。 */\nexport const GripIcon = (p: IconProps) => (\n <Svg {...p} fill=\"currentColor\" strokeWidth={0}>\n <circle cx=\"9\" cy=\"6\" r=\"1.25\" />\n <circle cx=\"15\" cy=\"6\" r=\"1.25\" />\n <circle cx=\"9\" cy=\"12\" r=\"1.25\" />\n <circle cx=\"15\" cy=\"12\" r=\"1.25\" />\n <circle cx=\"9\" cy=\"18\" r=\"1.25\" />\n <circle cx=\"15\" cy=\"18\" r=\"1.25\" />\n </Svg>\n);\n\nexport const DownloadIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\" />\n </Svg>\n);\n\nexport const KeyboardIcon = (p: IconProps) => (\n <Svg {...p}>\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"M6 8h.01\" />\n <path d=\"M10 8h.01\" />\n <path d=\"M14 8h.01\" />\n <path d=\"M18 8h.01\" />\n <path d=\"M6 12h.01\" />\n <path d=\"M10 12h.01\" />\n <path d=\"M14 12h.01\" />\n <path d=\"M18 12h.01\" />\n <path d=\"M7 16h10\" />\n </Svg>\n);","'use client';\n\nimport {\n createSearchRegex,\n FindAndReplacePluginKey,\n} from '@tiptap/extension-find-and-replace';\nimport { useEditorState, type Editor } from '@tiptap/react';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type RefObject,\n} from 'react';\nimport {\n FIND_DEBOUNCE_MS,\n hasFindReplaceExtension,\n isComposingKeyEvent,\n runFindCommand,\n} from '../findReplace';\nimport {\n ChevronDownIcon,\n ChevronUpIcon,\n ReplaceAllIcon,\n ReplaceIcon,\n SearchIcon,\n XIcon,\n} from '../icons';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport styles from '../styles/findBar.module.css';\n\nexport interface FindReplaceBarProps {\n /** 编辑器实例(由 `<MarkdownWysiwygEditor>` 提供,浮动条不自行创建编辑器)。 */\n editor: Editor;\n /** 关闭(Esc / ×):宿主负责收起浮动条并把焦点还给编辑器。 */\n onClose: () => void;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n /**\n * 查找输入框 ref。宿主拿它实现「再按一次 Mod+F 聚焦并全选当前查询」。\n * 也用于浮动条打开时自动聚焦。\n */\n inputRef?: RefObject<HTMLInputElement | null>;\n /** 定位 / 层级 class,由宿主给(浮动条自身只负责内部布局)。 */\n className?: string;\n /** 根节点内联样式(编辑器用它落 findBarOffset;宿主自绘时也可用)。 */\n style?: CSSProperties;\n}\n\n/**\n * 查找替换浮动条。匹配、高亮、替换全部由官方\n * `@tiptap/extension-find-and-replace` 承担(含 RE2 安全正则、跨标记匹配、单事务全部替换),\n * 这里只做输入、计数、开关与键位。\n *\n * 打开即把查询推给编辑器,关闭时 `clearSearch()`——高亮是会话级的,不留残影。\n *\n * 给宿主的稳定选择器(内部 class 是 CSS Modules 哈希过的,宿主够不着):根节点\n * `data-find-bar`、两个输入框 `data-find-field=\"search|replace\"`、计数 `data-find-counter`、\n * 两个开关 `data-find-option=\"match-case|whole-words\"`、按钮\n * `data-find-action=\"previous|next|close|replace|replace-all\"`。\n */\n\n/** 「扩展没注册」的警告只发一次(模块级):同页多实例、或条子被反复开关时,重复同一句没有信息量。 */\nlet warnedMissingExtension = false;\nfunction warnMissingExtensionOnce(): void {\n if (warnedMissingExtension) return;\n warnedMissingExtension = true;\n console.warn(\n '[tiptap-markdown-react] FindReplaceBar 需要官方 find 扩展:' +\n '请让 <MarkdownWysiwygEditor findReplace> 保持开启(默认 true),' +\n '否则 setSearchTerm / replaceAll 等命令不存在。',\n );\n}\n\nexport function FindReplaceBar({\n editor,\n onClose,\n locale = 'en',\n inputRef,\n className,\n style,\n}: FindReplaceBarProps) {\n const t = getLocaleBundle(locale).find;\n const [term, setTerm] = useState('');\n const [replaceTerm, setReplaceTerm] = useState('');\n\n // 宿主自己摆条子时可能把 findReplace 关了——那时扩展没注册,命令与插件 state 都不存在。\n // 不拦住的话下面每个 effect 都会 TypeError 把宿主的树带崩;这里退化为不渲染并留一句提示。\n const hasExtension = hasFindReplaceExtension(editor);\n useEffect(() => {\n if (!hasExtension) warnMissingExtensionOnce();\n }, [hasExtension]);\n\n const state = useEditorState({\n editor,\n selector: ({ editor: instance }) => {\n const plugin = FindAndReplacePluginKey.getState(instance.state);\n return {\n currentIndex: plugin?.currentIndex ?? null,\n total: plugin?.results.length ?? 0,\n caseSensitive: plugin?.caseSensitive ?? false,\n wholeWord: plugin?.wholeWord ?? false,\n useRegex: plugin?.useRegex ?? false,\n };\n },\n });\n\n /** 所有命令都从这里走:扩展缺席时直接跳过(见上面 hasExtension)。 */\n const runCommand = useCallback(\n (run: () => void) => {\n if (!hasExtension) return;\n runFindCommand(run);\n },\n [hasExtension],\n );\n\n // 查询词防抖由这里做(扩展注册时 searchDebounceMs: 0,原因见 findReplace.ts):\n // 输入时延迟推给编辑器,回车 / 替换等动作前先 flush,否则会拿着旧查询执行。\n const appliedTermRef = useRef<string | null>(null);\n const pushTerm = useCallback(\n (value: string) => {\n appliedTermRef.current = value;\n runCommand(() => editor.commands.setSearchTerm(value));\n },\n [editor, runCommand],\n );\n const flushTerm = useCallback(() => {\n if (appliedTermRef.current !== term) pushTerm(term);\n }, [pushTerm, term]);\n\n useEffect(() => {\n if (appliedTermRef.current === term) return;\n const timer = setTimeout(() => pushTerm(term), FIND_DEBOUNCE_MS);\n return () => clearTimeout(timer);\n }, [term, pushTerm]);\n\n useEffect(() => {\n runCommand(() => editor.commands.setReplaceTerm(replaceTerm));\n }, [editor, replaceTerm, runCommand]);\n\n // 关闭 / 卸载时清空查询与高亮。\n useEffect(() => {\n return () => {\n if (!editor.isDestroyed) runCommand(() => editor.commands.clearSearch());\n };\n }, [editor, runCommand]);\n\n /** 动作类命令(导航 / 替换):先落定查询词,再执行。 */\n const runAction = useCallback(\n (action: () => void) => {\n flushTerm();\n runCommand(action);\n },\n [flushTerm, runCommand],\n );\n\n // RE2 模式非法(lookaround / backreference 等):官方返回零结果不报错,这里给个提示,\n // 否则「输入了正则却一直 0 / 0」没有解释。\n const invalidPattern = useMemo(\n () =>\n term !== '' &&\n state.useRegex &&\n createSearchRegex(term, {\n caseSensitive: state.caseSensitive,\n useRegex: true,\n wholeWord: false,\n }) === null,\n [term, state.useRegex, state.caseSensitive],\n );\n\n // currentIndex 为 null = 有结果、但还没定位到某一条(官方 `formatResultCount` 同款语义:\n // 那一条分支返回 `0 / total`)。不这样判的话第 1 条会被冒充成「当前」。\n const current =\n state.total === 0 || state.currentIndex === null\n ? 0\n : state.currentIndex + 1;\n const counter = t.counter(current, state.total);\n\n const keepFocus = (e: ReactMouseEvent) => e.preventDefault();\n\n const onFindKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (isComposingKeyEvent(e.nativeEvent)) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n runAction(() => {\n if (e.shiftKey) editor.commands.goToPreviousResult();\n else editor.commands.goToNextResult();\n });\n }\n };\n\n const onReplaceKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (isComposingKeyEvent(e.nativeEvent)) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n runAction(() => editor.commands.replace());\n }\n };\n\n const toggle = (key: 'caseSensitive' | 'wholeWord', value: boolean) => {\n runCommand(() => {\n if (key === 'caseSensitive') editor.commands.setCaseSensitive(value);\n else editor.commands.setWholeWord(value);\n });\n };\n\n if (!hasExtension) return null;\n\n const toggleClass = (on: boolean) =>\n `${styles.btn} ${styles.toggle}${on ? ` ${styles.toggleOn}` : ''}`;\n\n return (\n <div\n className={className ? `${styles.bar} ${className}` : styles.bar}\n style={style}\n data-find-bar=\"\"\n role=\"dialog\"\n aria-label={t.find}\n onKeyDown={(e) => {\n // 输入法组合中的 Esc 是「取消候选词」,放行。其余吃到 Esc 就自己收掉并**消费掉**这次\n // 按键(stopPropagation)——宿主按文档把条子放进自己的弹窗 / 抽屉时,那一层的 Esc\n // 处理不该被同一次按键带着一起关。\n if (isComposingKeyEvent(e.nativeEvent)) return;\n if (e.key !== 'Escape') return;\n e.preventDefault();\n e.stopPropagation();\n onClose();\n }}\n >\n <div className={styles.row}>\n <span className={styles.field}>\n <SearchIcon size={14} className={styles.fieldIcon} />\n <input\n ref={inputRef}\n className={styles.input}\n data-find-field=\"search\"\n value={term}\n onChange={(e) => setTerm(e.target.value)}\n onKeyDown={onFindKeyDown}\n placeholder={t.find}\n aria-label={t.find}\n autoFocus\n spellCheck={false}\n />\n <span\n className={`${styles.counter}${invalidPattern ? ` ${styles.counterInvalid}` : ''}`}\n data-find-counter=\"\"\n aria-live=\"polite\"\n title={invalidPattern ? t.invalidRegex : undefined}\n >\n {invalidPattern ? t.invalidRegex : counter}\n </span>\n </span>\n <button\n type=\"button\"\n className={toggleClass(state.caseSensitive)}\n data-find-option=\"match-case\"\n title={t.caseSensitive}\n aria-label={t.caseSensitive}\n aria-pressed={state.caseSensitive}\n onMouseDown={keepFocus}\n onClick={() => toggle('caseSensitive', !state.caseSensitive)}\n >\n Aa\n </button>\n <button\n type=\"button\"\n className={toggleClass(state.wholeWord)}\n data-find-option=\"whole-words\"\n title={t.wholeWord}\n aria-label={t.wholeWord}\n aria-pressed={state.wholeWord}\n onMouseDown={keepFocus}\n onClick={() => toggle('wholeWord', !state.wholeWord)}\n >\n ab\n </button>\n\n <span className={styles.spacer} />\n <span className={styles.iconGroup}>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"previous\"\n title={t.previous}\n aria-label={t.previous}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.goToPreviousResult())}\n >\n <ChevronUpIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"next\"\n title={t.next}\n aria-label={t.next}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.goToNextResult())}\n >\n <ChevronDownIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"close\"\n title={t.close}\n aria-label={t.close}\n onClick={onClose}\n >\n <XIcon size={16} />\n </button>\n </span>\n </div>\n\n {editor.isEditable ? (\n <div className={styles.row}>\n <span className={styles.field}>\n <input\n className={styles.input}\n data-find-field=\"replace\"\n value={replaceTerm}\n onChange={(e) => setReplaceTerm(e.target.value)}\n onKeyDown={onReplaceKeyDown}\n placeholder={t.replace}\n aria-label={t.replace}\n spellCheck={false}\n />\n </span>\n <span className={styles.spacer} />\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"replace\"\n title={t.replaceOne}\n aria-label={t.replaceOne}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.replace())}\n >\n <ReplaceIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-find-action=\"replace-all\"\n title={t.replaceAll}\n aria-label={t.replaceAll}\n disabled={state.total === 0}\n onMouseDown={keepFocus}\n onClick={() => runAction(() => editor.commands.replaceAll())}\n >\n <ReplaceAllIcon size={16} />\n </button>\n </div>\n ) : (\n <div className={styles.row}>\n <span className={styles.readOnly}>{t.readOnly}</span>\n </div>\n )}\n </div>\n );\n}\n","import { Node } from '@tiptap/core';\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n importPlaceholder: {\n insertImportPlaceholder: (label: string) => ReturnType;\n updateImportPlaceholder: (label: string) => ReturnType;\n removeImportPlaceholder: () => ReturnType;\n };\n }\n}\n\nfunction findPlaceholderPos(doc: {\n descendants: (\n f: (node: { type: { name: string } }, pos: number) => boolean | void,\n ) => void;\n}): number | null {\n let found: number | null = null;\n doc.descendants((node, pos) => {\n if (node.type.name === 'importPlaceholder') {\n found = pos;\n return false;\n }\n });\n return found;\n}\n\n/** 导入进行中插在光标处的占位块。不进 Markdown,autosave 不会把进度文案落库。 */\nexport const ImportPlaceholder = Node.create({\n name: 'importPlaceholder',\n group: 'block',\n atom: true,\n selectable: false,\n draggable: false,\n\n addAttributes() {\n return {\n label: {\n default: '',\n parseHTML: (el) => el.getAttribute('data-label') ?? '',\n renderHTML: (attrs) =>\n attrs.label ? { 'data-label': attrs.label } : {},\n },\n };\n },\n\n parseHTML() {\n return [{ tag: 'div[data-type=\"import-placeholder\"]' }];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n const label = String(node.attrs.label ?? '');\n return [\n 'div',\n {\n ...HTMLAttributes,\n 'data-type': 'import-placeholder',\n class: 'tmr-import-placeholder',\n contenteditable: 'false',\n 'aria-live': 'polite',\n 'aria-busy': 'true',\n },\n ['span', { class: 'tmr-import-placeholder-spin', 'aria-hidden': 'true' }],\n ['span', { class: 'tmr-import-placeholder-label' }, label],\n ];\n },\n\n renderMarkdown() {\n return '';\n },\n\n addCommands() {\n return {\n insertImportPlaceholder:\n (label) =>\n ({ chain }) =>\n chain()\n .focus()\n .insertContent({\n type: this.name,\n attrs: { label },\n })\n .command(({ tr }) => {\n tr.setMeta('addToHistory', false);\n tr.setMeta('preventUpdate', true);\n return true;\n })\n .run(),\n updateImportPlaceholder:\n (label) =>\n ({ tr, dispatch, state }) => {\n const pos = findPlaceholderPos(state.doc);\n if (pos == null) return false;\n if (dispatch) {\n dispatch(\n tr\n .setNodeMarkup(pos, undefined, { label })\n .setMeta('addToHistory', false)\n .setMeta('preventUpdate', true),\n );\n }\n return true;\n },\n removeImportPlaceholder:\n () =>\n ({ tr, dispatch, state }) => {\n const pos = findPlaceholderPos(state.doc);\n if (pos == null) return false;\n const node = state.doc.nodeAt(pos);\n if (!node) return false;\n if (dispatch) {\n dispatch(\n tr\n .delete(pos, pos + node.nodeSize)\n .setMeta('addToHistory', false)\n .setMeta('preventUpdate', true),\n );\n }\n return true;\n },\n };\n },\n});\n","import type { Editor } from '@tiptap/core';\nimport { useCallback, useEffect, useLayoutEffect, useSyncExternalStore } from 'react';\nimport { getLocaleBundle } from './index';\nimport type { LocaleBundle, LocaleCode } from './types';\n\nconst serverBundle = getLocaleBundle('en');\n\ninterface LocaleEntry {\n locale: LocaleCode;\n bundle: LocaleBundle;\n listeners: Set<() => void>;\n /** 渲染期只改快照,绘制前再通知 NodeView。 */\n pendingNotify: boolean;\n}\n\nconst stores = new WeakMap<Editor, LocaleEntry>();\n\nfunction ensure(editor: Editor): LocaleEntry {\n let entry = stores.get(editor);\n if (!entry) {\n entry = {\n locale: 'en',\n bundle: serverBundle,\n listeners: new Set(),\n pendingNotify: false,\n };\n stores.set(editor, entry);\n }\n return entry;\n}\n\n/** 写入当前语言。同值不重复通知。可在渲染期间调用。 */\nexport function setEditorLocale(editor: Editor, locale: LocaleCode): void {\n const entry = ensure(editor);\n if (entry.locale === locale) return;\n entry.locale = locale;\n entry.bundle = getLocaleBundle(locale);\n entry.pendingNotify = true;\n}\n\n/** 把渲染期写入的语言变化发给已挂载的 NodeView。 */\nexport function flushEditorLocale(editor: Editor): void {\n const entry = stores.get(editor);\n if (!entry?.pendingNotify) return;\n entry.pendingNotify = false;\n entry.listeners.forEach((listener) => listener());\n}\n\nexport function getEditorLocale(editor: Editor): LocaleCode {\n return stores.get(editor)?.locale ?? 'en';\n}\n\nexport function getEditorBundle(editor: Editor): LocaleBundle {\n return stores.get(editor)?.bundle ?? serverBundle;\n}\n\nexport function subscribeEditorLocale(\n editor: Editor,\n listener: () => void,\n): () => void {\n const entry = ensure(editor);\n entry.listeners.add(listener);\n return () => {\n entry.listeners.delete(listener);\n };\n}\n\nconst useIsoLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\n/**\n * 编辑器 / 预览在渲染时写入语言,绘制前通知独立 React 根上的 NodeView。\n * `immediatelyRender: false` 时视图在 effect 之后才创建,首帧就能读到正确语言。\n */\nexport function usePublishEditorLocale(\n editor: Editor | null,\n locale: LocaleCode,\n): void {\n if (editor) setEditorLocale(editor, locale);\n useIsoLayoutEffect(() => {\n if (!editor || editor.isDestroyed) return;\n flushEditorLocale(editor);\n }, [editor, locale]);\n}\n\n/** NodeView 订阅所属编辑器的文案。快照引用稳定,语言不变时不重渲染。 */\nexport function useEditorBundle(editor: Editor): LocaleBundle {\n const subscribe = useCallback(\n (onStoreChange: () => void) => subscribeEditorLocale(editor, onStoreChange),\n [editor],\n );\n const getSnapshot = useCallback(() => getEditorBundle(editor), [editor]);\n return useSyncExternalStore(subscribe, getSnapshot, () => serverBundle);\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/core';\nimport { Selection, TextSelection } from '@tiptap/pm/state';\nimport { NodeViewWrapper, type NodeViewProps } from '@tiptap/react';\nimport { useEffect, useRef, useState } from 'react';\nimport { XIcon } from '../icons';\nimport { useEditorBundle } from '../i18n/store';\nimport type { ImageUploadOptions } from '../imageUpload';\nimport styles from '../styles/imageUpload.module.css';\n\ninterface FileItem {\n id: string;\n file: File;\n /** 0–100。 */\n progress: number;\n status: 'uploading' | 'error';\n abort: AbortController;\n}\n\nlet seq = 0;\nconst nextId = () => `image-upload-${++seq}`;\n\n/** 文件体积的人类可读格式。 */\nfunction formatFileSize(bytes: number): string {\n if (bytes <= 0) return '0 B';\n const units = ['B', 'KB', 'MB', 'GB'];\n const i = Math.min(\n Math.floor(Math.log(bytes) / Math.log(1024)),\n units.length - 1,\n );\n return `${parseFloat((bytes / 1024 ** i).toFixed(1))} ${units[i]}`;\n}\n\n/** MB 数值去掉多余的 .0(5MB / 2.5MB)。 */\nconst toMb = (bytes: number) => parseFloat((bytes / 1024 / 1024).toFixed(1));\n\n/**\n * 上传完成后把光标移到图片之后;没有后继节点就补一个空段落。\n * 不挪的话替换成 atom 图片后光标悬空,用户接着打字会落在占位块之前。\n */\nfunction focusNextNode(editor: Editor) {\n const { state, view } = editor;\n const next = Selection.findFrom(state.selection.$to, 1, true);\n if (next) {\n view.dispatch(state.tr.setSelection(next).scrollIntoView());\n return;\n }\n const paragraph = state.schema.nodes.paragraph;\n if (!paragraph) return;\n const end = state.doc.content.size;\n const tr = state.tr.insert(end, paragraph.create());\n view.dispatch(\n tr\n .setSelection(TextSelection.near(tr.doc.resolve(end + 1)))\n .scrollIntoView(),\n );\n}\n\n/**\n * 图片上传占位块的 NodeView:空态是点击 / 拖拽区,排队后是文件行(进度条 + 百分比 +\n * 移除),全部成功时就地把自己换成图片节点。上传函数与回调都来自扩展 options。\n *\n * 组件结构 / 文件队列 / 拖拽 / 中止逻辑按官方 UI Components 的 `image-upload-node`\n * 移植(MIT,见 THIRD_PARTY_LICENSES.md 第 2 节),按本库的样式体系(CSS Modules +\n * --tmr-* 变量)与 labels 注入重写。\n */\nexport function ImageUploadView(props: NodeViewProps) {\n const { node, extension, editor, getPos } = props;\n const options = extension.options as ImageUploadOptions;\n const labels = useEditorBundle(editor).imageUpload;\n\n const accept = String(node.attrs.accept ?? 'image/*');\n const limit = Number(node.attrs.limit ?? 1) || 1;\n const maxSize = Number(node.attrs.maxSize ?? 0) || 0;\n\n const [items, setItems] = useState<FileItem[]>([]);\n const [dragActive, setDragActive] = useState(false);\n const [dragOver, setDragOver] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n // 卸载(占位块被替换 / 删除 / 换文档)时,把在飞的请求全部中断\n const itemsRef = useRef(items);\n itemsRef.current = items;\n useEffect(\n () => () => itemsRef.current.forEach((it) => it.abort.abort()),\n [],\n );\n\n const patchItem = (id: string, partial: Partial<FileItem>) => {\n setItems((prev) =>\n prev.map((it) => (it.id === id ? { ...it, ...partial } : it)),\n );\n };\n\n const uploadOne = async (file: File): Promise<string | null> => {\n if (maxSize > 0 && file.size > maxSize) {\n options.onError?.(\n new Error(\n `File size exceeds the maximum of ${formatFileSize(maxSize)}`,\n ),\n );\n return null;\n }\n if (!options.upload) {\n options.onError?.(new Error('Upload function is not defined'));\n return null;\n }\n\n const abort = new AbortController();\n const id = nextId();\n setItems((prev) => [\n ...prev,\n { id, file, progress: 0, status: 'uploading', abort },\n ]);\n\n try {\n const url = await options.upload(\n file,\n ({ progress }) => patchItem(id, { progress }),\n abort.signal,\n );\n if (!url) throw new Error('Upload returned no URL');\n if (abort.signal.aborted) return null;\n options.onSuccess?.(url);\n return url;\n } catch (err) {\n // abort 是用户主动移除,不算失败\n if (!abort.signal.aborted) {\n patchItem(id, { status: 'error', progress: 0 });\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n }\n return null;\n }\n };\n\n const uploadFiles = async (files: File[]) => {\n if (files.length === 0) return;\n if (limit > 0 && files.length > limit) {\n options.onError?.(\n new Error(`Maximum ${limit} file${limit === 1 ? '' : 's'} at a time`),\n );\n return;\n }\n // 逐文件配对结果:只对成功的做插入,且文件名跟着自己的 URL 走\n const settled = await Promise.all(\n files.map(async (file) => ({ file, url: await uploadOne(file) })),\n );\n const uploaded = settled.filter(\n (s): s is { file: File; url: string } => s.url != null,\n );\n if (uploaded.length === 0) return;\n\n const pos = getPos();\n if (typeof pos !== 'number') return;\n const imageType = options.type ?? 'image';\n const nodes = uploaded.map(({ file, url }) => {\n const alt = file.name.replace(/\\.[^/.]+$/, '') || labels.fallbackAlt;\n return { type: imageType, attrs: { src: url, alt, title: alt } };\n });\n editor\n .chain()\n .focus()\n .deleteRange({ from: pos, to: pos + node.nodeSize })\n .insertContentAt(pos, nodes)\n .run();\n focusNextNode(editor);\n };\n\n const removeItem = (id: string) => {\n itemsRef.current.find((it) => it.id === id)?.abort.abort();\n setItems((prev) => prev.filter((it) => it.id !== id));\n };\n\n const clearAll = () => {\n itemsRef.current.forEach((it) => it.abort.abort());\n setItems([]);\n };\n\n const openPicker = () => {\n if (!editor.isEditable || items.length > 0) return;\n const input = inputRef.current;\n if (!input) return;\n input.value = '';\n input.click();\n };\n\n const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(e.target.files ?? []);\n e.target.value = '';\n if (files.length > 0) void uploadFiles(files);\n };\n\n const onDrop = (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n setDragOver(false);\n const files = Array.from(e.dataTransfer.files);\n if (files.length > 0) void uploadFiles(files);\n };\n\n const editable = editor.isEditable;\n const hasFiles = items.length > 0;\n const maxSizeMb = maxSize > 0 ? toMb(maxSize) : null;\n const dropzoneClass = [\n styles.dropzone,\n dragActive ? styles.dragActive : '',\n dragOver ? styles.dragOver : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <NodeViewWrapper\n className={styles.wrap}\n data-image-upload=\"\"\n tabIndex={editable ? 0 : undefined}\n onClick={openPicker}\n >\n {!hasFiles ? (\n <div\n className={dropzoneClass}\n data-image-upload-dropzone=\"\"\n role={editable ? 'button' : undefined}\n aria-label={\n editable ? `${labels.dropzoneClick}${labels.dropzoneRest}` : undefined\n }\n onDragEnter={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(true);\n }}\n onDragOver={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragOver(true);\n }}\n onDragLeave={(e) => {\n e.preventDefault();\n e.stopPropagation();\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n setDragActive(false);\n setDragOver(false);\n }\n }}\n onDrop={onDrop}\n >\n <span className={styles.fileShape} aria-hidden>\n <FileDocIcon />\n <FileCornerIcon />\n <span className={styles.fileBadge}>\n <CloudUploadIcon />\n </span>\n </span>\n <span className={styles.text}>\n <span className={styles.textMain}>\n <em className={styles.textEm}>{labels.dropzoneClick}</em>\n {labels.dropzoneRest}\n </span>\n <span className={styles.subtext}>\n {labels.dropzoneLimits(limit, maxSizeMb)}\n </span>\n </span>\n </div>\n ) : (\n <div className={styles.previews} data-image-upload-list=\"\">\n {items.length > 1 && (\n <div className={styles.header}>\n <span>{labels.uploadingCount(items.length)}</span>\n <button\n type=\"button\"\n className={styles.clearAll}\n onClick={(e) => {\n e.stopPropagation();\n clearAll();\n }}\n >\n {labels.clearAll}\n </button>\n </div>\n )}\n {items.map((it) => (\n <div\n key={it.id}\n className={styles.preview}\n data-image-upload-item=\"\"\n >\n {it.status === 'uploading' && (\n <div\n className={styles.progress}\n style={{ width: `${it.progress}%` }}\n aria-hidden\n />\n )}\n <div className={styles.previewContent}>\n <span className={styles.fileInfo}>\n <span className={styles.fileIcon} aria-hidden>\n <CloudUploadIcon />\n </span>\n <span className={styles.details}>\n <span className={styles.fileName}>{it.file.name}</span>\n <span className={styles.subtext}>\n {it.status === 'error'\n ? labels.failed\n : formatFileSize(it.file.size)}\n </span>\n </span>\n </span>\n <span className={styles.actions}>\n {it.status === 'uploading' && (\n <span className={styles.progressText}>\n {Math.round(it.progress)}%\n </span>\n )}\n <button\n type=\"button\"\n className={styles.remove}\n data-image-upload-remove=\"\"\n title={labels.remove}\n aria-label={labels.remove}\n onClick={(e) => {\n e.stopPropagation();\n removeItem(it.id);\n }}\n >\n <XIcon size={14} />\n </button>\n </span>\n </div>\n </div>\n ))}\n </div>\n )}\n\n {editable && (\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={limit > 1}\n hidden\n onChange={onInputChange}\n onClick={(e) => e.stopPropagation()}\n />\n )}\n </NodeViewWrapper>\n );\n}\n\n/* 图标 path 取自 tiptap-ui-components(MIT)的 image-upload-node,见 THIRD_PARTY_LICENSES.md */\n\nfunction CloudUploadIcon({ size = 14 }: { size?: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden\n >\n <path d=\"M11.1953 4.41771C10.3478 4.08499 9.43578 3.94949 8.5282 4.02147C7.62062 4.09345 6.74133 4.37102 5.95691 4.83316C5.1725 5.2953 4.50354 5.92989 4.00071 6.68886C3.49788 7.44783 3.17436 8.31128 3.05465 9.2138C2.93495 10.1163 3.0222 11.0343 3.3098 11.8981C3.5974 12.7619 4.07781 13.5489 4.71463 14.1995C5.10094 14.5942 5.09414 15.2274 4.69945 15.6137C4.30476 16 3.67163 15.9932 3.28532 15.5985C2.43622 14.731 1.79568 13.6816 1.41221 12.5299C1.02875 11.3781 0.91241 10.1542 1.07201 8.95084C1.23162 7.74748 1.66298 6.59621 2.33343 5.58425C3.00387 4.57229 3.89581 3.72617 4.9417 3.10998C5.98758 2.4938 7.15998 2.1237 8.37008 2.02773C9.58018 1.93176 10.7963 2.11243 11.9262 2.55605C13.0561 2.99968 14.0703 3.69462 14.8919 4.58825C15.5423 5.29573 16.0585 6.11304 16.4177 7.00002H17.4999C18.6799 6.99991 19.8288 7.37933 20.7766 8.08222C21.7245 8.78515 22.4212 9.7743 22.7637 10.9036C23.1062 12.0328 23.0765 13.2423 22.6788 14.3534C22.2812 15.4644 21.5367 16.4181 20.5554 17.0736C20.0962 17.3803 19.4752 17.2567 19.1684 16.7975C18.8617 16.3382 18.9853 15.7172 19.4445 15.4105C20.069 14.9934 20.5427 14.3865 20.7958 13.6794C21.0488 12.9724 21.0678 12.2027 20.8498 11.4841C20.6318 10.7655 20.1885 10.136 19.5853 9.6887C18.9821 9.24138 18.251 8.99993 17.5001 9.00002H15.71C15.2679 9.00002 14.8783 8.70973 14.7518 8.28611C14.4913 7.41374 14.0357 6.61208 13.4195 5.94186C12.8034 5.27164 12.0427 4.75043 11.1953 4.41771Z\" />\n <path d=\"M11 14.4142V21C11 21.5523 11.4477 22 12 22C12.5523 22 13 21.5523 13 21V14.4142L15.2929 16.7071C15.6834 17.0976 16.3166 17.0976 16.7071 16.7071C17.0976 16.3166 17.0976 15.6834 16.7071 15.2929L12.7078 11.2936C12.7054 11.2912 12.703 11.2888 12.7005 11.2864C12.5208 11.1099 12.2746 11.0008 12.003 11L12 11L11.997 11C11.8625 11.0004 11.7343 11.0273 11.6172 11.0759C11.502 11.1236 11.3938 11.1937 11.2995 11.2864C11.297 11.2888 11.2946 11.2912 11.2922 11.2936L7.29289 15.2929C6.90237 15.6834 6.90237 16.3166 7.29289 16.7071C7.68342 17.0976 8.31658 17.0976 8.70711 16.7071L11 14.4142Z\" />\n </svg>\n );\n}\n\nfunction FileDocIcon() {\n return (\n <svg\n width=\"43\"\n height=\"57\"\n viewBox=\"0 0 43 57\"\n fill=\"none\"\n aria-hidden\n className={styles.fileShapeDoc}\n >\n <path\n d=\"M0.75 10.75C0.75 5.64137 4.89137 1.5 10 1.5H32.3431C33.2051 1.5 34.0317 1.84241 34.6412 2.4519L40.2981 8.10876C40.9076 8.71825 41.25 9.5449 41.25 10.4069V46.75C41.25 51.8586 37.1086 56 32 56H10C4.89137 56 0.75 51.8586 0.75 46.75V10.75Z\"\n fill=\"currentColor\"\n fillOpacity=\"0.11\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n />\n </svg>\n );\n}\n\nfunction FileCornerIcon() {\n return (\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"currentColor\"\n aria-hidden\n className={styles.fileShapeCorner}\n >\n <path d=\"M0 0.75H0.343146C1.40401 0.75 2.42143 1.17143 3.17157 1.92157L8.82843 7.57843C9.57857 8.32857 10 9.34599 10 10.4069V10.75H4C1.79086 10.75 0 8.95914 0 6.75V0.75Z\" />\n </svg>\n );\n}\n","/**\n * 图片上传占位块(`ImageUploadNode`):交互与扩展结构对齐官方 UI Components 的\n * `image-upload-node`(MIT,移植范围与声明见 THIRD_PARTY_LICENSES.md 第 2 节)——\n * 工具栏按钮插入占位块,块内拖拽 / 点选、进度就地显示,全部成功后原地换成图片节点。\n */\nimport { mergeAttributes, Node, type Editor } from '@tiptap/core';\nimport { ReactNodeViewRenderer } from '@tiptap/react';\nimport { ImageUploadView } from './components/ImageUploadView';\n\n/**\n * 上传函数:收到文件与进度回调(`progress` 为 0–100),返回可访问的图片 URL。\n * `signal` 在用户移除该文件 / 清空队列 / 占位块被删掉时 abort,宿主据此中断请求。\n * 旧的 `(file) => Promise<string>` 单参函数仍可直接传入。\n */\nexport type ImageUploadFn = (\n file: File,\n onProgress?: (event: { progress: number }) => void,\n signal?: AbortSignal,\n) => Promise<string>;\n\nexport interface ImageUploadOptions {\n /** 真正干活的上传(网络 I/O 归宿主)。未配置时选中文件即走 `onError`。 */\n upload?: ImageUploadFn;\n /** 文件选择框接受的文件类型,默认 `image/*`。 */\n accept?: string;\n /** 一次可排队上传的文件数上限,默认 1;大于 1 时文件选择框允许多选。 */\n limit?: number;\n /** 单文件体积上限(字节),默认 0 = 不限。 */\n maxSize?: number;\n /** 上传成功后就地替换成的节点类型,默认 `image`(本库的块级图片)。 */\n type?: string;\n /** 单个文件失败的回调:超体积、超数量、上传函数抛错都走这里。 */\n onError?: (error: Error) => void;\n /** 单个文件成功的回调(先于占位块替换触发)。 */\n onSuccess?: (url: string) => void;\n}\n\n/**\n * 组件层(`<MarkdownWysiwygEditor imageUpload={{…}}>`)的配置:`upload` 必填——\n * 组件入口就是要注入 I/O 的地方;扩展层单独用时才允许先不配。\n */\nexport type ImageUploadConfig = ImageUploadOptions & { upload: ImageUploadFn };\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n imageUpload: {\n /**\n * 在光标处插入图片上传占位块:可点击选图 / 拖拽,上传进度就地显示,\n * 全部成功后就地换成图片节点。参数覆盖 configure 的 accept / limit / maxSize。\n */\n setImageUploadNode: (options?: {\n accept?: string;\n limit?: number;\n maxSize?: number;\n }) => ReturnType;\n };\n }\n}\n\n/** 编辑器里是否注册了图片上传扩展(工具栏据此把图片按钮切成「插入上传块」)。 */\nexport function hasImageUpload(editor: Editor): boolean {\n return editor.extensionManager.extensions.some(\n (ext) => ext.name === ImageUploadNode.name,\n );\n}\n\n/**\n * 图片上传占位块(对齐官方 ImageUploadNode 的交互):块级 atom,NodeView 里自带\n * 拖拽区 / 文件队列 / 进度条,上传成功后原地替换为 `type` 指定的图片节点。\n *\n * 两条与官方不同的边界处理:\n * - `renderMarkdown` 返回空串——上传中的占位块不进 Markdown,autosave 不会把\n * 进度文案落库(HTML 序列化才有 `data-type=\"image-upload\"` 的痕迹)。\n * - 体积上限 0 表示「不限」:判超限必须走 `maxSize > 0`,否则默认值 0 会把所有\n * 非空文件都判成超限(官方实现即栽在这条上)。\n */\nexport const ImageUploadNode = Node.create<ImageUploadOptions>({\n name: 'imageUpload',\n group: 'block',\n atom: true,\n draggable: true,\n selectable: true,\n\n addOptions() {\n return {\n upload: undefined,\n accept: 'image/*',\n limit: 1,\n maxSize: 0,\n type: 'image',\n onError: undefined,\n onSuccess: undefined,\n };\n },\n\n addAttributes() {\n return {\n accept: { default: this.options.accept },\n limit: { default: this.options.limit },\n maxSize: { default: this.options.maxSize },\n };\n },\n\n parseHTML() {\n return [{ tag: 'div[data-type=\"image-upload\"]' }];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'div',\n mergeAttributes({ 'data-type': 'image-upload' }, HTMLAttributes),\n ];\n },\n\n /** 占位块不进 Markdown(与 ImportPlaceholder 同):上传中的状态不该被保存。 */\n renderMarkdown() {\n return '';\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(ImageUploadView);\n },\n\n addCommands() {\n return {\n setImageUploadNode:\n (options) =>\n ({ commands }) =>\n commands.insertContent({ type: this.name, attrs: options }),\n };\n },\n\n addKeyboardShortcuts() {\n return {\n // 官方图片按钮的快捷键(Cmd/Ctrl+Shift+I)。Windows 上 Ctrl+Shift+I 是\n // DevTools 的键位,冲突时浏览器优先——与官方行为一致。\n 'Mod-Shift-i': () => this.editor.commands.setImageUploadNode(),\n // 选中占位块时按 Enter = 打开文件选择框。借 NodeView 的点击路径(NodeViewWrapper\n // 外包一层 div,所以要点它的第一个子元素),与官方同款实现。\n Enter: ({ editor }) => {\n const { selection } = editor.state;\n const { nodeAfter } = selection.$from;\n if (nodeAfter?.type.name !== this.name || !editor.isActive(this.name)) {\n return false;\n }\n const nodeEl = editor.view.nodeDOM(selection.$from.pos);\n const clickable =\n nodeEl instanceof HTMLElement ? nodeEl.firstChild : null;\n if (clickable instanceof HTMLElement) {\n clickable.click();\n return true;\n }\n return false;\n },\n };\n },\n});\n","import type { Editor } from '@tiptap/core';\nimport { NodeSelection } from '@tiptap/pm/state';\nimport { normalizeImageAlign, type ImageAlign } from './imageMarkdown';\n\n/**\n * 找「当前图片」的位置:节点选区落在图上,或光标紧邻图前 / 图后。\n * 对齐与重置尺寸(resetImageSize)共用这套判据。\n */\nexport function findImagePos(editor: Editor): number | null {\n const { selection } = editor.state;\n if (selection instanceof NodeSelection && selection.node.type.name === 'image') {\n return selection.from;\n }\n const { $from } = selection;\n if ($from.nodeAfter?.type.name === 'image') return $from.pos;\n if ($from.nodeBefore?.type.name === 'image') {\n return $from.pos - $from.nodeBefore.nodeSize;\n }\n return null;\n}\n\nfunction readAlign(editor: Editor, pos: number): ImageAlign | null {\n return normalizeImageAlign(editor.state.doc.nodeAt(pos)?.attrs.align);\n}\n\n/** 当前图片是否已对齐到指定方向(宿主画按钮态用;没选中图时为 false)。 */\nexport function isImageAlignActive(editor: Editor, align: ImageAlign): boolean {\n const pos = findImagePos(editor);\n return pos != null && readAlign(editor, pos) === align;\n}\n\n/**\n * 把「当前图片」(选中 / 光标紧邻)对齐到 left / center / right。\n * attrs 存 `align`、渲染成 `data-align`;markdown 里以\n * `<img … data-align=\"…\">` 保真(见 imageMarkdown.ts)。返回是否找到图片。\n * 已是对齐值时不写事务(不污染 undo 历史)。\n */\nexport function setImageAlign(editor: Editor, align: ImageAlign): boolean {\n const pos = findImagePos(editor);\n if (pos == null) return false;\n if (readAlign(editor, pos) === align) return true;\n return editor\n .chain()\n .focus()\n .setNodeSelection(pos)\n .updateAttributes('image', { align })\n .run();\n}\n\n/**\n * 按文档位置对齐(悬停工具条用:hover 时记下位置,attrs 读写都走 pos)。\n * 不改变选区——点一下对齐按钮不该先「选中」图片。\n *\n * 注意别改成长期持有 img 元素的引用:**attrs 更新时 prosemirror 会替换裸渲染的\n * img 元素**(旧引用脱离文档、读不到新属性,后续 posAtDOM 也会抛),所以位置比元素可靠。\n */\nexport function setImageAlignAtPos(\n editor: Editor,\n pos: number,\n align: ImageAlign,\n): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return false;\n if (normalizeImageAlign(node.attrs.align) === align) return true;\n editor.view.dispatch(editor.state.tr.setNodeAttribute(pos, 'align', align));\n return true;\n}\n\n/**\n * 按 DOM 元素对齐(宿主自定义 UI 用:拿到的是当前挂着的元素)。转成位置后走\n * {@link setImageAlignAtPos}。\n */\nexport function setImageAlignAt(\n editor: Editor,\n imageElement: HTMLElement,\n align: ImageAlign,\n): boolean {\n try {\n const pos = editor.view.posAtDOM(imageElement, 0);\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return false;\n return setImageAlignAtPos(editor, pos, align);\n } catch {\n return false;\n }\n}\n","import type { EmojiItem } from '@tiptap/extension-emoji';\n\n/** 底部标签。顺序与官方数据的 group 一致;图标是分类代表,不依赖某一条数据还在不在。 */\nexport const EMOJI_CATEGORIES = [\n { id: 'smileys', group: '', label: 'Smileys', icon: '😀' },\n { id: 'people', group: 'people & body', label: 'People', icon: '👋' },\n { id: 'nature', group: 'animals & nature', label: 'Animals & nature', icon: '🐻' },\n { id: 'food', group: 'food & drink', label: 'Food & drink', icon: '🍔' },\n { id: 'travel', group: 'travel & places', label: 'Travel & places', icon: '✈️' },\n { id: 'activities', group: 'activities', label: 'Activities', icon: '⚽' },\n { id: 'objects', group: 'objects', label: 'Objects', icon: '💡' },\n { id: 'symbols', group: 'symbols', label: 'Symbols', icon: '💯' },\n { id: 'flags', group: 'flags', label: 'Flags', icon: '🏁' },\n] as const;\n\nexport type EmojiCategoryId = (typeof EMOJI_CATEGORIES)[number]['id'];\n\n/** 肤色和发型零件。官方扩展不支持肤色修饰,单独显示也不是可选表情。 */\nexport function isEmojiComponent(item: EmojiItem): boolean {\n return item.group === 'components';\n}\n\nexport function itemsInCategory(items: EmojiItem[], id: EmojiCategoryId): EmojiItem[] {\n const category = EMOJI_CATEGORIES.find((entry) => entry.id === id);\n if (!category) return [];\n return items.filter((item) => (item.group ?? '') === category.group);\n}\n","import type { EmojiItem } from '@tiptap/extension-emoji';\nimport { isEmojiComponent } from './emojiCategories';\n\n/**\n * 单独的区域指示符是拼国旗的字母零件,系统字体画成方框字母。\n * 建议列表里不放;手打完整 `:regional_indicator_a:` 仍能变成节点。\n */\nfunction isRegionalIndicator(item: EmojiItem): boolean {\n return item.name.startsWith('regional_indicator_');\n}\n\n/**\n * 按短码 / 名称优先、标签其次过滤表情。\n * 空查询返回全部可用表情,弹层再按分类切开。\n * 区域指示符和肤色零件不进列表。没有 Unicode 字符的自定义图也不进。\n */\nexport function filterEmojiItems(items: EmojiItem[], query: string): EmojiItem[] {\n const q = query.trim().toLowerCase();\n const usable = items.filter(\n (item) => !!item.emoji && !isRegionalIndicator(item) && !isEmojiComponent(item),\n );\n if (!q) return usable;\n\n const primary: EmojiItem[] = [];\n const secondary: EmojiItem[] = [];\n for (const item of usable) {\n const nameHit =\n item.name.toLowerCase().startsWith(q) ||\n item.shortcodes.some((code) => code.toLowerCase().startsWith(q));\n if (nameHit) primary.push(item);\n else if (item.tags.some((tag) => tag.toLowerCase().startsWith(q))) {\n secondary.push(item);\n }\n }\n return [...primary, ...secondary];\n}\n","import type { EmojiItem } from '@tiptap/extension-emoji';\nimport { EmojiSuggestionPluginKey } from '@tiptap/extension-emoji';\nimport type { AnyExtension, Editor, Range } from '@tiptap/core';\nimport type { EditorState } from '@tiptap/pm/state';\nimport { ReactRenderer } from '@tiptap/react';\nimport type {\n SuggestionKeyDownProps,\n SuggestionProps,\n} from '@tiptap/suggestion';\nimport {\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { getEditorBundle } from '../i18n/store';\nimport styles from '../styles/emojiSuggestion.module.css';\nimport { EMOJI_CATEGORIES, itemsInCategory, type EmojiCategoryId } from './emojiCategories';\nimport { filterEmojiItems } from './filterEmoji';\n\n/** 与样式里的 8 列对齐,方向键按格子移动。 */\nconst COLUMNS = 8;\n\nexport interface EmojiSuggestionPopupHandle {\n /** 编辑器键盘事件经 IME 守卫后委托进来;返回 true 表示已消费。 */\n onKeyDown: (event: KeyboardEvent) => boolean;\n}\n\nexport interface EmojiSuggestionPopupProps {\n items: EmojiItem[];\n query: string;\n menuLabel: string;\n command: (item: EmojiItem) => void;\n}\n\n/** 输入法组合态下的按键交给 IME 自己处理(上游 suggestion 不查 composing)。 */\nfunction isComposing(event: KeyboardEvent, view: { composing: boolean }): boolean {\n return view.composing || event.isComposing || event.keyCode === 229;\n}\n\nconst themeVars = [\n '--tmr-text',\n '--tmr-accent',\n '--tmr-toolbar-border',\n '--tmr-toolbar-muted',\n] as const;\n\nfunction copyThemeVars(from: HTMLElement, to: HTMLElement) {\n const computed = getComputedStyle(from);\n for (const name of themeVars) {\n const value = computed.getPropertyValue(name);\n if (value) to.style.setProperty(name, value);\n }\n}\n\nfunction moveIndex(index: number, key: string, length: number, shiftKey: boolean): number | null {\n if (key === 'ArrowRight' || (key === 'Tab' && !shiftKey)) {\n return (index + 1) % length;\n }\n if (key === 'ArrowLeft' || (key === 'Tab' && shiftKey)) {\n return (index - 1 + length) % length;\n }\n if (key === 'ArrowDown') {\n const next = index + COLUMNS;\n return next < length ? next : null;\n }\n if (key === 'ArrowUp') {\n const next = index - COLUMNS;\n return next >= 0 ? next : null;\n }\n return null;\n}\n\nfunction keepCellInView(menu: HTMLElement, cell: HTMLElement) {\n const menuRect = menu.getBoundingClientRect();\n const cellRect = cell.getBoundingClientRect();\n if (cellRect.top < menuRect.top) menu.scrollTop -= menuRect.top - cellRect.top;\n else if (cellRect.bottom > menuRect.bottom) {\n menu.scrollTop += cellRect.bottom - menuRect.bottom;\n }\n}\n\n/**\n * 冒号表情格子。空查询按底部分类标签浏览;继续打短码则在全部分类里筛选。\n * 定位由 @tiptap/suggestion 的 mount 接管。\n */\nexport const EmojiSuggestionPopup = forwardRef<\n EmojiSuggestionPopupHandle,\n EmojiSuggestionPopupProps\n>(function EmojiSuggestionPopup({ items, query, menuLabel, command }, ref) {\n const gridId = useId();\n const browsing = !query.trim();\n const [categoryId, setCategoryId] = useState<EmojiCategoryId>(EMOJI_CATEGORIES[0].id);\n const [index, setIndex] = useState(0);\n const menuRef = useRef<HTMLDivElement>(null);\n const cellRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const categories = useMemo(\n () => EMOJI_CATEGORIES.filter((category) => itemsInCategory(items, category.id).length > 0),\n [items],\n );\n const activeId = categories.some((category) => category.id === categoryId)\n ? categoryId\n : (categories[0]?.id ?? EMOJI_CATEGORIES[0].id);\n const visible = browsing ? itemsInCategory(items, activeId) : items;\n\n useEffect(() => {\n setIndex(0);\n menuRef.current?.scrollTo(0, 0);\n }, [query, activeId]);\n\n useEffect(() => {\n const menu = menuRef.current;\n const cell = cellRefs.current[index];\n if (menu && cell) keepCellInView(menu, cell);\n }, [index, activeId, query]);\n\n useImperativeHandle(\n ref,\n () => ({\n onKeyDown(event) {\n if (!visible.length) return false;\n const next = moveIndex(index, event.key, visible.length, event.shiftKey);\n if (next !== null) {\n setIndex(next);\n return true;\n }\n if (\n event.key === 'ArrowUp' ||\n event.key === 'ArrowDown' ||\n event.key === 'ArrowLeft' ||\n event.key === 'ArrowRight' ||\n event.key === 'Tab'\n ) {\n return true;\n }\n if (event.key === 'Enter') {\n const item = visible[index];\n if (!item) return false;\n command(item);\n return true;\n }\n return false;\n },\n }),\n [visible, index, command],\n );\n\n if (!visible.length) return null;\n\n return (\n <div className={styles.panel}>\n <div\n id={gridId}\n ref={menuRef}\n className={styles.menu}\n role=\"listbox\"\n aria-label={menuLabel}\n >\n {visible.map((item, i) => {\n const shortcode = `:${item.name}:`;\n return (\n <button\n key={item.name}\n ref={(node) => {\n cellRefs.current[i] = node;\n }}\n type=\"button\"\n role=\"option\"\n aria-label={shortcode}\n aria-selected={i === index}\n title={shortcode}\n className={i === index ? styles.cellActive : styles.cell}\n tabIndex={-1}\n onMouseEnter={() => setIndex(i)}\n onMouseDown={(event) => {\n event.preventDefault();\n command(item);\n }}\n >\n {item.emoji}\n </button>\n );\n })}\n </div>\n {browsing ? (\n <div className={styles.tabs} role=\"tablist\" aria-label={menuLabel}>\n {categories.map((category) => {\n const selected = category.id === activeId;\n return (\n <button\n key={category.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={selected}\n aria-controls={gridId}\n aria-label={category.label}\n title={category.label}\n className={selected ? styles.tabActive : styles.tab}\n tabIndex={-1}\n onMouseDown={(event) => {\n event.preventDefault();\n setCategoryId(category.id);\n }}\n >\n {category.icon}\n </button>\n );\n })}\n </div>\n ) : null}\n </div>\n );\n});\n\n/**\n * 把 schema 上的 emoji 扩展配成带(或不带)建议列表的那一份。\n * configure 深合并 options,官方的 command / char / pluginKey 留着。\n * 关掉时 allow 恒为 false:不弹层,`:smile:` 输入规则仍在。\n */\nexport function configureEmojiSuggestion(\n extension: AnyExtension,\n enabled: boolean,\n): AnyExtension {\n if (!enabled) {\n return extension.configure({\n suggestion: { allow: () => false },\n });\n }\n\n return extension.configure({\n suggestion: {\n pluginKey: EmojiSuggestionPluginKey,\n items: ({ editor, query }: { editor: Editor; query: string }) =>\n filterEmojiItems(editor.storage.emoji.emojis, query),\n allow: ({\n editor,\n state,\n range,\n }: {\n editor: Editor;\n state: EditorState;\n range: Range;\n }) => {\n if (!editor.isEditable) return false;\n if (editor.isActive('code') || editor.isActive('codeBlock')) return false;\n const type = state.schema.nodes.emoji;\n if (!type) return false;\n return !!state.doc.resolve(range.from).parent.type.contentMatch.matchType(type);\n },\n render: () => {\n let renderer: ReactRenderer<EmojiSuggestionPopupHandle> | null = null;\n let unmountPopup: (() => void) | null = null;\n return {\n onStart: (props: SuggestionProps<EmojiItem>) => {\n renderer = new ReactRenderer(EmojiSuggestionPopup, {\n editor: props.editor,\n props: {\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).emoji.menuLabel,\n command: props.command,\n },\n className: styles.popupHost,\n });\n copyThemeVars(props.editor.view.dom, renderer.element);\n unmountPopup = props.mount(renderer.element);\n },\n onUpdate: (props: SuggestionProps<EmojiItem>) => {\n renderer?.updateProps({\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).emoji.menuLabel,\n command: props.command,\n });\n },\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (!renderer || isComposing(props.event, props.view)) return false;\n return renderer.ref?.onKeyDown(props.event) ?? false;\n },\n onExit: () => {\n unmountPopup?.();\n unmountPopup = null;\n renderer?.destroy();\n renderer = null;\n },\n };\n },\n },\n });\n}\n","/**\n * 本库自写的词数。传给 MIT 包 `@tiptap/extensions` 的 CharacterCount 当作\n * `wordCounter`,不复制该扩展的源码。\n *\n * 扩展默认按空格切分,一整段中文会被算成 1 个词。这里每个汉字算 1 个词,\n * 其余仍按空白切开,并丢掉纯标点。\n */\nconst CJK_IDEOGRAPH =\n /[\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff]/g;\n\nexport function countWords(text: string): number {\n let cjk = 0;\n const rest = text.replace(CJK_IDEOGRAPH, () => {\n cjk += 1;\n return ' ';\n });\n const words = rest\n .split(/\\s+/)\n .filter((word) => /[\\p{L}\\p{N}]/u.test(word));\n return cjk + words.length;\n}\n","import type { Editor } from '@tiptap/core';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport type { SlashMenuItem } from './types';\n\n/**\n * 默认斜杠命令项:全部是「可序列化为 markdown」的块级操作。\n * 图片、附件、AI、二级子菜单、最近使用不在 v1 范围。\n *\n * 图标沿用工具栏下拉菜单的文字字形语言(`{ }` / `√x` / `∑` / `—` / `❝`),\n * 三组(文本 / 列表 / 高级)在弹窗里分节展示。\n */\nexport function createDefaultSlashMenuItems(\n locale: LocaleCode = 'en',\n): SlashMenuItem[] {\n const labels = getLocaleBundle(locale).slashMenu;\n return [\n {\n id: 'normal-text',\n title: labels.normalText,\n group: labels.groupText,\n icon: '¶',\n aliases: ['zhengwen', '正文', 'paragraph', 'text'],\n order: 0,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).setParagraph().run();\n },\n },\n ...([1, 2, 3] as const).map((level): SlashMenuItem => ({\n id: `heading-${level}`,\n title: labels.headingLabel(level),\n group: labels.groupText,\n icon: `H${level}`,\n aliases: [`h${level}`, 'biaoti', '标题', 'heading'],\n order: level,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .setHeading({ level })\n .run();\n },\n })),\n {\n id: 'bullet-list',\n title: labels.bulletList,\n group: labels.groupList,\n icon: '•',\n aliases: ['wuxu', '无序', '列表', 'bullet', 'ul'],\n order: 4,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleBulletList().run();\n },\n },\n {\n id: 'ordered-list',\n title: labels.orderedList,\n group: labels.groupList,\n icon: '1.',\n aliases: ['youxu', '有序', '列表', 'ordered', 'ol'],\n order: 5,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleOrderedList().run();\n },\n },\n {\n id: 'task-list',\n title: labels.taskList,\n group: labels.groupList,\n icon: '✓',\n aliases: ['renwu', '任务', '待办', 'task', 'todo', 'checklist'],\n order: 6,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleTaskList().run();\n },\n },\n {\n id: 'blockquote',\n title: labels.blockquote,\n group: labels.groupAdvanced,\n icon: '❝',\n aliases: ['yinyong', '引用', 'quote', 'blockquote'],\n order: 7,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleBlockquote().run();\n },\n },\n {\n id: 'details',\n title: labels.details,\n group: labels.groupAdvanced,\n icon: '▸',\n aliases: ['zhedie', '折叠', 'toggle', 'details', 'collapse'],\n order: 8,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).setDetails().run();\n },\n },\n {\n id: 'code-block',\n title: labels.codeBlock,\n group: labels.groupAdvanced,\n icon: '{ }',\n aliases: ['daima', '代码', 'code', 'fence'],\n order: 8,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).toggleCodeBlock().run();\n },\n },\n {\n id: 'divider',\n title: labels.divider,\n group: labels.groupAdvanced,\n icon: '—',\n aliases: ['fenge', '分割线', '分隔线', 'hr', 'divider'],\n order: 9,\n apply: (editor, range) => {\n editor.chain().focus().deleteRange(range).setHorizontalRule().run();\n },\n },\n {\n id: 'table',\n title: labels.table,\n group: labels.groupAdvanced,\n icon: '▦',\n aliases: ['biaoge', '表格', 'table'],\n order: 10,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertTable({ rows: 3, cols: 3, withHeaderRow: true })\n .run();\n },\n },\n {\n id: 'inline-math',\n title: labels.inlineMath,\n group: labels.groupAdvanced,\n icon: '√x',\n aliases: ['gongshi', '公式', '行内公式', 'math', 'latex'],\n order: 11,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({ type: 'inlineMath', attrs: { latex: 'E=mc^2' } })\n .run();\n },\n },\n {\n id: 'block-math',\n title: labels.blockMath,\n group: labels.groupAdvanced,\n icon: '∑',\n aliases: ['gongshi', '公式', '块级公式', 'mathblock', 'latex'],\n order: 12,\n apply: (editor, range) => {\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({ type: 'blockMath', attrs: { latex: 'E=mc^2' } })\n .run();\n },\n },\n ];\n}\n\n/** 按 title / aliases 做小写 includes 过滤;空 query 返回全部。 */\nexport function filterSlashItems(\n items: SlashMenuItem[],\n query: string,\n): SlashMenuItem[] {\n const q = query.trim().toLowerCase();\n if (!q) return items;\n return items.filter(\n (item) =>\n item.title.toLowerCase().includes(q) ||\n item.aliases?.some((alias) => alias.toLowerCase().includes(q)),\n );\n}\n","import {\n Fragment,\n forwardRef,\n useEffect,\n useImperativeHandle,\n useState,\n type ReactNode,\n} from 'react';\nimport styles from '../styles/slashMenu.module.css';\nimport type { SlashMenuItem } from './types';\n\nexport interface SlashMenuPopupHandle {\n /** 编辑器键盘事件经 IME 守卫后委托进来;返回 true 表示已消费。 */\n onKeyDown: (event: KeyboardEvent) => boolean;\n}\n\nexport interface SlashMenuPopupProps {\n items: SlashMenuItem[];\n query: string;\n menuLabel: string;\n command: (item: SlashMenuItem) => void;\n}\n\n/**\n * 把有序列表切成「相邻同名组」的节:返回 [组名 | null, 项][]。\n * 组首项的行携带组名(渲染为节标题 + 该项),其余行组名为 null。\n */\nfunction withGroupHeaders(\n items: SlashMenuItem[],\n): Array<[ReactNode, SlashMenuItem]> {\n const rows: Array<[ReactNode, SlashMenuItem]> = [];\n let lastGroup: string | undefined;\n for (const item of items) {\n if (item.group && item.group !== lastGroup) {\n rows.push([item.group, item]);\n lastGroup = item.group;\n } else {\n if (!item.group) lastGroup = undefined;\n rows.push([null, item]);\n }\n }\n return rows;\n}\n\n/**\n * 斜杠菜单弹层。样式对齐工具栏下拉菜单(radix 面板 + 文字字形图标 +\n * 品牌色高亮);定位由 @tiptap/suggestion 的 mount(floating-ui)接管。\n * 零匹配时渲染 null:弹层隐藏但 suggestion 保持激活,query 恢复命中时\n * 重新出现。\n */\nexport const SlashMenuPopup = forwardRef<\n SlashMenuPopupHandle,\n SlashMenuPopupProps\n>(function SlashMenuPopup({ items, query, menuLabel, command }, ref) {\n const [index, setIndex] = useState(0);\n\n // query 变化(过滤)后选区回到第一项\n useEffect(() => {\n setIndex(0);\n }, [query]);\n\n useImperativeHandle(\n ref,\n () => ({\n onKeyDown(event) {\n if (!items.length) return false;\n if (event.key === 'ArrowDown' || (event.key === 'Tab' && !event.shiftKey)) {\n setIndex((i) => (i + 1) % items.length);\n return true;\n }\n if (event.key === 'ArrowUp' || (event.key === 'Tab' && event.shiftKey)) {\n setIndex((i) => (i - 1 + items.length) % items.length);\n return true;\n }\n if (event.key === 'Enter') {\n const item = items[index];\n if (!item) return false;\n command(item);\n return true;\n }\n return false;\n },\n }),\n [items, index, command],\n );\n\n if (!items.length) return null;\n\n // 键盘索引按「可选项」计;分组头不占位\n let optionIndex = -1;\n\n return (\n <div className={styles.menu} role=\"listbox\" aria-label={menuLabel}>\n {withGroupHeaders(items).map(([header, item]) => {\n optionIndex += 1;\n const i = optionIndex;\n return (\n <Fragment key={item.id}>\n {header !== null ? (\n <div className={styles.groupLabel} aria-hidden=\"true\">\n {header}\n </div>\n ) : null}\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={i === index}\n title={item.title}\n className={i === index ? styles.itemActive : styles.item}\n // 键盘焦点留在编辑器里,↑↓/Enter 才能继续驱动弹层\n tabIndex={-1}\n onMouseEnter={() => setIndex(i)}\n onMouseDown={(e) => {\n // 用 mousedown 抢在编辑器 blur 之前执行,避免选区先被抖掉\n e.preventDefault();\n command(item);\n }}\n >\n {item.icon != null ? (\n <span className={styles.itemIcon} aria-hidden=\"true\">\n {item.icon}\n </span>\n ) : (\n <span className={styles.itemIcon} aria-hidden=\"true\" />\n )}\n <span className={styles.itemTitle}>{item.title}</span>\n </button>\n </Fragment>\n );\n })}\n </div>\n );\n});\n","import { Extension } from '@tiptap/core';\nimport { ReactRenderer } from '@tiptap/react';\nimport Suggestion, {\n type SuggestionKeyDownProps,\n type SuggestionProps,\n} from '@tiptap/suggestion';\nimport { PluginKey } from '@tiptap/pm/state';\nimport { getEditorBundle, getEditorLocale } from '../i18n/store';\nimport styles from '../styles/slashMenu.module.css';\nimport { createDefaultSlashMenuItems, filterSlashItems } from './items';\nimport { SlashMenuPopup, type SlashMenuPopupHandle } from './SlashMenuPopup';\nimport type { SlashMenuItem } from './types';\n\nexport interface SlashMenuOptions {\n /** 追加的宿主自定义命令项(排在默认项之后)。 */\n items?: SlashMenuItem[];\n}\n\n/** 输入法组合态下的按键交给 IME 自己处理(上游 suggestion 不查 composing)。 */\nfunction isComposing(event: KeyboardEvent, view: { composing: boolean }): boolean {\n return view.composing || event.isComposing || event.keyCode === 229;\n}\n\n/**\n * 斜杠菜单:键入 `/` 唤起块级插入弹窗。\n *\n * - 触发规则:行首或空白后(`allowedPrefixes`,空白含全角 U+3000);\n * 正文里「和/或」这类合法斜杠不会触发。\n * - 代码块内不触发;表格单元格内触发(见 allow)。\n * - 定位/滚动跟随/点外关闭全走官方 `props.mount`(floating-ui),\n * 弹层挂 document.body(逃出宿主 overflow 裁剪),`--tmr-*` 主题\n * 变量从编辑器解析后复制到弹层宿主。\n * - 只读态由 suggestion 状态机自身拦截(isEditable 短路)。\n */\nexport const SlashMenu = Extension.create<SlashMenuOptions>({\n name: 'slashMenu',\n\n addOptions() {\n return { items: undefined };\n },\n\n addProseMirrorPlugins() {\n const { items: extraItems } = this.options;\n\n let renderer: ReactRenderer<SlashMenuPopupHandle> | null = null;\n let unmountPopup: (() => void) | null = null;\n\n return [\n Suggestion<SlashMenuItem>({\n editor: this.editor,\n pluginKey: new PluginKey('tmrSlashMenu'),\n char: '/',\n // 行首(节点内起始,空 prefix 天然放行)或空白后触发\n allowedPrefixes: [' ', '\\t', '\\u3000'],\n allowSpaces: false,\n decorationClass: 'tmr-slash-suggestion',\n decorationEmptyClass: 'tmr-slash-suggestion-empty',\n items: ({ editor, query }) =>\n filterSlashItems(\n [\n ...createDefaultSlashMenuItems(getEditorLocale(editor)),\n ...(extraItems ?? []),\n ],\n query,\n ),\n command: ({ editor, range, props: item }) => {\n item.apply(editor, range);\n },\n allow: ({ editor }) => editor.isEditable && !editor.isActive('codeBlock'),\n render: () => ({\n onStart: (props: SuggestionProps<SlashMenuItem>) => {\n renderer = new ReactRenderer(\n SlashMenuPopup,\n {\n editor: props.editor,\n props: {\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).slashMenu.menuLabel,\n command: props.command,\n },\n className: styles.popupHost,\n },\n );\n // 挂到 body(suggestion 默认容器):弹层要能浮出编辑器,宿主在\n // 编辑器外层套 overflow:hidden(圆角窗口/滚动容器)时不会被裁。\n // 主题变量随行:编辑器子树上的 --tmr-* 解析值复制到弹层宿主,\n // 宿主把变量定义在任意深度的子树里也能保持配色一致。\n const pmStyle = getComputedStyle(props.editor.view.dom);\n for (const name of [\n '--tmr-text',\n '--tmr-accent',\n '--tmr-toolbar-border',\n '--tmr-toolbar-muted',\n ]) {\n const value = pmStyle.getPropertyValue(name);\n if (value) renderer.element.style.setProperty(name, value);\n }\n unmountPopup = props.mount(renderer.element);\n },\n onUpdate: (props: SuggestionProps<SlashMenuItem>) => {\n renderer?.updateProps({\n items: props.items,\n query: props.query,\n menuLabel: getEditorBundle(props.editor).slashMenu.menuLabel,\n command: props.command,\n });\n },\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (!renderer || isComposing(props.event, props.view)) return false;\n return renderer.ref?.onKeyDown(props.event) ?? false;\n },\n onExit: () => {\n unmountPopup?.();\n unmountPopup = null;\n renderer?.destroy();\n renderer = null;\n },\n }),\n }),\n ];\n },\n});\n","import { getLocaleBundle, type LocaleCode } from '../i18n';\n\n/**\n * 快捷键面板数据模型。keys 里的 'Mod' / 'Alt' 按平台渲染\n * (macOS: Cmd / Opt;其他: Ctrl / Alt)。\n * markdown.terminator 为触发终止键;无 terminator 的包裹型语法\n * (如 **x**)在键入收尾字符时即触发。\n */\nexport interface ShortcutEntry {\n id: string;\n /** 展示名(已本地化)。 */\n label: string;\n /** 键帽序列;不传表示无默认键位。 */\n keys?: string[];\n /** Markdown 触发写法;不传表示无对应 input rule。 */\n markdown?: {\n /** 触发片段,如 '**x**' 或 '#'。 */\n marker: string;\n /** 终止键(如 Space / Enter)。 */\n terminator?: string;\n };\n}\n\nexport interface ShortcutGroup {\n id: string;\n title: string;\n entries: ShortcutEntry[];\n}\n\n/**\n * 快捷键注册表。**每一条都必须真实**:键位来自所装 Tiptap 扩展的\n * addKeyboardShortcuts 默认值,markdown 触发来自对应 input rule\n * (上标/下标/链接在本库无默认键位与 input rule,故不列)。\n */\nexport function buildShortcutGroups(locale: LocaleCode = 'en'): ShortcutGroup[] {\n const labels = getLocaleBundle(locale).shortcut;\n return [\n {\n id: 'format',\n title: labels.formatGroup,\n entries: [\n { id: 'bold', label: labels.bold, keys: ['Mod', 'b'], markdown: { marker: '**x**' } },\n { id: 'italic', label: labels.italic, keys: ['Mod', 'i'], markdown: { marker: '*x*' } },\n { id: 'strike', label: labels.strike, keys: ['Mod', 'Shift', 's'], markdown: { marker: '~~x~~' } },\n { id: 'underline', label: labels.underline, keys: ['Mod', 'u'] },\n { id: 'inline-code', label: labels.inlineCode, keys: ['Mod', 'e'], markdown: { marker: '`x`' } },\n { id: 'highlight', label: labels.highlight, keys: ['Mod', 'Shift', 'h'], markdown: { marker: '==x==' } },\n { id: 'smart-quote', label: labels.smartQuote, markdown: { marker: '\"' } },\n { id: 'ellipsis', label: labels.ellipsis, markdown: { marker: '...' } },\n { id: 'em-dash', label: labels.emDash, markdown: { marker: '--' } },\n { id: 'align-left', label: labels.alignLeft, keys: ['Mod', 'Shift', 'l'] },\n { id: 'align-center', label: labels.alignCenter, keys: ['Mod', 'Shift', 'e'] },\n { id: 'align-right', label: labels.alignRight, keys: ['Mod', 'Shift', 'r'] },\n { id: 'align-justify', label: labels.alignJustify, keys: ['Mod', 'Shift', 'j'] },\n ...([1, 2, 3, 4, 5, 6] as const).map((level): ShortcutEntry => ({\n id: `heading-${level}`,\n label: labels.headingLabel(level),\n keys: ['Mod', 'Alt', String(level)],\n markdown: { marker: '#'.repeat(level), terminator: 'Space' },\n })),\n { id: 'paragraph', label: labels.paragraph, keys: ['Mod', 'Alt', '0'] },\n ],\n },\n {\n id: 'insert',\n title: labels.insertGroup,\n entries: [\n { id: 'image-upload', label: labels.imageUpload, keys: ['Mod', 'Shift', 'i'] },\n { id: 'blockquote', label: labels.blockquote, keys: ['Mod', 'Shift', 'b'], markdown: { marker: '>', terminator: 'Space' } },\n { id: 'bullet-list', label: labels.bulletList, keys: ['Mod', 'Shift', '8'], markdown: { marker: '-', terminator: 'Space' } },\n { id: 'ordered-list', label: labels.orderedList, keys: ['Mod', 'Shift', '7'], markdown: { marker: '1.', terminator: 'Space' } },\n { id: 'task-list', label: labels.taskList, markdown: { marker: '[]', terminator: 'Space' } },\n { id: 'code-block', label: labels.codeBlock, markdown: { marker: '```', terminator: 'Space' } },\n { id: 'divider', label: labels.divider, markdown: { marker: '---', terminator: 'Space' } },\n { id: 'emoji', label: labels.emoji, markdown: { marker: ':smile:' } },\n ],\n },\n {\n id: 'edit',\n title: labels.editGroup,\n entries: [\n { id: 'undo', label: labels.undo, keys: ['Mod', 'z'] },\n { id: 'redo', label: labels.redo, keys: ['Mod', 'Shift', 'z'] },\n { id: 'hard-break', label: labels.hardBreak, keys: ['Shift', 'Enter'] },\n { id: 'find-replace', label: labels.findReplace, keys: ['Mod', 'f'] },\n ],\n },\n ];\n}\n","import { useEffect, useMemo } from 'react';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport { XIcon } from '../icons';\nimport styles from '../styles/shortcutPanel.module.css';\nimport { buildShortcutGroups, type ShortcutGroup } from './types';\n\nexport interface ShortcutPanelProps {\n open: boolean;\n onClose: () => void;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n}\n\nfunction isMacPlatform(): boolean {\n if (typeof navigator === 'undefined') return false;\n return /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);\n}\n\n/** 键帽文案:Mod/Alt 按平台落名,单字母统一大写。 */\nfunction keycapText(key: string, mac: boolean): string {\n if (key === 'Mod') return mac ? 'Cmd' : 'Ctrl';\n if (key === 'Alt') return mac ? 'Opt' : 'Alt';\n if (/^[a-z]$/.test(key)) return key.toUpperCase();\n return key;\n}\n\n/**\n * 快捷键抽屉(语雀式):右侧滑出的三列对照表——格式 / 快捷键 / Markdown。\n * 数据来自 buildShortcutGroups(键位与 input rule 均为库内真实行为)。\n * 定位 fixed 贴视口右缘;Esc 关闭。\n */\nexport function ShortcutPanel({\n open,\n onClose,\n locale = 'en',\n}: ShortcutPanelProps) {\n const t = getLocaleBundle(locale).shortcut;\n\n // 打开期间 Esc 关闭\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', onKey);\n return () => document.removeEventListener('keydown', onKey);\n }, [open, onClose]);\n\n const groups: ShortcutGroup[] = useMemo(\n () => (open ? buildShortcutGroups(locale) : []),\n [open, locale],\n );\n const mac = useMemo(isMacPlatform, []);\n\n if (!open) return null;\n\n return (\n <aside\n className={styles.drawer}\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label={t.panelTitle}\n >\n <button\n type=\"button\"\n className={styles.closeBtn}\n title={t.close}\n aria-label={t.close}\n onClick={onClose}\n >\n <XIcon size={16} />\n </button>\n <div className={styles.head}>\n <span className={styles.colFormat}>{t.colFormat}</span>\n <span className={styles.colShortcut}>{t.colShortcut}</span>\n <span className={styles.colMarkdown}>{t.colMarkdown}</span>\n </div>\n <div className={styles.body}>\n {groups.map((group) => (\n <section key={group.id} className={styles.group}>\n <h3 className={styles.groupTitle}>{group.title}</h3>\n {group.entries.map((entry) => (\n <div key={entry.id} className={styles.row}>\n <span className={styles.rowLabel}>{entry.label}</span>\n <span className={styles.rowKeys}>\n {entry.keys ? (\n entry.keys.map((key, i) => (\n <kbd key={i} className={styles.keycap}>\n {keycapText(key, mac)}\n </kbd>\n ))\n ) : (\n <span className={styles.none}>—</span>\n )}\n </span>\n <span className={styles.rowMd}>\n {entry.markdown ? (\n <>\n <kbd className={styles.keycap}>{entry.markdown.marker}</kbd>\n {entry.markdown.terminator ? (\n <kbd className={styles.keycap}>{entry.markdown.terminator}</kbd>\n ) : null}\n </>\n ) : (\n <span className={styles.none}>—</span>\n )}\n </span>\n </div>\n ))}\n </section>\n ))}\n </div>\n </aside>\n );\n}\n","// 副作用:增强 Editor.markdown(MarkdownManager)的类型。\nimport '@tiptap/markdown';\nimport { Extension, type Editor } from '@tiptap/core';\nimport { FileHandlePlugin } from '@tiptap/extension-file-handler';\nimport { PluginKey } from '@tiptap/pm/state';\n\n/** .md / .markdown 扩展名(不区分大小写)。 */\nconst MD_FILE_RE = /\\.(md|markdown)$/i;\n\n/**\n * .md 的 MIME 类型在各平台不稳定(text/markdown / text/x-markdown / 空串),\n * 而 FileHandlePlugin 的 allowedMimeTypes 是严格 `includes(file.type)` 匹配\n * (源码确认,不支持扩展名)。故列表包含空串兜住「系统未注册 MIME」的 .md,\n * 真正的判定在回调里按文件名做。副作用:拖入其它空 MIME 文件会被吞成 no-op\n * ——好过浏览器默认导航打开该文件。\n */\nconst MD_MIME_TYPES = ['text/markdown', 'text/x-markdown', ''];\n\n/** 读 .md 文件文本 → markdown.parse → 插入(drop 时插到落点,paste 时插到光标)。 */\nasync function insertMarkdownFiles(\n editor: Editor,\n files: File[],\n pos?: number,\n): Promise<void> {\n const mdFiles = files.filter((f) => MD_FILE_RE.test(f.name));\n if (mdFiles.length === 0) return;\n // markdown manager 仅在注册了 @tiptap/markdown 扩展后存在\n const markdown = editor.markdown;\n if (!markdown) return;\n const texts = await Promise.all(mdFiles.map((f) => f.text()));\n const json = markdown.parse(texts.join('\\n\\n'));\n if (pos != null) {\n editor.chain().focus().insertContentAt(pos, json).run();\n } else {\n editor.chain().focus().insertContent(json).run();\n }\n}\n\n/**\n * 把 .md / .markdown 文件拖拽或粘贴进编辑器,解析后插入内容。\n * 复用官方 @tiptap/extension-file-handler 的底层 FileHandlePlugin,但注册在\n * 自有 PluginKey 下——宿主仍可通过 extraExtensions 注入自己的 FileHandler\n * (如图片上传)而不撞 key。\n */\nexport const MarkdownFileDrop = Extension.create({\n name: 'markdownFileDrop',\n\n addProseMirrorPlugins() {\n const { editor } = this;\n return [\n FileHandlePlugin({\n key: new PluginKey('markdownFileDrop'),\n editor,\n allowedMimeTypes: MD_MIME_TYPES,\n onDrop: (ed, files, pos) => {\n insertMarkdownFiles(ed, files, pos).catch((err) => {\n console.error('markdown file drop failed:', err);\n });\n },\n onPaste: (ed, files) => {\n insertMarkdownFiles(ed, files).catch((err) => {\n console.error('markdown file paste failed:', err);\n });\n },\n }),\n ];\n },\n});\n","import { Extension } from '@tiptap/core';\nimport { skipTrailingNodeMeta } from '@tiptap/extensions';\nimport type { Node } from '@tiptap/pm/model';\nimport { Plugin, PluginKey, type EditorState } from '@tiptap/pm/state';\nimport { Decoration, DecorationSet } from '@tiptap/pm/view';\nimport type { Editor } from '@tiptap/react';\n\n/**\n * 「这段正在被 AI 改写」的高亮。\n *\n * 用 **decoration** 而不是 mark:两者都不会漏进 markdown,都能盖住表格,但 decoration\n * 不需要往 schema 里加标记、也不参与 undo 历史,而且**原子节点(图表 / 图片)也能盖**\n * ——mark 只作用于行内内容,盖不住 `atom: true` 的块级节点。\n *\n * 状态由宿主通过 `pendingAnchors` 推入(提交圈选改写时给上,回填或冲突时清空)。\n * 文档被编辑时装饰会跟着映射(在原文中间插字,高亮跟着变长;在前面插,整体后移)。\n */\nexport interface PendingAnchor {\n /** 宿主自己的标识(例如一次圈选请求的 request_id)。 */\n id: string;\n /** doc 坐标区间;通常是与选区相交的顶层块的首尾(见 getMarkdownForRange)。 */\n ranges: Array<{ from: number; to: number }>;\n}\n\nexport interface PendingAnchorState {\n decorations: DecorationSet;\n}\n\n/** 用 transaction meta 推入锚点:`tr.setMeta(PENDING_ANCHOR_META, anchors)`。 */\nexport const PENDING_ANCHOR_META = 'tmr:pendingAnchor';\n\nexport const pendingAnchorPluginKey = new PluginKey<PendingAnchorState>(\n 'tmrPendingAnchor',\n);\n\n/** 装饰上的 class;点击命中时宿主用 `[data-pending-id]` 取回 id。 */\nexport const PENDING_ANCHOR_CLASS = 'tmr-pending-anchor';\n\nexport const pendingAnchorExtension = Extension.create({\n name: 'pendingAnchor',\n\n addProseMirrorPlugins() {\n return [\n new Plugin<PendingAnchorState>({\n key: pendingAnchorPluginKey,\n state: {\n init: () => ({ decorations: DecorationSet.empty }),\n apply(tr, value) {\n const next = tr.getMeta(PENDING_ANCHOR_META) as\n | PendingAnchor[]\n | undefined;\n if (next) return { decorations: buildDecorations(tr.doc, next) };\n if (tr.docChanged) {\n return { decorations: value.decorations.map(tr.mapping, tr.doc) };\n }\n return value;\n },\n },\n props: {\n decorations(state: EditorState) {\n return pendingAnchorPluginKey.getState(state)?.decorations ?? null;\n },\n },\n }),\n ];\n },\n});\n\n/** 更新待改写高亮(传空数组即清空)。不进 undo 历史。 */\nexport function setPendingAnchors(editor: Editor, anchors: PendingAnchor[]): void {\n editor.view.dispatch(\n editor.state.tr\n .setMeta(PENDING_ANCHOR_META, anchors)\n // Tiptap 的 TrailingNode(StarterKit 自带)会在「第一个 transaction」时给文档末尾补一个\n // 空段落——只要文末是图表 / 表格 / 代码块这类节点。那是它本来的行为,但**不该由一次\n // 「加个高亮」触发**:那等于用户什么都没干、正文却多了个空段落(落库就变成结尾的 \\n\\n)。\n // 用官方 meta 跳过本次。\n .setMeta(skipTrailingNodeMeta, true)\n .setMeta('addToHistory', false),\n );\n}\n\nfunction buildDecorations(doc: Node, anchors: PendingAnchor[]): DecorationSet {\n const bounds: Array<{ from: number; to: number }> = [];\n doc.forEach((child, offset) => {\n bounds.push({ from: offset, to: offset + child.nodeSize });\n });\n\n const decorations: Decoration[] = [];\n for (const anchor of anchors) {\n for (const range of anchor.ranges) {\n const from = Math.max(0, Math.min(range.from, range.to));\n const to = Math.min(doc.content.size, Math.max(range.from, range.to));\n if (from >= to) continue;\n\n for (const bound of bounds) {\n if (bound.to <= from || bound.from >= to) continue;\n const attrs = {\n class: PENDING_ANCHOR_CLASS,\n 'data-pending-id': anchor.id,\n };\n if (from <= bound.from && to >= bound.to) {\n // 整块:用 node 装饰,表格 / 图表这类块级节点才盖得住\n decorations.push(Decoration.node(bound.from, bound.to, attrs));\n } else {\n // 块内的一部分(未扩块的选区):行内高亮\n decorations.push(\n Decoration.inline(Math.max(from, bound.from), Math.min(to, bound.to), attrs),\n );\n }\n }\n }\n }\n\n return decorations.length ? DecorationSet.create(doc, decorations) : DecorationSet.empty;\n}\n","import type { Selection } from '@tiptap/pm/state';\nimport { NodeSelection, TextSelection } from '@tiptap/pm/state';\nimport { CellSelection } from '@tiptap/pm/tables';\n\n/**\n * 选区类型——给宿主判断「用户选的是什么」用。\n *\n * - `text`:普通文字选区(含 AllSelection)\n * - `cell`:表格里的单元格选区(`CellSelection`,拖过表格时就是它)\n * - `node`:整节点选区(`NodeSelection`)。图表 / 图片 / 代码块这类 `atom` 节点只能这样被选中\n * ——它们在编辑器里渲染成图或块,用户没法用拖选文字的方式选中\n */\nexport type SelectionKind = 'text' | 'cell' | 'node';\n\nexport function selectionKind(selection: Selection): SelectionKind {\n if (selection instanceof CellSelection) return 'cell';\n if (selection instanceof NodeSelection) return 'node';\n if (selection instanceof TextSelection) return 'text';\n return 'text';\n}\n","// 副作用:增强 Editor.markdown(MarkdownManager)的类型。\nimport '@tiptap/markdown';\nimport { Extension } from '@tiptap/core';\nimport { Plugin, PluginKey } from '@tiptap/pm/state';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\n\n/**\n * 启发式判断一段纯文本是否「像 markdown」。\n * 只要命中任一块级/行内特征即认为是——宁可偶尔把带星号的普通文本转掉,\n * 也不漏掉真 markdown(用户可用 Shift+粘贴逃生)。\n */\nexport function looksLikeMarkdown(text: string): boolean {\n return (\n /^#{1,6}\\s/m.test(text) || // 标题\n /\\*\\*[^*\\n]+\\*\\*/.test(text) || // 加粗\n /(^|[^!])\\[[^\\]\\n]+\\]\\([^)\\n]+\\)/.test(text) || // 链接\n /!\\[[^\\]\\n]*\\]\\([^)\\n]+\\)/.test(text) || // 图片\n /^[-*+]\\s+\\S/m.test(text) || // 无序列表\n /^\\d+\\.\\s+\\S/m.test(text) || // 有序列表\n /^>\\s/m.test(text) || // 引用\n /^```/m.test(text) || // 代码围栏\n /^\\|.+\\|\\s*$/m.test(text) || // 表格行\n /^(-{3,}|\\*{3,})\\s*$/m.test(text) || // 分割线\n /<!--\\s*[\\[{][\\s\\S]*?\"chartType\"[\\s\\S]*?-->/.test(text) // 图表注释\n );\n}\n\n/**\n * 粘贴 markdown 纯文本自动转富文本(官方 @tiptap/markdown 不内置此能力,\n * 实现方式取自官方文档 \"Paste Markdown Detection\" 配方并调整边界)。\n *\n * 判定只看 text/plain 的内容,不看剪贴板是否带 text/html——很多来源\n * (Xcode/VS Code 等编辑器经 RTF/语法高亮 HTML)复制 markdown 源码时会\n * 附带一份仅是\"字面文本+样式\"的 HTML,按 HTML 标签判定来源不可枚举。\n * 而真富文本(Word/网页文章)的 text/plain 降级不含 markdown 符号,\n * 启发式天然不命中,仍走默认富文本粘贴。\n *\n * 放行(不转换)的边界:\n * - Shift+粘贴(粘贴为纯文本)→ 保留逃生通道\n * - 光标在代码块内 → markdown 源码原样贴入\n * - looksLikeMarkdown 未命中 → 默认粘贴\n */\nexport const MarkdownPaste = Extension.create({\n name: 'markdownPaste',\n\n addProseMirrorPlugins() {\n const { editor } = this;\n return [\n new Plugin({\n key: new PluginKey('markdownPaste'),\n props: {\n handlePaste(view, event) {\n const clipboard = event.clipboardData;\n if (!clipboard) return false;\n const text = clipboard.getData('text/plain');\n if (!text) return false;\n // Shift+粘贴 = 用户显式要纯文本(shiftKey 是 prosemirror-view\n // 内部状态,无公开 API,容错访问;拿不到则照常转换)\n const input = (view as unknown as { input?: { shiftKey?: boolean } })\n .input;\n if (input?.shiftKey) return false;\n // 代码块内粘贴 markdown 源码是常见操作,不转换\n if (view.state.selection.$from.parent.type.spec.code) return false;\n if (!looksLikeMarkdown(text)) return false;\n // markdown manager 仅在注册了 @tiptap/markdown 扩展后存在\n const markdown = editor.markdown;\n if (!markdown) return false;\n try {\n // 与 insertMarkdown / 初始 content 一致:先把\n // `<!-- chartType -->` + GFM 表收成 ```tmr-chart,否则\n // TipTap 会丢掉 HTML 注释,只剩普通表格。\n const json = markdown.parse(prepareChartMarkdown(text));\n editor.commands.insertContent(json);\n return true;\n } catch (err) {\n console.error('markdown paste parse failed:', err);\n return false; // 解析失败回退默认纯文本粘贴\n }\n },\n },\n }),\n ];\n },\n});\n","'use client';\n\nimport type { Chart as ChartJs } from 'chart.js';\nimport {\n NodeViewWrapper,\n type NodeViewProps,\n} from '@tiptap/react';\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type MouseEvent,\n} from 'react';\nimport { destroyChart, renderOrUpdateChart } from '../chart/renderChart';\nimport type { ChartPayload, ChartTheme } from '../chart/types';\nimport { useEditorBundle } from '../i18n/store';\nimport '../styles/chart.css';\n\nfunction tabLabel(\n chartType: string,\n title: string | undefined,\n index: number,\n fallback: (type: string, i: number) => string,\n): string {\n if (title?.trim()) return title;\n return fallback(chartType, index);\n}\n\n/**\n * Chart NodeView: render + NodeSelection (like Image).\n * Config editing is intentionally disabled for now (no modal).\n */\nexport function ChartView({\n node,\n editor,\n extension,\n selected,\n getPos,\n}: NodeViewProps) {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const chartRef = useRef<ChartJs | null>(null);\n const [active, setActive] = useState(0);\n const labels = useEditorBundle(editor).chart;\n\n const payload: ChartPayload = useMemo(\n () => ({\n config: (node.attrs.config ?? []) as ChartPayload['config'],\n columns: (node.attrs.columns ?? []) as ChartPayload['columns'],\n dataSource: (node.attrs.dataSource ??\n []) as ChartPayload['dataSource'],\n }),\n [node.attrs.config, node.attrs.columns, node.attrs.dataSource],\n );\n\n const theme = (extension.options.chartTheme ?? null) as ChartTheme | null;\n const configs = payload.config;\n const safeIndex = Math.min(active, Math.max(0, configs.length - 1));\n const config = configs[safeIndex];\n const height = config?.height ?? 320;\n const canSelect = Boolean(editor?.isEditable);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas || !config) return;\n chartRef.current = renderOrUpdateChart({\n canvas,\n config,\n payload,\n theme,\n chart: chartRef.current,\n });\n }, [config, payload, theme]);\n\n useEffect(() => {\n return () => {\n destroyChart(chartRef.current);\n chartRef.current = null;\n };\n }, []);\n\n // When active tab changes, recreate chart (type may differ)\n useEffect(() => {\n destroyChart(chartRef.current);\n chartRef.current = null;\n const canvas = canvasRef.current;\n if (!canvas || !config) return;\n chartRef.current = renderOrUpdateChart({\n canvas,\n config,\n payload,\n theme,\n chart: null,\n });\n }, [safeIndex]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const selectChart = () => {\n if (!editor || !canSelect) return;\n const pos = getPos();\n if (typeof pos !== 'number') return;\n editor.commands.setNodeSelection(pos);\n };\n\n return (\n <NodeViewWrapper\n as=\"div\"\n className={`tmr-chart${canSelect ? ' tmr-chart--interactive' : ''}${\n selected ? ' ProseMirror-selectednode' : ''\n }`}\n data-type=\"chart\"\n data-selected={selected ? 'true' : undefined}\n contentEditable={false}\n onMouseDown={(e: MouseEvent) => {\n if ((e.target as HTMLElement).closest('button')) return;\n if (e.button !== 0) return;\n // Select node but do not preventDefault — Chart.js needs hover/click.\n selectChart();\n }}\n >\n {config?.title ? (\n <div className=\"tmr-chart-title\">{config.title}</div>\n ) : null}\n {config?.dataTime ? (\n <div className=\"tmr-chart-time\">{config.dataTime}</div>\n ) : null}\n {configs.length > 1 ? (\n <div className=\"tmr-chart-tabs\" role=\"tablist\">\n {configs.map((c, i) => (\n <button\n key={`${c.chartType}-${i}`}\n type=\"button\"\n role=\"tab\"\n className=\"tmr-chart-tab\"\n data-active={i === safeIndex ? 'true' : 'false'}\n aria-selected={i === safeIndex}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => {\n e.stopPropagation();\n setActive(i);\n }}\n >\n {tabLabel(c.chartType, c.title, i, labels.tabLabel)}\n </button>\n ))}\n </div>\n ) : null}\n <div className=\"tmr-chart-canvas-host\" style={{ height }}>\n <canvas ref={canvasRef} />\n </div>\n </NodeViewWrapper>\n );\n}\n","import { ReactNodeViewRenderer } from '@tiptap/react';\nimport { reportChart, type ReportChartOptions } from './ChartExtension';\nimport { ChartView } from '../components/ChartView';\nimport type { ChartTheme } from './types';\n\nexport interface CreateChartOptions {\n /** When true, reserved for future in-place chart editing. Currently unused (no edit UI). Default false. */\n editable?: boolean;\n chartTheme?: ChartTheme | null;\n}\n\nexport interface InteractiveChartOptions extends ReportChartOptions {\n chartEditable: boolean;\n chartTheme: ChartTheme | null;\n}\n\n/**\n * Client chart extension with React NodeView.\n * Server / pure HTML paths should use {@link reportChart} / {@link pureChart}.\n */\nexport function createChart(options: CreateChartOptions = {}) {\n // Chart config editing (modal) is disabled for now; keep option for later.\n const { editable = false, chartTheme = null } = options;\n return reportChart.extend<InteractiveChartOptions>({\n addOptions() {\n return {\n HTMLAttributes: {},\n chartEditable: editable,\n chartTheme,\n };\n },\n addNodeView() {\n return ReactNodeViewRenderer(ChartView);\n },\n });\n}\n","'use client';\n\nimport * as DropdownMenu from '@radix-ui/react-dropdown-menu';\nimport {\n NodeViewContent,\n NodeViewWrapper,\n type NodeViewProps,\n} from '@tiptap/react';\nimport { useEffect, useRef, useState } from 'react';\nimport {\n CheckIcon,\n ChevronDownIcon,\n ChevronsUpDownIcon,\n CopyIcon,\n TrashIcon,\n} from '../icons';\nimport { useEditorBundle } from '../i18n/store';\nimport styles from '../styles/codeBlock.module.css';\n\n// 语言选择可选项。空 value = Auto-detect(交给 lowlight 自动识别)\nconst LANGUAGES: { value: string; label: string }[] = [\n { value: '', label: 'Auto-detect' },\n { value: 'javascript', label: 'JavaScript' },\n { value: 'typescript', label: 'TypeScript' },\n { value: 'python', label: 'Python' },\n { value: 'java', label: 'Java' },\n { value: 'go', label: 'Go' },\n { value: 'rust', label: 'Rust' },\n { value: 'c', label: 'C' },\n { value: 'cpp', label: 'C++' },\n { value: 'csharp', label: 'C#' },\n { value: 'sql', label: 'SQL' },\n { value: 'json', label: 'JSON' },\n { value: 'yaml', label: 'YAML' },\n { value: 'bash', label: 'Bash' },\n { value: 'shell', label: 'Shell' },\n { value: 'html', label: 'HTML' },\n { value: 'css', label: 'CSS' },\n { value: 'markdown', label: 'Markdown' },\n];\n\n/**\n * 代码块自定义 NodeView:右上角语言选择器(Radix DropdownMenu,支持方向键导航)+ 删除。\n * 文案从编辑器的 locale 读取,缺省英文。\n *\n * 只读态(预览 / 只读编辑器)走另一套头部:左侧语言标签,右侧复制 + 展开收起;\n * 超出 `--tmr-code-max-height`(缺省 480px)的代码块默认限高块内滚动,行号槽\n * 用 MutationObserver 跟踪 PM 管理的 code 内容保持同步(纯文本变化不触发 React\n * 重渲染,gutter 行数靠观察器自刷新)。\n */\nexport function CodeBlockView({\n node,\n updateAttributes,\n deleteNode,\n editor,\n}: NodeViewProps) {\n const labels = useEditorBundle(editor).codeBlock;\n const language = (node.attrs.language as string) || '';\n const current = LANGUAGES.find((l) => l.value === language);\n const currentLabel = current\n ? current.value === ''\n ? labels.autoDetect\n : current.label\n : labels.autoDetect;\n const editable = editor.isEditable;\n const readonly = !editable;\n\n const [copied, setCopied] = useState(false);\n const [folded, setFolded] = useState(false);\n const [lineCount, setLineCount] = useState(() =>\n Math.max((node.textContent ?? '').split('\\n').length, 1),\n );\n const preRef = useRef<HTMLPreElement | null>(null);\n\n // 流式更新只改 code 里的文本节点,React 不重渲染;观察 pre 子树同步行数。\n useEffect(() => {\n if (!readonly) return;\n const pre = preRef.current;\n if (!pre) return;\n const update = () => {\n setLineCount(Math.max((pre.textContent ?? '').split('\\n').length, 1));\n };\n update();\n const mo = new MutationObserver(update);\n mo.observe(pre, { childList: true, subtree: true, characterData: true });\n return () => mo.disconnect();\n }, [readonly]);\n\n const handleCopy = () => {\n const text = node.textContent ?? '';\n const done = () => {\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n };\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(text).then(done, () => {});\n return;\n }\n // 剪贴板 API 不可用(非安全上下文 / 旧 webview):退化为 execCommand\n const ta = document.createElement('textarea');\n ta.value = text;\n document.body.appendChild(ta);\n ta.select();\n try {\n document.execCommand('copy');\n done();\n } catch {\n // 复制失败静默\n }\n ta.remove();\n };\n\n const wrapperClass = [\n styles.wrapper,\n readonly ? styles.readonly : '',\n folded ? styles.folded : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <NodeViewWrapper className={wrapperClass}>\n {editable ? (\n <div className={styles.header} contentEditable={false}>\n <DropdownMenu.Root>\n <DropdownMenu.Trigger asChild>\n <button\n type=\"button\"\n className={styles.langTrigger}\n aria-label={labels.language}\n >\n <span>{currentLabel}</span>\n <ChevronDownIcon size={14} className={styles.langChevron} />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Portal>\n <DropdownMenu.Content\n className={styles.langMenu}\n sideOffset={4}\n align=\"end\"\n >\n {LANGUAGES.map((l) => {\n const optionLabel =\n l.value === '' ? labels.autoDetect : l.label;\n const selected = l.value === language;\n return (\n <DropdownMenu.Item\n key={l.value}\n className={`${styles.langItem}${selected ? ` ${styles.langItemSelected}` : ''}`}\n onSelect={() => updateAttributes({ language: l.value })}\n >\n <span className={styles.langItemCheck}>\n {selected ? <CheckIcon size={14} /> : null}\n </span>\n {optionLabel}\n </DropdownMenu.Item>\n );\n })}\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n <button\n type=\"button\"\n className={styles.deleteBtn}\n title={labels.delete}\n aria-label={labels.delete}\n onClick={() => deleteNode()}\n >\n <TrashIcon />\n </button>\n </div>\n ) : (\n // 只读头部:左侧语言标签,右侧复制 + 展开收起(展开后按钮保留,供收回)\n <div className={styles.readonlyHeader} contentEditable={false}>\n <span className={styles.langBadge}>\n <span className={styles.langBraces}>{'{}'}</span>\n {currentLabel}\n </span>\n <span className={styles.readonlyActions}>\n <button\n type=\"button\"\n className={styles.actionBtn}\n onClick={handleCopy}\n aria-label={labels.copy}\n title={labels.copy}\n >\n {copied ? <CheckIcon size={14} /> : <CopyIcon size={14} />}\n </button>\n <button\n type=\"button\"\n className={styles.actionBtn}\n onClick={() => setFolded((v) => !v)}\n aria-label={folded ? labels.expand : labels.collapse}\n title={folded ? labels.expand : labels.collapse}\n aria-expanded={!folded}\n >\n <ChevronsUpDownIcon size={14} />\n </button>\n </span>\n </div>\n )}\n <pre ref={preRef} className={styles.pre}>\n {readonly ? (\n <span\n className={styles.gutter}\n contentEditable={false}\n aria-hidden=\"true\"\n >\n {Array.from({ length: lineCount }, (_, i) => (\n <span key={i} className={styles.gutterLine}>\n {i + 1}\n </span>\n ))}\n </span>\n ) : null}\n {/* code 标签是代码块标准内容容器;v3 类型未含 'code',运行时有效,故断言 */}\n <NodeViewContent as={'code' as 'div'} />\n </pre>\n </NodeViewWrapper>\n );\n}\n","/**\n * Custom positioning reference element.\n * @see https://floating-ui.com/docs/virtual-elements\n */\n\nconst sides = ['top', 'right', 'bottom', 'left'];\nconst alignments = ['start', 'end'];\nconst placements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + \"-\" + alignments[0], side + \"-\" + alignments[1]), []);\nconst min = Math.min;\nconst max = Math.max;\nconst round = Math.round;\nconst floor = Math.floor;\nconst createCoords = v => ({\n x: v,\n y: v\n});\nconst oppositeSideMap = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nfunction clamp(start, value, end) {\n return max(start, min(value, end));\n}\nfunction evaluate(value, param) {\n return typeof value === 'function' ? value(param) : value;\n}\nfunction getSide(placement) {\n return placement.split('-')[0];\n}\nfunction getAlignment(placement) {\n return placement.split('-')[1];\n}\nfunction getOppositeAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}\nfunction getAxisLength(axis) {\n return axis === 'y' ? 'height' : 'width';\n}\nfunction getSideAxis(placement) {\n const firstChar = placement[0];\n return firstChar === 't' || firstChar === 'b' ? 'y' : 'x';\n}\nfunction getAlignmentAxis(placement) {\n return getOppositeAxis(getSideAxis(placement));\n}\nfunction getAlignmentSides(placement, rects, rtl) {\n if (rtl === void 0) {\n rtl = false;\n }\n const alignment = getAlignment(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const length = getAxisLength(alignmentAxis);\n let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';\n if (rects.reference[length] > rects.floating[length]) {\n mainAlignmentSide = getOppositePlacement(mainAlignmentSide);\n }\n return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];\n}\nfunction getExpandedPlacements(placement) {\n const oppositePlacement = getOppositePlacement(placement);\n return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];\n}\nfunction getOppositeAlignmentPlacement(placement) {\n return placement.includes('start') ? placement.replace('start', 'end') : placement.replace('end', 'start');\n}\nconst lrPlacement = ['left', 'right'];\nconst rlPlacement = ['right', 'left'];\nconst tbPlacement = ['top', 'bottom'];\nconst btPlacement = ['bottom', 'top'];\nfunction getSideList(side, isStart, rtl) {\n switch (side) {\n case 'top':\n case 'bottom':\n if (rtl) return isStart ? rlPlacement : lrPlacement;\n return isStart ? lrPlacement : rlPlacement;\n case 'left':\n case 'right':\n return isStart ? tbPlacement : btPlacement;\n default:\n return [];\n }\n}\nfunction getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {\n const alignment = getAlignment(placement);\n let list = getSideList(getSide(placement), direction === 'start', rtl);\n if (alignment) {\n list = list.map(side => side + \"-\" + alignment);\n if (flipAlignment) {\n list = list.concat(list.map(getOppositeAlignmentPlacement));\n }\n }\n return list;\n}\nfunction getOppositePlacement(placement) {\n const side = getSide(placement);\n return oppositeSideMap[side] + placement.slice(side.length);\n}\nfunction expandPaddingObject(padding) {\n var _padding$top, _padding$right, _padding$bottom, _padding$left;\n return {\n top: (_padding$top = padding.top) != null ? _padding$top : 0,\n right: (_padding$right = padding.right) != null ? _padding$right : 0,\n bottom: (_padding$bottom = padding.bottom) != null ? _padding$bottom : 0,\n left: (_padding$left = padding.left) != null ? _padding$left : 0\n };\n}\nfunction getPaddingObject(padding) {\n return typeof padding !== 'number' ? expandPaddingObject(padding) : {\n top: padding,\n right: padding,\n bottom: padding,\n left: padding\n };\n}\nfunction rectToClientRect(rect) {\n const {\n x,\n y,\n width,\n height\n } = rect;\n return {\n width,\n height,\n top: y,\n left: x,\n right: x + width,\n bottom: y + height,\n x,\n y\n };\n}\n\nexport { alignments, clamp, createCoords, evaluate, expandPaddingObject, floor, getAlignment, getAlignmentAxis, getAlignmentSides, getAxisLength, getExpandedPlacements, getOppositeAlignmentPlacement, getOppositeAxis, getOppositeAxisPlacements, getOppositePlacement, getPaddingObject, getSide, getSideAxis, max, min, placements, rectToClientRect, round, sides };\n","import { getSideAxis, getAlignmentAxis, getAxisLength, getSide, getAlignment, evaluate, getPaddingObject, rectToClientRect, min, clamp, placements, getAlignmentSides, getOppositeAlignmentPlacement, getOppositePlacement, getExpandedPlacements, getOppositeAxisPlacements, sides, max, getOppositeAxis } from '@floating-ui/utils';\nexport { rectToClientRect } from '@floating-ui/utils';\n\nfunction computeCoordsFromPlacement(_ref, placement, rtl) {\n let {\n reference,\n floating\n } = _ref;\n const sideAxis = getSideAxis(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const alignLength = getAxisLength(alignmentAxis);\n const side = getSide(placement);\n const isVertical = sideAxis === 'y';\n const commonX = reference.x + reference.width / 2 - floating.width / 2;\n const commonY = reference.y + reference.height / 2 - floating.height / 2;\n const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;\n let coords;\n switch (side) {\n case 'top':\n coords = {\n x: commonX,\n y: reference.y - floating.height\n };\n break;\n case 'bottom':\n coords = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n case 'right':\n coords = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n case 'left':\n coords = {\n x: reference.x - floating.width,\n y: commonY\n };\n break;\n default:\n coords = {\n x: reference.x,\n y: reference.y\n };\n }\n const alignment = getAlignment(placement);\n if (alignment) {\n coords[alignmentAxis] += commonAlign * (alignment === 'end' ? 1 : -1) * (rtl && isVertical ? -1 : 1);\n }\n return coords;\n}\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nasync function detectOverflow(state, options) {\n var _await$platform$isEle;\n if (options === void 0) {\n options = {};\n }\n const {\n x,\n y,\n platform,\n rects,\n elements,\n strategy\n } = state;\n const {\n boundary = 'clippingAncestors',\n rootBoundary = 'viewport',\n elementContext = 'floating',\n altBoundary = false,\n padding = 0\n } = evaluate(options, state);\n const paddingObject = getPaddingObject(padding);\n const altContext = elementContext === 'floating' ? 'reference' : 'floating';\n const element = elements[altBoundary ? altContext : elementContext];\n const clippingClientRect = rectToClientRect(await platform.getClippingRect({\n element: ((_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),\n boundary,\n rootBoundary,\n strategy\n }));\n const rect = elementContext === 'floating' ? {\n x,\n y,\n width: rects.floating.width,\n height: rects.floating.height\n } : rects.reference;\n const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));\n const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) && (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {\n x: 1,\n y: 1\n };\n const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({\n elements,\n rect,\n offsetParent,\n strategy\n }) : rect);\n return {\n top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,\n bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,\n left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,\n right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x\n };\n}\n\n// Maximum number of resets that can occur before bailing to avoid infinite reset loops.\nconst MAX_RESET_COUNT = 50;\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n *\n * This export does not have any `platform` interface logic. You will need to\n * write one for the platform you are using Floating UI with.\n */\nconst computePosition = async (reference, floating, config) => {\n const {\n placement = 'bottom',\n strategy = 'absolute',\n middleware = [],\n platform\n } = config;\n const platformWithDetectOverflow = platform.detectOverflow ? platform : {\n ...platform,\n detectOverflow\n };\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));\n let rects = await platform.getElementRects({\n reference,\n floating,\n strategy\n });\n let {\n x,\n y\n } = computeCoordsFromPlacement(rects, placement, rtl);\n let statefulPlacement = placement;\n let resetCount = 0;\n const middlewareData = {};\n for (let i = 0; i < middleware.length; i++) {\n const currentMiddleware = middleware[i];\n if (!currentMiddleware) {\n continue;\n }\n const {\n name,\n fn\n } = currentMiddleware;\n const {\n x: nextX,\n y: nextY,\n data,\n reset\n } = await fn({\n x,\n y,\n initialPlacement: placement,\n placement: statefulPlacement,\n strategy,\n middlewareData,\n rects,\n platform: platformWithDetectOverflow,\n elements: {\n reference,\n floating\n }\n });\n x = nextX != null ? nextX : x;\n y = nextY != null ? nextY : y;\n middlewareData[name] = {\n ...middlewareData[name],\n ...data\n };\n if (reset && resetCount < MAX_RESET_COUNT) {\n resetCount++;\n if (typeof reset === 'object') {\n if (reset.placement) {\n statefulPlacement = reset.placement;\n }\n if (reset.rects) {\n rects = reset.rects === true ? await platform.getElementRects({\n reference,\n floating,\n strategy\n }) : reset.rects;\n }\n ({\n x,\n y\n } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));\n }\n i = -1;\n }\n }\n return {\n x,\n y,\n placement: statefulPlacement,\n strategy,\n middlewareData\n };\n};\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = options => ({\n name: 'arrow',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement,\n rects,\n platform,\n elements,\n middlewareData\n } = state;\n // Since `element` is required, we don't Partial<> the type.\n const {\n element,\n padding = 0\n } = evaluate(options, state) || {};\n if (element == null) {\n return {};\n }\n const paddingObject = getPaddingObject(padding);\n const coords = {\n x,\n y\n };\n const axis = getAlignmentAxis(placement);\n const length = getAxisLength(axis);\n const arrowDimensions = await platform.getDimensions(element);\n const isYAxis = axis === 'y';\n const minProp = isYAxis ? 'top' : 'left';\n const maxProp = isYAxis ? 'bottom' : 'right';\n const clientProp = isYAxis ? 'clientHeight' : 'clientWidth';\n const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];\n const startDiff = coords[axis] - rects.reference[axis];\n const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));\n let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0;\n\n // DOM platform can return `window` as the `offsetParent`.\n if (!clientSize || !(await (platform.isElement == null ? void 0 : platform.isElement(arrowOffsetParent)))) {\n clientSize = elements.floating[clientProp] || rects.floating[length];\n }\n const centerToReference = endDiff / 2 - startDiff / 2;\n\n // If the padding is large enough that it causes the arrow to no longer be\n // centered, modify the padding so that it is centered.\n const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;\n const minPadding = min(paddingObject[minProp], largestPossiblePadding);\n const maxPadding = min(paddingObject[maxProp], largestPossiblePadding);\n\n // Make sure the arrow doesn't overflow the floating element if the center\n // point is outside the floating element's bounds.\n const max = clientSize - arrowDimensions[length] - maxPadding;\n const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;\n const offset = clamp(minPadding, center, max);\n\n // If the reference is small enough that the arrow's padding causes it to\n // to point to nothing for an aligned placement, adjust the offset of the\n // floating element itself. To ensure `shift()` continues to take action,\n // a single reset is performed when this is true.\n const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < minPadding ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;\n const alignmentOffset = shouldAddOffset ? center < minPadding ? center - minPadding : center - max : 0;\n return {\n [axis]: coords[axis] + alignmentOffset,\n data: {\n [axis]: offset,\n centerOffset: center - offset - alignmentOffset,\n ...(shouldAddOffset && {\n alignmentOffset\n })\n },\n reset: shouldAddOffset\n };\n }\n});\n\nfunction getPlacementList(alignment, autoAlignment, allowedPlacements) {\n const allowedPlacementsSortedByAlignment = alignment ? [...allowedPlacements.filter(placement => getAlignment(placement) === alignment), ...allowedPlacements.filter(placement => getAlignment(placement) !== alignment)] : allowedPlacements.filter(placement => getSide(placement) === placement);\n return allowedPlacementsSortedByAlignment.filter(placement => {\n if (alignment) {\n return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);\n }\n return true;\n });\n}\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'autoPlacement',\n options,\n async fn(state) {\n var _middlewareData$autoP, _middlewareData$autoP2, _placementsThatFitOnE;\n const {\n rects,\n middlewareData,\n placement,\n platform,\n elements\n } = state;\n const {\n crossAxis = false,\n alignment,\n allowedPlacements = placements,\n autoAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n const placements$1 = alignment !== undefined || allowedPlacements === placements ? getPlacementList(alignment || null, autoAlignment, allowedPlacements) : allowedPlacements;\n const currentIndex = ((_middlewareData$autoP = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP.index) || 0;\n const currentPlacement = placements$1[currentIndex];\n if (currentPlacement == null) {\n return {};\n }\n\n // Make `computeCoords` start from the right place.\n if (placement !== currentPlacement) {\n return {\n reset: {\n placement: placements$1[0]\n }\n };\n }\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const alignmentSides = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));\n const currentOverflows = [overflow[getSide(currentPlacement)], overflow[alignmentSides[0]], overflow[alignmentSides[1]]];\n const allOverflows = [...(((_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.overflows) || []), {\n placement: currentPlacement,\n overflows: currentOverflows\n }];\n const nextPlacement = placements$1[currentIndex + 1];\n\n // There are more placements to check.\n if (nextPlacement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n const placementsSortedByMostSpace = allOverflows.map(d => {\n const alignment = getAlignment(d.placement);\n return [d.placement, alignment && crossAxis ?\n // Check along the mainAxis and main crossAxis side.\n d.overflows.slice(0, 2).reduce((acc, v) => acc + v, 0) :\n // Check only the mainAxis.\n d.overflows[0], d.overflows];\n }).sort((a, b) => a[1] - b[1]);\n const placementsThatFitOnEachSide = placementsSortedByMostSpace.filter(d => d[2].slice(0,\n // Aligned placements should not check their opposite crossAxis\n // side.\n getAlignment(d[0]) ? 2 : 3).every(v => v <= 0));\n const resetPlacement = ((_placementsThatFitOnE = placementsThatFitOnEachSide[0]) == null ? void 0 : _placementsThatFitOnE[0]) || placementsSortedByMostSpace[0][0];\n if (resetPlacement !== placement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: resetPlacement\n }\n };\n }\n return {};\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'flip',\n options,\n async fn(state) {\n var _middlewareData$arrow, _middlewareData$flip;\n const {\n placement,\n middlewareData,\n rects,\n initialPlacement,\n platform,\n elements\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true,\n fallbackPlacements: specifiedFallbackPlacements,\n fallbackStrategy = 'bestFit',\n fallbackAxisSideDirection = 'none',\n flipAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n\n // If a reset by the arrow was caused due to an alignment offset being\n // added, we should skip any logic now since `flip()` has already done its\n // work.\n // https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643\n if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n const side = getSide(placement);\n const initialSideAxis = getSideAxis(initialPlacement);\n const isBasePlacement = getSide(initialPlacement) === initialPlacement;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));\n const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== 'none';\n if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {\n fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));\n }\n const placements = [initialPlacement, ...fallbackPlacements];\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const overflows = [];\n let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];\n if (checkMainAxis) {\n overflows.push(overflow[side]);\n }\n if (checkCrossAxis) {\n const sides = getAlignmentSides(placement, rects, rtl);\n overflows.push(overflow[sides[0]], overflow[sides[1]]);\n }\n overflowsData = [...overflowsData, {\n placement,\n overflows\n }];\n\n // One or more sides is overflowing.\n if (!overflows.every(side => side <= 0)) {\n var _middlewareData$flip2, _overflowsData$filter;\n const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;\n const nextPlacement = placements[nextIndex];\n if (nextPlacement) {\n const ignoreCrossAxisOverflow = checkCrossAxis === 'alignment' ? initialSideAxis !== getSideAxis(nextPlacement) : false;\n if (!ignoreCrossAxisOverflow ||\n // We leave the current main axis only if every placement on that axis\n // overflows the main axis.\n overflowsData.every(d => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) {\n // Try next placement and re-run the lifecycle.\n return {\n data: {\n index: nextIndex,\n overflows: overflowsData\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n }\n\n // First, find the candidates that fit on the mainAxis side of overflow,\n // then find the placement that fits the best on the main crossAxis side.\n let resetPlacement = (_overflowsData$filter = overflowsData.filter(d => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;\n\n // Otherwise fallback.\n if (!resetPlacement) {\n switch (fallbackStrategy) {\n case 'bestFit':\n {\n var _overflowsData$filter2;\n const placement = (_overflowsData$filter2 = overflowsData.filter(d => {\n if (hasFallbackAxisSideDirection) {\n const currentSideAxis = getSideAxis(d.placement);\n return currentSideAxis === initialSideAxis ||\n // Create a bias to the `y` side axis due to horizontal\n // reading directions favoring greater width.\n currentSideAxis === 'y';\n }\n return true;\n }).map(d => [d.placement, d.overflows.filter(overflow => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$filter2[0];\n if (placement) {\n resetPlacement = placement;\n }\n break;\n }\n case 'initialPlacement':\n resetPlacement = initialPlacement;\n break;\n }\n }\n if (placement !== resetPlacement) {\n return {\n reset: {\n placement: resetPlacement\n }\n };\n }\n }\n return {};\n }\n };\n};\n\nfunction getSideOffsets(overflow, rect) {\n return {\n top: overflow.top - rect.height,\n right: overflow.right - rect.width,\n bottom: overflow.bottom - rect.height,\n left: overflow.left - rect.width\n };\n}\nfunction isAnySideFullyClipped(overflow) {\n return sides.some(side => overflow[side] >= 0);\n}\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'hide',\n options,\n async fn(state) {\n const {\n rects,\n platform\n } = state;\n const {\n strategy = 'referenceHidden',\n ...detectOverflowOptions\n } = evaluate(options, state);\n switch (strategy) {\n case 'referenceHidden':\n {\n const overflow = await platform.detectOverflow(state, {\n ...detectOverflowOptions,\n elementContext: 'reference'\n });\n const offsets = getSideOffsets(overflow, rects.reference);\n return {\n data: {\n referenceHiddenOffsets: offsets,\n referenceHidden: isAnySideFullyClipped(offsets)\n }\n };\n }\n case 'escaped':\n {\n const overflow = await platform.detectOverflow(state, {\n ...detectOverflowOptions,\n altBoundary: true\n });\n const offsets = getSideOffsets(overflow, rects.floating);\n return {\n data: {\n escapedOffsets: offsets,\n escaped: isAnySideFullyClipped(offsets)\n }\n };\n }\n default:\n {\n return {};\n }\n }\n }\n };\n};\n\nfunction getBoundingRect(rects) {\n const minX = min(...rects.map(rect => rect.left));\n const minY = min(...rects.map(rect => rect.top));\n const maxX = max(...rects.map(rect => rect.right));\n const maxY = max(...rects.map(rect => rect.bottom));\n return {\n x: minX,\n y: minY,\n width: maxX - minX,\n height: maxY - minY\n };\n}\nfunction getRectsByLine(rects) {\n const sortedRects = rects.slice().sort((a, b) => a.y - b.y);\n const groups = [];\n let prevRect = null;\n for (let i = 0; i < sortedRects.length; i++) {\n const rect = sortedRects[i];\n if (!prevRect || rect.y - prevRect.y > prevRect.height / 2) {\n groups.push([rect]);\n } else {\n groups[groups.length - 1].push(rect);\n }\n prevRect = rect;\n }\n return groups.map(rect => rectToClientRect(getBoundingRect(rect)));\n}\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'inline',\n options,\n async fn(state) {\n const {\n placement,\n elements,\n rects,\n platform,\n strategy\n } = state;\n // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a\n // ClientRect's bounds, despite the event listener being triggered. A\n // padding of 2 seems to handle this issue.\n const {\n padding = 2,\n x,\n y\n } = evaluate(options, state);\n const nativeClientRects = Array.from((await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) || []);\n\n // No rects (e.g. a hidden or detached reference, or a collapsed range) —\n // keep the existing reference rect rather than resetting to an invalid\n // one with non-finite values.\n if (!nativeClientRects.length) {\n return {};\n }\n const clientRects = getRectsByLine(nativeClientRects);\n const fallback = rectToClientRect(getBoundingRect(nativeClientRects));\n const paddingObject = getPaddingObject(padding);\n function getBoundingClientRect() {\n // There are two rects and they are disjoined.\n if (clientRects.length === 2 && (clientRects[0].left > clientRects[1].right || clientRects[1].left > clientRects[0].right) && x != null && y != null) {\n // Find the first rect in which the point is fully inside.\n return clientRects.find(rect => x > rect.left - paddingObject.left && x < rect.right + paddingObject.right && y > rect.top - paddingObject.top && y < rect.bottom + paddingObject.bottom) || fallback;\n }\n\n // There are 2 or more connected rects.\n if (clientRects.length >= 2) {\n if (getSideAxis(placement) === 'y') {\n const firstRect = clientRects[0];\n const lastRect = clientRects[clientRects.length - 1];\n const isTop = getSide(placement) === 'top';\n const top = firstRect.top;\n const bottom = lastRect.bottom;\n const left = isTop ? firstRect.left : lastRect.left;\n const right = isTop ? firstRect.right : lastRect.right;\n return rectToClientRect({\n x: left,\n y: top,\n width: right - left,\n height: bottom - top\n });\n }\n const isLeftSide = getSide(placement) === 'left';\n const maxRight = max(...clientRects.map(rect => rect.right));\n const minLeft = min(...clientRects.map(rect => rect.left));\n const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);\n const top = measureRects[0].top;\n const bottom = measureRects[measureRects.length - 1].bottom;\n return rectToClientRect({\n x: minLeft,\n y: top,\n width: maxRight - minLeft,\n height: bottom - top\n });\n }\n return fallback;\n }\n const resetRects = await platform.getElementRects({\n reference: {\n getBoundingClientRect\n },\n floating: elements.floating,\n strategy\n });\n if (rects.reference.x !== resetRects.reference.x || rects.reference.y !== resetRects.reference.y || rects.reference.width !== resetRects.reference.width || rects.reference.height !== resetRects.reference.height) {\n return {\n reset: {\n rects: resetRects\n }\n };\n }\n return {};\n }\n };\n};\n\nconst originSides = /*#__PURE__*/new Set(['left', 'top']);\n\n// For type backwards-compatibility, the `OffsetOptions` type was also\n// Derivable.\n\nasync function convertValueToCoords(state, options) {\n const {\n placement,\n platform,\n elements\n } = state;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isVertical = getSideAxis(placement) === 'y';\n const mainAxisMulti = originSides.has(side) ? -1 : 1;\n const crossAxisMulti = rtl && isVertical ? -1 : 1;\n const rawValue = evaluate(options, state);\n\n // eslint-disable-next-line prefer-const\n let {\n mainAxis,\n crossAxis,\n alignmentAxis\n } = typeof rawValue === 'number' ? {\n mainAxis: rawValue,\n crossAxis: 0,\n alignmentAxis: null\n } : {\n mainAxis: rawValue.mainAxis || 0,\n crossAxis: rawValue.crossAxis || 0,\n alignmentAxis: rawValue.alignmentAxis\n };\n if (alignment && typeof alignmentAxis === 'number') {\n crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;\n }\n return isVertical ? {\n x: crossAxis * crossAxisMulti,\n y: mainAxis * mainAxisMulti\n } : {\n x: mainAxis * mainAxisMulti,\n y: crossAxis * crossAxisMulti\n };\n}\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = function (options) {\n if (options === void 0) {\n options = 0;\n }\n return {\n name: 'offset',\n options,\n async fn(state) {\n var _middlewareData$offse, _middlewareData$arrow;\n const {\n x,\n y,\n placement,\n middlewareData\n } = state;\n const diffCoords = await convertValueToCoords(state, options);\n\n // If the placement is the same and the arrow caused an alignment offset\n // then we don't need to change the positioning coordinates.\n if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n return {\n x: x + diffCoords.x,\n y: y + diffCoords.y,\n data: {\n ...diffCoords,\n placement\n }\n };\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'shift',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement,\n platform\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = false,\n limiter = {\n fn: _ref => {\n let {\n x,\n y\n } = _ref;\n return {\n x,\n y\n };\n }\n },\n ...detectOverflowOptions\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const crossAxis = getSideAxis(placement);\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n const clampCoord = (axis, coord) => clamp(coord + overflow[axis === 'y' ? 'top' : 'left'], coord, coord - overflow[axis === 'y' ? 'bottom' : 'right']);\n if (checkMainAxis) {\n mainAxisCoord = clampCoord(mainAxis, mainAxisCoord);\n }\n if (checkCrossAxis) {\n crossAxisCoord = clampCoord(crossAxis, crossAxisCoord);\n }\n const limitedCoords = limiter.fn({\n ...state,\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n });\n return {\n ...limitedCoords,\n data: {\n x: limitedCoords.x - x,\n y: limitedCoords.y - y,\n enabled: {\n [mainAxis]: checkMainAxis,\n [crossAxis]: checkCrossAxis\n }\n }\n };\n }\n };\n};\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n options,\n fn(state) {\n var _rawOffset$mainAxis, _rawOffset$crossAxis;\n const {\n x,\n y,\n placement,\n rects,\n middlewareData\n } = state;\n const {\n offset = 0,\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const crossAxis = getSideAxis(placement);\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n const rawOffset = evaluate(offset, state);\n const computedOffset = typeof rawOffset === 'number' ? {\n mainAxis: rawOffset,\n crossAxis: 0\n } : {\n mainAxis: (_rawOffset$mainAxis = rawOffset.mainAxis) != null ? _rawOffset$mainAxis : 0,\n crossAxis: (_rawOffset$crossAxis = rawOffset.crossAxis) != null ? _rawOffset$crossAxis : 0\n };\n if (checkMainAxis) {\n const len = mainAxis === 'y' ? 'height' : 'width';\n const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;\n const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;\n if (mainAxisCoord < limitMin) {\n mainAxisCoord = limitMin;\n } else if (mainAxisCoord > limitMax) {\n mainAxisCoord = limitMax;\n }\n }\n if (checkCrossAxis) {\n var _middlewareData$offse, _middlewareData$offse2;\n const len = mainAxis === 'y' ? 'width' : 'height';\n const isOriginSide = originSides.has(getSide(placement));\n const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis);\n const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0);\n if (crossAxisCoord < limitMin) {\n crossAxisCoord = limitMin;\n } else if (crossAxisCoord > limitMax) {\n crossAxisCoord = limitMax;\n }\n }\n return {\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n };\n }\n };\n};\n\n// Method syntax keeps callback parameters bivariant, but expressing the\n// explicit `| undefined` required by `exactOptionalPropertyTypes` needs\n// property syntax, which is contravariant under `strictFunctionTypes`.\n// Extracting the function from a method position restores that bivariance so\n// consumers can still assign callbacks with narrower parameter types.\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'size',\n options,\n async fn(state) {\n const {\n placement,\n rects,\n platform,\n elements\n } = state;\n const {\n apply = () => {},\n ...detectOverflowOptions\n } = evaluate(options, state);\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isYAxis = getSideAxis(placement) === 'y';\n const {\n width,\n height\n } = rects.floating;\n let heightSide;\n let widthSide;\n if (side === 'top' || side === 'bottom') {\n heightSide = side;\n widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';\n } else {\n widthSide = side;\n heightSide = alignment === 'end' ? 'top' : 'bottom';\n }\n const maximumClippingHeight = height - overflow.top - overflow.bottom;\n const maximumClippingWidth = width - overflow.left - overflow.right;\n const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight);\n const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth);\n const shiftData = state.middlewareData.shift;\n const noShift = !shiftData;\n let availableHeight = overflowAvailableHeight;\n let availableWidth = overflowAvailableWidth;\n if (shiftData != null && shiftData.enabled.x) {\n availableWidth = maximumClippingWidth;\n }\n if (shiftData != null && shiftData.enabled.y) {\n availableHeight = maximumClippingHeight;\n }\n if (noShift && !alignment) {\n if (isYAxis) {\n availableWidth = width - 2 * max(overflow.left, overflow.right);\n } else {\n availableHeight = height - 2 * max(overflow.top, overflow.bottom);\n }\n }\n await apply({\n ...state,\n availableWidth,\n availableHeight\n });\n const nextDimensions = await platform.getDimensions(elements.floating);\n if (width !== nextDimensions.width || height !== nextDimensions.height) {\n return {\n reset: {\n rects: true\n }\n };\n }\n return {};\n }\n };\n};\n\nexport { arrow, autoPlacement, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, shift, size };\n","import { rectToClientRect, arrow as arrow$1, autoPlacement as autoPlacement$1, detectOverflow as detectOverflow$1, flip as flip$1, hide as hide$1, inline as inline$1, limitShift as limitShift$1, offset as offset$1, shift as shift$1, size as size$1, computePosition as computePosition$1 } from '@floating-ui/core';\nimport { round, createCoords, max, min, floor } from '@floating-ui/utils';\nimport { getComputedStyle as getComputedStyle$1, isHTMLElement, isElement, getWindow, isWebKit, getFrameElement, getNodeScroll, getDocumentElement, isTopLayer, getNodeName, isOverflowElement, getOverflowAncestors, getParentNode, isLastTraversableNode, isContainingBlock, isTableElement, getContainingBlock } from '@floating-ui/utils/dom';\nexport { getOverflowAncestors } from '@floating-ui/utils/dom';\n\nfunction getCssDimensions(element) {\n const css = getComputedStyle$1(element);\n // In testing environments, the `width` and `height` properties are empty\n // strings for SVG elements, returning NaN. Fallback to `0` in this case.\n let width = parseFloat(css.width) || 0;\n let height = parseFloat(css.height) || 0;\n const hasOffset = isHTMLElement(element);\n const offsetWidth = hasOffset ? element.offsetWidth : width;\n const offsetHeight = hasOffset ? element.offsetHeight : height;\n const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;\n if (shouldFallback) {\n width = offsetWidth;\n height = offsetHeight;\n }\n return {\n width,\n height,\n $: shouldFallback\n };\n}\n\nfunction unwrapElement(element) {\n return !isElement(element) ? element.contextElement : element;\n}\n\nfunction getScale(element) {\n const domElement = unwrapElement(element);\n if (!isHTMLElement(domElement)) {\n return createCoords(1);\n }\n const rect = domElement.getBoundingClientRect();\n const {\n width,\n height,\n $\n } = getCssDimensions(domElement);\n let x = ($ ? round(rect.width) : rect.width) / width;\n let y = ($ ? round(rect.height) : rect.height) / height;\n\n // 0, NaN, or Infinity should always fallback to 1.\n\n if (!x || !Number.isFinite(x)) {\n x = 1;\n }\n if (!y || !Number.isFinite(y)) {\n y = 1;\n }\n return {\n x,\n y\n };\n}\n\nconst noOffsets = /*#__PURE__*/createCoords(0);\nfunction getVisualOffsets(element) {\n const win = getWindow(element);\n if (!isWebKit() || !win.visualViewport) {\n return noOffsets;\n }\n return {\n x: win.visualViewport.offsetLeft,\n y: win.visualViewport.offsetTop\n };\n}\nfunction shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n return !!floatingOffsetParent && isFixed && floatingOffsetParent === getWindow(element);\n}\n\nfunction getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n const clientRect = element.getBoundingClientRect();\n const domElement = unwrapElement(element);\n let scale = createCoords(1);\n if (includeScale) {\n if (offsetParent) {\n if (isElement(offsetParent)) {\n scale = getScale(offsetParent);\n }\n } else {\n scale = getScale(element);\n }\n }\n const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);\n let x = (clientRect.left + visualOffsets.x) / scale.x;\n let y = (clientRect.top + visualOffsets.y) / scale.y;\n let width = clientRect.width / scale.x;\n let height = clientRect.height / scale.y;\n if (domElement && offsetParent) {\n const win = getWindow(domElement);\n const offsetWin = isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;\n let currentWin = win;\n let currentIFrame = getFrameElement(currentWin);\n while (currentIFrame && offsetWin !== currentWin) {\n const iframeScale = getScale(currentIFrame);\n const iframeRect = currentIFrame.getBoundingClientRect();\n const css = getComputedStyle$1(currentIFrame);\n const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;\n const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;\n x *= iframeScale.x;\n y *= iframeScale.y;\n width *= iframeScale.x;\n height *= iframeScale.y;\n x += left;\n y += top;\n currentWin = getWindow(currentIFrame);\n currentIFrame = getFrameElement(currentWin);\n }\n }\n return rectToClientRect({\n width,\n height,\n x,\n y\n });\n}\n\n// If <html> has a CSS width greater than the viewport, then this will be\n// incorrect for RTL.\nfunction getWindowScrollBarX(element, rect) {\n const leftScroll = getNodeScroll(element).scrollLeft;\n if (!rect) {\n return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;\n }\n return rect.left + leftScroll;\n}\n\nfunction getHTMLOffset(documentElement, scroll) {\n const htmlRect = documentElement.getBoundingClientRect();\n const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);\n const y = htmlRect.top + scroll.scrollTop;\n return {\n x,\n y\n };\n}\n\nfunction convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {\n let {\n elements,\n rect,\n offsetParent,\n strategy\n } = _ref;\n const isFixed = strategy === 'fixed';\n const documentElement = getDocumentElement(offsetParent);\n const topLayer = elements ? isTopLayer(elements.floating) : false;\n if (offsetParent === documentElement || topLayer && isFixed) {\n return rect;\n }\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n let scale = createCoords(1);\n const offsets = createCoords(0);\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n if (isOffsetParentAnElement || !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isOffsetParentAnElement) {\n const offsetRect = getBoundingClientRect(offsetParent);\n scale = getScale(offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n }\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n return {\n width: rect.width * scale.x,\n height: rect.height * scale.y,\n x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,\n y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y\n };\n}\n\nfunction getClientRects(element) {\n return element.getClientRects ? Array.from(element.getClientRects()) : [];\n}\n\n// Gets the entire size of the scrollable document area, even extending outside\n// of the `<html>` and `<body>` rect bounds if horizontally scrollable.\nfunction getDocumentRect(html) {\n const scroll = getNodeScroll(html);\n const body = html.ownerDocument.body;\n const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);\n const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);\n let x = -scroll.scrollLeft + getWindowScrollBarX(html);\n const y = -scroll.scrollTop;\n if (getComputedStyle$1(body).direction === 'rtl') {\n x += max(html.clientWidth, body.clientWidth) - width;\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\n// Safety check: ensure the scrollbar space is reasonable in case this\n// calculation is affected by unusual styles.\n// Most scrollbars leave 15-18px of space.\nconst SCROLLBAR_MAX = 25;\nfunction getViewportRect(element, strategy, rootBoundary) {\n if (rootBoundary === void 0) {\n rootBoundary = 'viewport';\n }\n const isLayoutViewport = rootBoundary === 'layoutViewport';\n const win = getWindow(element);\n const html = getDocumentElement(element);\n const visualViewport = win.visualViewport;\n let width = html.clientWidth;\n let height = html.clientHeight;\n let x = 0;\n let y = 0;\n if (visualViewport) {\n // Client coordinates are relative to the layout viewport, except in\n // WebKit with an `absolute` strategy, where they are relative to the\n // visual viewport.\n const layoutRelativeClientCoords = !isWebKit() || strategy === 'fixed';\n if (isLayoutViewport) {\n if (!layoutRelativeClientCoords) {\n x = -visualViewport.offsetLeft;\n y = -visualViewport.offsetTop;\n }\n } else {\n width = visualViewport.width;\n height = visualViewport.height;\n if (layoutRelativeClientCoords) {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n }\n const windowScrollbarX = getWindowScrollBarX(html);\n // `scrollbar-gutter: stable` on the <html> reserves gutter space that shrinks\n // the visual width but isn't reflected in `html.clientWidth`, so subtract it.\n // Only the inline-end (right) gutter can hold the scrollbar; `both-edges` also\n // reserves an empty inline-start gutter that clips nothing, so exclude just\n // the one scrollbar-side gutter — halve the measured (two-gutter) total. A\n // left-side scrollbar (`windowScrollbarX > 0`) is already handled by\n // `getHTMLOffset`/`visualViewport.width`; skip it here.\n if (windowScrollbarX <= 0) {\n const doc = html.ownerDocument;\n const body = doc.body;\n const bodyStyles = getComputedStyle(body);\n const bodyMarginInline = doc.compatMode === 'CSS1Compat' ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;\n const reservedWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);\n const gutter = getComputedStyle(html).scrollbarGutter === 'stable both-edges' ? reservedWidth / 2 : reservedWidth;\n if (gutter <= SCROLLBAR_MAX) {\n width -= gutter;\n }\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\n// Returns the inner client rect, subtracting scrollbars if present.\nfunction getInnerBoundingClientRect(element, strategy) {\n const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');\n const top = clientRect.top + element.clientTop;\n const left = clientRect.left + element.clientLeft;\n const scale = getScale(element);\n const width = element.clientWidth * scale.x;\n const height = element.clientHeight * scale.y;\n const x = left * scale.x;\n const y = top * scale.y;\n return {\n width,\n height,\n x,\n y\n };\n}\nfunction getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {\n let rect;\n if (clippingAncestor === 'viewport' || clippingAncestor === 'layoutViewport') {\n rect = getViewportRect(element, strategy, clippingAncestor);\n } else if (clippingAncestor === 'document') {\n rect = getDocumentRect(getDocumentElement(element));\n } else if (isElement(clippingAncestor)) {\n rect = getInnerBoundingClientRect(clippingAncestor, strategy);\n } else {\n const visualOffsets = getVisualOffsets(element);\n rect = {\n x: clippingAncestor.x - visualOffsets.x,\n y: clippingAncestor.y - visualOffsets.y,\n width: clippingAncestor.width,\n height: clippingAncestor.height\n };\n }\n return rectToClientRect(rect);\n}\n\n// A \"clipping ancestor\" is an `overflow` element with the characteristic of\n// clipping (or hiding) child elements. This returns all clipping ancestors\n// of the given element up the tree.\nfunction getClippingElementAncestors(element, cache) {\n const cachedResult = cache.get(element);\n if (cachedResult) {\n return cachedResult;\n }\n let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');\n let lastKeptComputedStyle = null;\n const elementIsFixed = getComputedStyle$1(element).position === 'fixed';\n let currentNode = elementIsFixed ? getParentNode(element) : element;\n\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {\n const computedStyle = getComputedStyle$1(currentNode);\n const currentNodeIsContaining = isContainingBlock(currentNode);\n // Position of the containing block chain below the current node. A fixed\n // element whose containing block hasn't been found yet is a fixed chain.\n const lastPosition = lastKeptComputedStyle ? lastKeptComputedStyle.position : elementIsFixed ? 'fixed' : '';\n\n // A non-containing ancestor does not clip the element when the chain\n // below it escapes it: a fixed chain escapes all ancestors up to the\n // next containing block, an absolute chain escapes static ancestors.\n const shouldDropCurrentNode = !currentNodeIsContaining && (lastPosition === 'fixed' || lastPosition === 'absolute' && computedStyle.position === 'static');\n if (shouldDropCurrentNode) {\n // Drop non-containing blocks.\n result = result.filter(ancestor => ancestor !== currentNode);\n } else {\n // The kept node carries the chain position for the next iteration.\n lastKeptComputedStyle = computedStyle;\n }\n currentNode = getParentNode(currentNode);\n }\n cache.set(element, result);\n return result;\n}\n\n// Gets the maximum area that the element is visible in due to any number of\n// clipping ancestors.\nfunction getClippingRect(_ref) {\n let {\n element,\n boundary,\n rootBoundary,\n strategy\n } = _ref;\n const elementClippingAncestors = boundary === 'clippingAncestors' ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary);\n const clippingAncestors = [...elementClippingAncestors, rootBoundary];\n const firstRect = getClientRectFromClippingAncestor(element, clippingAncestors[0], strategy);\n let top = firstRect.top;\n let right = firstRect.right;\n let bottom = firstRect.bottom;\n let left = firstRect.left;\n for (let i = 1; i < clippingAncestors.length; i++) {\n const rect = getClientRectFromClippingAncestor(element, clippingAncestors[i], strategy);\n top = max(rect.top, top);\n right = min(rect.right, right);\n bottom = min(rect.bottom, bottom);\n left = max(rect.left, left);\n }\n return {\n width: right - left,\n height: bottom - top,\n x: left,\n y: top\n };\n}\n\nfunction getDimensions(element) {\n const {\n width,\n height\n } = getCssDimensions(element);\n return {\n width,\n height\n };\n}\n\nfunction getRectRelativeToOffsetParent(element, offsetParent, strategy) {\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n const documentElement = getDocumentElement(offsetParent);\n const isFixed = strategy === 'fixed';\n const rect = getBoundingClientRect(element, true, isFixed, offsetParent);\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n const offsets = createCoords(0);\n if (isOffsetParentAnElement || !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isOffsetParentAnElement) {\n const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n }\n }\n\n // If the <body> scrollbar appears on the left (e.g. RTL systems). Use\n // Firefox with layout.scrollbar.side = 3 in about:config to test this.\n if (!isOffsetParentAnElement && documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;\n const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;\n return {\n x,\n y,\n width: rect.width,\n height: rect.height\n };\n}\n\nfunction isStaticPositioned(element) {\n return getComputedStyle$1(element).position === 'static';\n}\n\nfunction getTrueOffsetParent(element, polyfill) {\n if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {\n return null;\n }\n if (polyfill) {\n return polyfill(element);\n }\n let rawOffsetParent = element.offsetParent;\n\n // Firefox returns the <html> element as the offsetParent if it's non-static,\n // while Chrome and Safari return the <body> element. The <body> element must\n // be used to perform the correct calculations even if the <html> element is\n // non-static.\n if (getDocumentElement(element) === rawOffsetParent) {\n rawOffsetParent = rawOffsetParent.ownerDocument.body;\n }\n return rawOffsetParent;\n}\n\n// Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\nfunction getOffsetParent(element, polyfill) {\n const win = getWindow(element);\n if (isTopLayer(element)) {\n return win;\n }\n if (!isHTMLElement(element)) {\n let svgOffsetParent = getParentNode(element);\n while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {\n if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {\n return svgOffsetParent;\n }\n svgOffsetParent = getParentNode(svgOffsetParent);\n }\n return win;\n }\n let offsetParent = getTrueOffsetParent(element, polyfill);\n while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {\n offsetParent = getTrueOffsetParent(offsetParent, polyfill);\n }\n if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {\n return win;\n }\n return offsetParent || getContainingBlock(element) || win;\n}\n\nconst getElementRects = async function (data) {\n const getOffsetParentFn = this.getOffsetParent || getOffsetParent;\n const getDimensionsFn = this.getDimensions;\n const floatingDimensions = await getDimensionsFn(data.floating);\n return {\n reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),\n floating: {\n x: 0,\n y: 0,\n width: floatingDimensions.width,\n height: floatingDimensions.height\n }\n };\n};\n\nfunction isRTL(element) {\n return getComputedStyle$1(element).direction === 'rtl';\n}\n\nconst platform = {\n convertOffsetParentRelativeRectToViewportRelativeRect,\n getDocumentElement,\n getClippingRect,\n getOffsetParent,\n getElementRects,\n getClientRects,\n getDimensions,\n getScale,\n isElement,\n isRTL\n};\n\nfunction rectsAreEqual(a, b) {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n// https://samthor.au/2021/observing-dom/\nfunction observeMove(element, onMove, ancestorResize) {\n let io = null;\n let timeoutId;\n const root = getDocumentElement(element);\n function cleanup() {\n var _io;\n clearTimeout(timeoutId);\n (_io = io) == null || _io.disconnect();\n io = null;\n }\n function refresh(skip, threshold) {\n if (skip === void 0) {\n skip = false;\n }\n if (threshold === void 0) {\n threshold = 1;\n }\n cleanup();\n const elementRectForRootMargin = element.getBoundingClientRect();\n const {\n left,\n top,\n width,\n height\n } = elementRectForRootMargin;\n if (!skip) {\n onMove();\n }\n if (!width || !height) {\n return;\n }\n const insetTop = floor(top);\n const insetRight = floor(root.clientWidth - (left + width));\n const insetBottom = floor(root.clientHeight - (top + height));\n const insetLeft = floor(left);\n const rootMargin = -insetTop + \"px \" + -insetRight + \"px \" + -insetBottom + \"px \" + -insetLeft + \"px\";\n const options = {\n rootMargin,\n threshold: max(0, min(1, threshold)) || 1\n };\n let isFirstUpdate = true;\n function handleObserve(entries) {\n const ratio = entries[0].intersectionRatio;\n\n // The entry is a snapshot, so the reference may have moved since the\n // intersection was computed (under performance constraints, or between\n // consecutive frames of a multi-frame layout shift). The reported ratio\n // and the observed area are stale in that case and cannot be trusted to\n // detect subsequent movement, so refresh regardless of the ratio.\n if (!rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {\n return refresh();\n }\n if (ratio !== threshold) {\n if (!isFirstUpdate) {\n return refresh();\n }\n if (!ratio) {\n // If the reference is clipped in place, the ratio is 0. Throttle\n // the refresh to prevent an infinite loop of updates.\n timeoutId = setTimeout(() => {\n refresh(false, 1e-7);\n }, 1000);\n } else {\n refresh(false, ratio);\n }\n }\n isFirstUpdate = false;\n }\n\n // Older browsers don't support a `document` as the root and will throw an\n // error.\n try {\n io = new IntersectionObserver(handleObserve, {\n ...options,\n // Handle <iframe>s\n root: root.ownerDocument\n });\n } catch (_e) {\n io = new IntersectionObserver(handleObserve, options);\n }\n io.observe(element);\n }\n const win = getWindow(element);\n // The window is a resize ancestor, so when `ancestorResize` is enabled its\n // listener already runs the update on resize. Here we only need to rebuild\n // the `IntersectionObserver` for the new root size, skipping a redundant\n // update. When `ancestorResize` is disabled, this becomes the sole update.\n const handleResize = () => refresh(ancestorResize);\n win.addEventListener('resize', handleResize);\n refresh(true);\n return () => {\n win.removeEventListener('resize', handleResize);\n cleanup();\n };\n}\n\n/**\n * Automatically updates the position of the floating element when necessary.\n * Should only be called when the floating element is mounted on the DOM or\n * visible on the screen.\n * @returns cleanup function that should be invoked when the floating element is\n * removed from the DOM or hidden from the screen.\n * @see https://floating-ui.com/docs/autoUpdate\n */\nfunction autoUpdate(reference, floating, update, options) {\n if (options === void 0) {\n options = {};\n }\n const {\n ancestorScroll = true,\n ancestorResize = true,\n elementResize = typeof ResizeObserver === 'function',\n layoutShift = typeof IntersectionObserver === 'function',\n animationFrame = false\n } = options;\n const referenceEl = unwrapElement(reference);\n const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...(floating ? getOverflowAncestors(floating) : [])] : [];\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.addEventListener('scroll', update);\n ancestorResize && ancestor.addEventListener('resize', update);\n });\n const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update, ancestorResize) : null;\n let reobserveFrame = -1;\n let resizeObserver = null;\n if (elementResize) {\n resizeObserver = new ResizeObserver(_ref => {\n let [firstEntry] = _ref;\n if (firstEntry && firstEntry.target === referenceEl && resizeObserver && floating) {\n // Prevent update loops when using the `size` middleware.\n // https://github.com/floating-ui/floating-ui/issues/1740\n resizeObserver.unobserve(floating);\n cancelAnimationFrame(reobserveFrame);\n reobserveFrame = requestAnimationFrame(() => {\n var _resizeObserver;\n (_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);\n });\n }\n update();\n });\n if (referenceEl && !animationFrame) {\n resizeObserver.observe(referenceEl);\n }\n if (floating) {\n resizeObserver.observe(floating);\n }\n }\n let frameId;\n let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;\n if (animationFrame) {\n frameLoop();\n }\n function frameLoop() {\n const nextRefRect = getBoundingClientRect(reference);\n if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {\n update();\n }\n prevRefRect = nextRefRect;\n frameId = requestAnimationFrame(frameLoop);\n }\n update();\n return () => {\n var _resizeObserver2;\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.removeEventListener('scroll', update);\n ancestorResize && ancestor.removeEventListener('resize', update);\n });\n cleanupIo == null || cleanupIo();\n (_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();\n resizeObserver = null;\n if (animationFrame) {\n cancelAnimationFrame(frameId);\n }\n };\n}\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nconst detectOverflow = detectOverflow$1;\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = offset$1;\n\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = autoPlacement$1;\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = shift$1;\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = flip$1;\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = size$1;\n\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = hide$1;\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = arrow$1;\n\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = inline$1;\n\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = limitShift$1;\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n */\nconst computePosition = (reference, floating, options) => {\n // This caches the expensive `getClippingElementAncestors` function so that\n // multiple lifecycle resets re-use the same result. It only lives for a\n // single call. If other functions become expensive, we can add them as well.\n const cache = new Map();\n const mergedOptions = options != null ? options : {};\n const platformWithCache = {\n ...platform,\n ...mergedOptions.platform,\n _c: cache\n };\n return computePosition$1(reference, floating, {\n ...mergedOptions,\n platform: platformWithCache\n });\n};\n\nexport { arrow, autoPlacement, autoUpdate, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, platform, shift, size };\n","import '@tiptap/markdown';\nimport type { Editor } from '@tiptap/core';\nimport type { Node as PMNode } from '@tiptap/pm/model';\n\n/**\n * 块拖拽菜单能「转成」的目标。和斜杠菜单里可序列化的块对齐,不含表格 / 公式 / 图片。\n */\nexport const TURN_INTO_KINDS = [\n 'paragraph',\n 'heading-1',\n 'heading-2',\n 'heading-3',\n 'bulletList',\n 'orderedList',\n 'taskList',\n 'blockquote',\n 'codeBlock',\n] as const;\n\nexport type TurnIntoKind = (typeof TURN_INTO_KINDS)[number];\n\nconst TURNABLE = new Set([\n 'paragraph',\n 'heading',\n 'bulletList',\n 'orderedList',\n 'taskList',\n 'listItem',\n 'taskItem',\n 'blockquote',\n 'codeBlock',\n]);\n\n/** 列表 / 引用 / 代码块要先拆掉外壳,段落和标题直接改节点类型即可。 */\nconst LIFT_BEFORE_TURN = new Set([\n 'listItem',\n 'taskItem',\n 'bulletList',\n 'orderedList',\n 'taskList',\n 'blockquote',\n 'codeBlock',\n]);\n\nexport function canTurnBlockInto(node: PMNode | null | undefined): boolean {\n return !!node && TURNABLE.has(node.type.name);\n}\n\n/** 菜单里标出「当前就是这种块」。列表项看它的父列表。 */\nexport function currentTurnIntoKind(\n editor: Editor,\n pos: number,\n node: PMNode,\n): TurnIntoKind | null {\n const name = node.type.name;\n if (name === 'paragraph') return 'paragraph';\n if (name === 'heading') {\n const level = Number(node.attrs.level);\n if (level === 1 || level === 2 || level === 3) {\n return `heading-${level}` as TurnIntoKind;\n }\n return null;\n }\n if (name === 'bulletList') return 'bulletList';\n if (name === 'orderedList') return 'orderedList';\n if (name === 'taskList') return 'taskList';\n if (name === 'blockquote') return 'blockquote';\n if (name === 'codeBlock') return 'codeBlock';\n if (name === 'listItem' || name === 'taskItem') {\n const parent = editor.state.doc.resolve(pos).parent.type.name;\n if (parent === 'bulletList') return 'bulletList';\n if (parent === 'orderedList') return 'orderedList';\n if (parent === 'taskList') return 'taskList';\n }\n return null;\n}\n\n/** 文本块内部的第一个可放光标的位置;容器则钻到第一个文本块里。 */\nfunction firstTextPos(node: PMNode, pos: number): number | null {\n if (node.isTextblock) return pos + 1;\n let found: number | null = null;\n node.descendants((child, offset) => {\n if (found != null) return false;\n if (child.isTextblock) {\n found = pos + 1 + offset + 1;\n return false;\n }\n return true;\n });\n return found;\n}\n\n/**\n * 把 `pos` 上的块转成另一种。光标落进块内再执行,这样列表项 / 引用里的段落\n * 改的是这一块,而不是文档开头。\n */\nexport function turnBlockInto(\n editor: Editor,\n pos: number,\n kind: TurnIntoKind,\n): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || !canTurnBlockInto(node)) return false;\n const textPos = firstTextPos(node, pos);\n if (textPos == null) return false;\n\n const chain = editor.chain().setTextSelection(textPos);\n if (LIFT_BEFORE_TURN.has(node.type.name)) chain.clearNodes();\n\n if (kind === 'heading-1' || kind === 'heading-2' || kind === 'heading-3') {\n const level = Number(kind.slice(-1)) as 1 | 2 | 3;\n chain.setHeading({ level });\n } else if (kind === 'paragraph') {\n // 列表 / 代码块 / 引用先 clearNodes,它已经把文本块还原成段落。\n // 再 setParagraph 会因为「已经是段落」返回 false,把整条 chain 丢掉。\n if (!LIFT_BEFORE_TURN.has(node.type.name)) chain.setParagraph();\n } else if (kind === 'bulletList') {\n chain.toggleBulletList();\n } else if (kind === 'orderedList') {\n chain.toggleOrderedList();\n } else if (kind === 'taskList') {\n chain.toggleTaskList();\n } else if (kind === 'blockquote') {\n chain.toggleBlockquote();\n } else if (kind === 'codeBlock') {\n chain.setCodeBlock();\n }\n\n return chain.run();\n}\n\n/** 在块后面插入一份拷贝(列表项插成同级下一项)。 */\nexport function duplicateBlock(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node) return false;\n return editor.chain().insertContentAt(pos + node.nodeSize, node.toJSON()).run();\n}\n\n/**\n * 删掉这一块。文档里只剩这一块时换成空段落(schema 不允许空 doc)。\n * 列表里只剩这一项时连列表一起删,避免留下空的 ul/ol。\n */\nexport function deleteBlock(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node) return false;\n\n let from = pos;\n let to = pos + node.nodeSize;\n const $pos = editor.state.doc.resolve(pos);\n const parent = $pos.parent;\n if (\n (node.type.name === 'listItem' || node.type.name === 'taskItem') &&\n parent.childCount === 1\n ) {\n from = pos - $pos.parentOffset - 1;\n const list = editor.state.doc.nodeAt(from);\n if (list) to = from + list.nodeSize;\n }\n\n if (from <= 0 && to >= editor.state.doc.content.size) {\n return editor\n .chain()\n .insertContentAt(\n { from: 0, to: editor.state.doc.content.size },\n { type: 'paragraph' },\n )\n .run();\n }\n\n return editor.chain().deleteRange({ from, to }).run();\n}\n\n/** 这一块的 markdown。复制进剪贴板前先拿到字符串,失败时调用方可以自己兜底。 */\nexport function blockMarkdown(editor: Editor, pos: number): string | null {\n const node = editor.state.doc.nodeAt(pos);\n if (!node) return null;\n const serialized = editor.markdown?.serialize(node.toJSON());\n if (typeof serialized === 'string') return serialized;\n return node.textContent;\n}\n\nexport function canResetBlockFormatting(node: PMNode | null | undefined): boolean {\n if (!node || node.isAtom) return false;\n if (node.type.name === 'image' || node.type.name === 'horizontalRule') {\n return false;\n }\n if (node.isTextblock) return true;\n let found = false;\n node.descendants((child) => {\n if (child.isTextblock) {\n found = true;\n return false;\n }\n return true;\n });\n return found;\n}\n\nfunction withoutBlockStyles(node: PMNode): Record<string, unknown> {\n const next: Record<string, unknown> = { ...node.attrs };\n if ('textAlign' in next) next.textAlign = null;\n if ('indent' in next) next.indent = null;\n if ('lineHeight' in next) next.lineHeight = null;\n return next;\n}\n\n/** 清掉块内的行内 mark,以及段落 / 标题上的对齐、缩进、行高。 */\nexport function resetBlockFormatting(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || !canResetBlockFormatting(node)) return false;\n\n const from = pos + 1;\n const to = pos + node.nodeSize - 1;\n let tr = editor.state.tr;\n if (to > from) {\n for (const mark of Object.values(editor.schema.marks)) {\n tr = tr.removeMark(from, to, mark);\n }\n }\n\n const markup: { pos: number; attrs: Record<string, unknown> }[] = [];\n if (node.isTextblock) {\n markup.push({ pos, attrs: withoutBlockStyles(node) });\n }\n node.descendants((child, offset) => {\n if (!child.isTextblock) return;\n markup.push({\n pos: pos + 1 + offset,\n attrs: withoutBlockStyles(child),\n });\n });\n for (const item of markup) {\n tr = tr.setNodeMarkup(item.pos, undefined, item.attrs);\n }\n\n if (!tr.docChanged) return false;\n editor.view.dispatch(tr);\n return true;\n}\n\nexport function isImageBlock(node: PMNode | null | undefined): boolean {\n return (\n node?.type.name === 'image' &&\n typeof node.attrs.src === 'string' &&\n node.attrs.src.length > 0\n );\n}\n\n/** 下载图片节点。跨域地址浏览器会忽略 download,退化为打开链接。 */\nexport function downloadImageBlock(node: PMNode): void {\n if (!isImageBlock(node)) return;\n const src = node.attrs.src as string;\n const alt = typeof node.attrs.alt === 'string' ? node.attrs.alt.trim() : '';\n const fromUrl = src.split('/').pop()?.split('?')[0] ?? '';\n const a = document.createElement('a');\n a.href = src;\n a.download = alt || fromUrl || 'image';\n a.rel = 'noopener';\n a.target = '_blank';\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n\n","import type { Node as PMNode, Slice } from '@tiptap/pm/model';\nimport { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state';\nimport { dropPoint } from '@tiptap/pm/transform';\nimport type { EditorView } from '@tiptap/pm/view';\n\n/**\n * 手柄拖过来的是一块闭合节点(openStart 为 0)。ProseMirror 的 dropPoint\n * 第二轮会找包装:列表正好能把段落 / 标题包成 listItem,于是单行块会掉进列表里。\n * 列表在这个编辑器里是一整块,落点只能在它外面(上半段前面,下半段后面)。\n */\nconst LIST_NAMES = new Set(['bulletList', 'orderedList', 'taskList']);\n\nconst draggingViews = new WeakSet<EditorView>();\n\nexport const blockDragListGuardKey = new PluginKey('blockDragListGuard');\n\nexport function setBlockHandleDragging(view: EditorView, dragging: boolean) {\n if (dragging) draggingViews.add(view);\n else draggingViews.delete(view);\n}\n\nexport function isBlockHandleDragging(view: EditorView): boolean {\n return draggingViews.has(view);\n}\n\nfunction outermostListDepth(doc: PMNode, pos: number): number {\n const safe = Math.max(0, Math.min(pos, doc.content.size));\n const $pos = doc.resolve(safe);\n let listDepth = -1;\n for (let depth = $pos.depth; depth > 0; depth -= 1) {\n if (LIST_NAMES.has($pos.node(depth).type.name)) listDepth = depth;\n }\n return listDepth;\n}\n\n/** 坐标落在某份列表(含其中的条目)里面。 */\nexport function isPosInsideList(doc: PMNode, pos: number): boolean {\n return outermostListDepth(doc, pos) > 0;\n}\n\n/**\n * 把列表内部的位置抬到最外层那份列表的边界。\n * `after` 为真时放在列表后面,否则放在前面。\n */\nexport function listEscapePos(\n doc: PMNode,\n pos: number,\n after: boolean,\n): number | null {\n const listDepth = outermostListDepth(doc, pos);\n if (listDepth < 0) return null;\n const $pos = doc.resolve(Math.max(0, Math.min(pos, doc.content.size)));\n const start = $pos.before(listDepth);\n const node = $pos.node(listDepth);\n return after ? start + node.nodeSize : start;\n}\n\n/** 指针在列表盒的下半段时落到列表后面,否则落到前面。 */\nexport function listEscapeEdge(\n view: EditorView,\n pos: number,\n clientY: number,\n): number | null {\n const listDepth = outermostListDepth(view.state.doc, pos);\n if (listDepth < 0) return null;\n const $pos = view.state.doc.resolve(\n Math.max(0, Math.min(pos, view.state.doc.content.size)),\n );\n const start = $pos.before(listDepth);\n const node = $pos.node(listDepth);\n const dom = view.nodeDOM(start);\n let after = pos >= start + node.nodeSize / 2;\n if (dom instanceof HTMLElement) {\n const rect = dom.getBoundingClientRect();\n if (rect.height > 0) after = clientY >= rect.top + rect.height / 2;\n }\n return after ? start + node.nodeSize : start;\n}\n\n/**\n * 把手柄拖来的切片挪到 `insertPos`。位置落在被拖的选区内部时不挪。\n * 返回 true 表示这次落点已经由我们处理(包括故意不挪)。\n */\nexport function moveDraggedSlice(\n view: EditorView,\n slice: Slice,\n insertPos: number,\n): boolean {\n const { state } = view;\n const { from, to } = state.selection;\n if (insertPos > from && insertPos < to) return true;\n\n const tr = state.tr;\n tr.deleteSelection();\n const pos = tr.mapping.map(insertPos, 1);\n const block =\n slice.openStart === 0 &&\n slice.openEnd === 0 &&\n slice.content.childCount === 1\n ? slice.content.firstChild\n : null;\n if (block) tr.replaceRangeWith(pos, pos, block);\n else tr.replaceRange(pos, pos, slice);\n if (tr.doc.eq(state.doc)) return true;\n\n const $pos = tr.doc.resolve(Math.max(0, Math.min(pos, tr.doc.content.size)));\n const near = TextSelection.findFrom($pos, 1, true) ?? TextSelection.findFrom($pos, -1, true);\n if (near) tr.setSelection(near);\n try {\n view.focus();\n } catch {\n // 无 window 的环境里 focus 会抛;文档修改仍然要提交。\n }\n view.dispatch(tr.setMeta('uiEvent', 'drop').scrollIntoView());\n return true;\n}\n\nfunction snappedListEdge(\n view: EditorView,\n clientX: number,\n clientY: number,\n slice: Slice,\n): number | null {\n const coords = view.posAtCoords({ left: clientX, top: clientY });\n if (!coords) return null;\n const natural = dropPoint(view.state.doc, coords.pos, slice);\n const probe = natural ?? coords.pos;\n if (!isPosInsideList(view.state.doc, probe)) return null;\n return listEscapeEdge(view, probe, clientY);\n}\n\n/** 列表内部的落点指示挪到整份列表的上沿或下沿,避免画在条目之间。 */\nfunction placeBlockDropCursor(view: EditorView, pos: number) {\n const parent = view.dom.offsetParent;\n if (!(parent instanceof HTMLElement)) return;\n const cursor = parent.querySelector(\n '.prosemirror-dropcursor-block, .prosemirror-dropcursor-inline',\n );\n if (!(cursor instanceof HTMLElement)) return;\n\n const $pos = view.state.doc.resolve(pos);\n const before = $pos.nodeBefore;\n const anchorPos = before ? pos - before.nodeSize : pos;\n const anchor = view.nodeDOM(anchorPos);\n if (!(anchor instanceof HTMLElement)) return;\n const anchorRect = anchor.getBoundingClientRect();\n let lineTop = before ? anchorRect.bottom : anchorRect.top;\n if (before && $pos.nodeAfter) {\n const next = view.nodeDOM(pos);\n if (next instanceof HTMLElement) {\n lineTop = (anchorRect.bottom + next.getBoundingClientRect().top) / 2;\n }\n }\n\n const editorRect = view.dom.getBoundingClientRect();\n const scaleX = editorRect.width / view.dom.offsetWidth || 1;\n const scaleY = editorRect.height / view.dom.offsetHeight || 1;\n const parentRect = parent.getBoundingClientRect();\n const parentScaleX = parentRect.width / parent.offsetWidth || 1;\n const parentScaleY = parentRect.height / parent.offsetHeight || 1;\n const parentLeft = parentRect.left - parent.scrollLeft * parentScaleX;\n const parentTop = parentRect.top - parent.scrollTop * parentScaleY;\n const half = scaleY;\n\n cursor.classList.add('prosemirror-dropcursor-block');\n cursor.classList.remove('prosemirror-dropcursor-inline');\n cursor.style.left = `${(anchorRect.left - parentLeft) / scaleX}px`;\n cursor.style.top = `${(lineTop - half - parentTop) / scaleY}px`;\n cursor.style.width = `${(anchorRect.right - anchorRect.left) / scaleX}px`;\n cursor.style.height = `${(2 * half) / scaleY}px`;\n}\n\nexport function blockDragListGuardPlugin(): Plugin {\n return new Plugin({\n key: blockDragListGuardKey,\n props: {\n handleDrop(view, event, slice, moved) {\n if (!moved || !isBlockHandleDragging(view)) return false;\n const edge = snappedListEdge(view, event.clientX, event.clientY, slice);\n if (edge == null) return false;\n moveDraggedSlice(view, slice, edge);\n return true;\n },\n },\n view(editorView) {\n const onDragOver = (event: DragEvent) => {\n const slice = editorView.dragging?.slice;\n if (!slice || !isBlockHandleDragging(editorView)) return;\n const edge = snappedListEdge(\n editorView,\n event.clientX,\n event.clientY,\n slice,\n );\n if (edge == null) return;\n placeBlockDropCursor(editorView, edge);\n };\n editorView.dom.addEventListener('dragover', onDragOver);\n return {\n destroy() {\n editorView.dom.removeEventListener('dragover', onDragOver);\n setBlockHandleDragging(editorView, false);\n },\n };\n },\n });\n}\n","'use client';\n\nimport { offset } from '@floating-ui/dom';\nimport type { Editor } from '@tiptap/core';\nimport { DragHandle } from '@tiptap/extension-drag-handle-react';\nimport type { Node as PMNode } from '@tiptap/pm/model';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n TURN_INTO_KINDS,\n blockMarkdown,\n canResetBlockFormatting,\n canTurnBlockInto,\n currentTurnIntoKind,\n deleteBlock,\n downloadImageBlock,\n duplicateBlock,\n isImageBlock,\n resetBlockFormatting,\n turnBlockInto,\n type TurnIntoKind,\n} from '../blockDrag/actions';\nimport {\n blockDragListGuardKey,\n blockDragListGuardPlugin,\n setBlockHandleDragging,\n} from '../blockDrag/listDrop';\nimport {\n CopyIcon,\n DownloadIcon,\n EraserIcon,\n GripIcon,\n TrashIcon,\n} from '../icons';\nimport { useEditorBundle } from '../i18n/store';\nimport type { BlockDragLabels } from '../labels';\nimport styles from '../styles/blockDrag.module.css';\n\n/**\n * 官方 DragHandle 的定位。负的 mainAxis 让手柄盒子伸进块左缘几像素:\n * 手柄在编辑器 DOM 外面,中间只要有空隙,鼠标移过去会先触发 mouseleave,\n * 手柄立刻消失。按钮本身仍靠 cluster 的右内边距留在正文左侧,不压住字。\n * 和 content.module.css 里 `.withBlockDrag` 的左内边距一起算,给评论气泡留空。\n */\nconst HANDLE_POSITION = {\n placement: 'left-start' as const,\n strategy: 'absolute' as const,\n middleware: [offset({ mainAxis: -6, crossAxis: 4 })],\n};\n\n/**\n * 表格的行手柄贴在表左缘。块手柄若也对齐表顶,会压在第一行的行手柄上。\n * 把定位锚点上移,六点落到表格上方的空隙里;clusterOnTable 的热区再接回表角。\n */\nconst TABLE_ANCHOR_LIFT = 24;\n\nconst THEME_VARS = [\n '--tmr-text',\n '--tmr-accent',\n '--tmr-toolbar-border',\n '--tmr-toolbar-muted',\n '--tmr-popover-bg',\n '--tmr-danger',\n '--tmr-code-bg',\n];\n\ninterface BlockTarget {\n node: PMNode;\n pos: number;\n}\n\ninterface OpenMenu {\n x: number;\n y: number;\n pos: number;\n node: PMNode;\n}\n\nexport interface BlockDragHandleProps {\n editor: Editor;\n}\n\nfunction lockDragHandle(editor: Editor, locked: boolean) {\n if (editor.isDestroyed) return;\n editor.view.dispatch(\n editor.state.tr\n .setMeta('lockDragHandle', locked)\n .setMeta('addToHistory', false),\n );\n}\n\nasync function copyMarkdown(markdown: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(markdown);\n return;\n } catch {\n // 非安全上下文里 clipboard 会拒绝,退回选区复制。\n }\n const area = document.createElement('textarea');\n area.value = markdown;\n area.setAttribute('readonly', '');\n area.style.position = 'fixed';\n area.style.left = '-9999px';\n document.body.appendChild(area);\n area.select();\n document.execCommand('copy');\n area.remove();\n}\n\nconst TURN_ICON: Record<TurnIntoKind, string> = {\n paragraph: '¶',\n 'heading-1': 'H1',\n 'heading-2': 'H2',\n 'heading-3': 'H3',\n bulletList: '•',\n orderedList: '1.',\n taskList: '✓',\n blockquote: '❝',\n codeBlock: '{ }',\n};\n\n/**\n * 块拖拽:悬停时在块左侧露出手柄(拖动重排),点手柄打开菜单\n * (转换 / 复制 / 再制一份 / 删除)。交互对齐 Tiptap Drag Context Menu,\n * 样式走本库的 `--tmr-*`。手柄跟着文档最外层的块走,列表和引用整块移动。\n * 松开时如果指针在列表上,块落到整份列表的前面或后面,不会变成其中一条。\n *\n * 不注册 NodeRange 扩展:它会接管 Shift+方向键和 Mod-a。拖拽要用的\n * NodeRangeSelection 在加载 drag-handle 时已经注册进 Selection。\n */\nexport function BlockDragHandle({ editor }: BlockDragHandleProps) {\n const t: BlockDragLabels = useEditorBundle(editor).blockDrag;\n const [target, setTarget] = useState<BlockTarget | null>(null);\n const [menu, setMenu] = useState<OpenMenu | null>(null);\n const draggedRef = useRef(false);\n const targetRef = useRef<BlockTarget | null>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n const closeMenu = useCallback(() => {\n setMenu(null);\n lockDragHandle(editor, false);\n }, [editor]);\n\n // 拦住「单行块被包成列表项」。插件跟着手柄一起挂上,只读 / 窄屏没有拖拽。\n useEffect(() => {\n const plugin = blockDragListGuardPlugin();\n editor.registerPlugin(plugin);\n return () => {\n setBlockHandleDragging(editor.view, false);\n if (!editor.isDestroyed) editor.unregisterPlugin(blockDragListGuardKey);\n };\n }, [editor]);\n\n useEffect(() => {\n if (!menu) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n event.preventDefault();\n closeMenu();\n };\n const onDown = (event: MouseEvent) => {\n const el = event.target;\n if (!(el instanceof Element)) return;\n if (el.closest('[data-block-drag-menu], [data-block-drag-grip]')) return;\n closeMenu();\n };\n const onScroll = () => closeMenu();\n document.addEventListener('keydown', onKey);\n document.addEventListener('mousedown', onDown);\n document.addEventListener('scroll', onScroll, true);\n return () => {\n document.removeEventListener('keydown', onKey);\n document.removeEventListener('mousedown', onDown);\n document.removeEventListener('scroll', onScroll, true);\n };\n }, [menu, closeMenu]);\n\n useEffect(() => {\n if (!menu || !menuRef.current) return;\n const style = getComputedStyle(editor.view.dom);\n for (const name of THEME_VARS) {\n const value = style.getPropertyValue(name);\n if (value) menuRef.current.style.setProperty(name, value);\n }\n }, [menu, editor]);\n\n useEffect(() => () => lockDragHandle(editor, false), [editor, closeMenu]);\n\n const openMenu = (anchor: HTMLElement) => {\n if (!target) return;\n const rect = anchor.getBoundingClientRect();\n const estimated = 380;\n const y =\n rect.top + estimated > window.innerHeight\n ? Math.max(8, window.innerHeight - estimated - 8)\n : rect.top;\n const x = Math.min(rect.right + 6, window.innerWidth - 220);\n lockDragHandle(editor, true);\n setMenu({ x, y, pos: target.pos, node: target.node });\n };\n\n const run = (action: () => void) => {\n action();\n // 菜单按钮上的 preventDefault 会把焦点留在手柄上;命令本身不 focus\n // (无 window 的环境里 focus 会让整条 chain 失败)。\n editor.commands.focus();\n closeMenu();\n };\n\n const activeKind = menu\n ? currentTurnIntoKind(editor, menu.pos, menu.node)\n : null;\n const showTurn = menu ? canTurnBlockInto(menu.node) : false;\n const showReset = menu ? canResetBlockFormatting(menu.node) : false;\n const showDownload = menu ? isImageBlock(menu.node) : false;\n\n const turnLabel = (kind: TurnIntoKind): string => {\n if (kind === 'paragraph') return t.paragraph;\n if (kind === 'heading-1') return t.headingLabel(1);\n if (kind === 'heading-2') return t.headingLabel(2);\n if (kind === 'heading-3') return t.headingLabel(3);\n if (kind === 'bulletList') return t.bulletList;\n if (kind === 'orderedList') return t.orderedList;\n if (kind === 'taskList') return t.taskList;\n if (kind === 'blockquote') return t.blockquote;\n return t.codeBlock;\n };\n\n return (\n <>\n <DragHandle\n editor={editor}\n computePositionConfig={HANDLE_POSITION}\n onNodeChange={({ node, pos }) => {\n if (!node || pos < 0) {\n targetRef.current = null;\n setTarget(null);\n return;\n }\n const next = { node, pos };\n targetRef.current = next;\n setTarget(next);\n }}\n getReferencedVirtualElement={() => {\n const current = targetRef.current;\n if (!current || current.node.type.name !== 'table') return null;\n const pos = current.pos;\n return {\n getBoundingClientRect: () => {\n let dom: globalThis.Node | null = null;\n try {\n dom = editor.view.nodeDOM(pos);\n } catch {\n dom = null;\n }\n if (!(dom instanceof HTMLElement)) return new DOMRect();\n const rect = dom.getBoundingClientRect();\n return new DOMRect(\n rect.x,\n rect.y - TABLE_ANCHOR_LIFT,\n rect.width,\n rect.height,\n );\n },\n };\n }}\n onElementDragStart={() => {\n draggedRef.current = true;\n setBlockHandleDragging(editor.view, true);\n }}\n onElementDragEnd={() => {\n // drop 先于 dragend,松手时落点拦截还能读到这次是手柄拖拽。\n setBlockHandleDragging(editor.view, false);\n // click 紧跟 dragend。等这次点击过去再清掉,避免松手又把菜单打开。\n setTimeout(() => {\n draggedRef.current = false;\n }, 0);\n }}\n >\n <div\n className={`${styles.cluster} ${\n target?.node.type.name === 'table' ? styles.clusterOnTable : ''\n }`}\n data-block-drag=\"\"\n >\n <div\n role=\"button\"\n tabIndex={0}\n className={`${styles.button} ${styles.grip}`}\n data-block-drag-grip=\"\"\n title={t.dragHandle}\n aria-label={t.dragHandle}\n aria-haspopup=\"menu\"\n aria-expanded={menu != null}\n onKeyDown={(event) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n if (menu) closeMenu();\n else openMenu(event.currentTarget);\n }}\n onClick={(event) => {\n if (draggedRef.current) return;\n if (menu) {\n closeMenu();\n return;\n }\n openMenu(event.currentTarget);\n }}\n >\n <GripIcon size={14} />\n </div>\n </div>\n </DragHandle>\n {menu\n ? createPortal(\n <div\n ref={menuRef}\n className={styles.menu}\n data-block-drag-menu=\"\"\n role=\"menu\"\n aria-label={t.menuLabel}\n style={{ left: menu.x, top: menu.y }}\n onMouseDown={(event) => event.stopPropagation()}\n >\n {showTurn ? (\n <>\n <div className={styles.groupLabel}>{t.turnInto}</div>\n {TURN_INTO_KINDS.map((kind) => (\n <button\n key={kind}\n type=\"button\"\n role=\"menuitem\"\n className={\n kind === activeKind\n ? `${styles.item} ${styles.itemActive}`\n : styles.item\n }\n data-block-drag-action={kind}\n onClick={() =>\n run(() => turnBlockInto(editor, menu.pos, kind))\n }\n >\n <span className={styles.itemIcon} aria-hidden>\n {TURN_ICON[kind]}\n </span>\n <span>{turnLabel(kind)}</span>\n </button>\n ))}\n <div className={styles.divider} />\n </>\n ) : null}\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"copy\"\n onClick={() =>\n run(() => {\n const markdown = blockMarkdown(editor, menu.pos);\n if (markdown != null) void copyMarkdown(markdown);\n })\n }\n >\n <span className={styles.itemIcon} aria-hidden>\n <CopyIcon />\n </span>\n <span>{t.copy}</span>\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"duplicate\"\n onClick={() => run(() => duplicateBlock(editor, menu.pos))}\n >\n <span className={styles.itemIcon} aria-hidden>\n <CopyIcon />\n </span>\n <span>{t.duplicate}</span>\n </button>\n {showReset ? (\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"reset\"\n onClick={() =>\n run(() => resetBlockFormatting(editor, menu.pos))\n }\n >\n <span className={styles.itemIcon} aria-hidden>\n <EraserIcon />\n </span>\n <span>{t.resetFormatting}</span>\n </button>\n ) : null}\n {showDownload ? (\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n data-block-drag-action=\"download\"\n onClick={() => run(() => downloadImageBlock(menu.node))}\n >\n <span className={styles.itemIcon} aria-hidden>\n <DownloadIcon />\n </span>\n <span>{t.downloadImage}</span>\n </button>\n ) : null}\n <div className={styles.divider} />\n <button\n type=\"button\"\n role=\"menuitem\"\n className={`${styles.item} ${styles.itemDanger}`}\n data-block-drag-action=\"delete\"\n onClick={() => run(() => deleteBlock(editor, menu.pos))}\n >\n <span className={styles.itemIcon} aria-hidden>\n <TrashIcon />\n </span>\n <span>{t.delete}</span>\n </button>\n </div>,\n document.body,\n )\n : null}\n </>\n );\n}\n","'use client';\n\nimport CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';\nimport FindAndReplace from '@tiptap/extension-find-and-replace';\nimport Typography from '@tiptap/extension-typography';\nimport Image from '@tiptap/extension-image';\nimport { TableOfContents } from '@tiptap/extension-table-of-contents';\nimport { CharacterCount, Placeholder, Selection } from '@tiptap/extensions';\nimport { Markdown } from '@tiptap/markdown';\nimport {\n EditorContent,\n ReactNodeViewRenderer,\n useEditor,\n useEditorState,\n type AnyExtension,\n type Editor,\n} from '@tiptap/react';\nimport type {\n NodeViewRendererProps,\n ResizableNodeViewDirection,\n} from '@tiptap/core';\nimport { mergeAttributes } from '@tiptap/core';\nimport { createPortal } from 'react-dom';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n} from 'react';\nimport {\n enrichMarkdownCitations,\n type SourceRef,\n} from '../citationUtils';\nimport type { RenderCitation } from '../citationTypes';\nimport { createCitationRef } from '../createCitationRef';\nimport { CommentMark } from '../commentAnchor/CommentMark';\nimport {\n COMMENT_ACTIVE_META,\n COMMENT_CLICK_META,\n COMMENT_GUTTER_META,\n commentAnchorExtension,\n} from '../commentAnchor/commentAnchorPlugin';\nimport {\n applyCommentAnchorsToEditor,\n collectCommentIds,\n focusComment,\n nextComment,\n} from '../commentAnchor/commentAnchorController';\nimport type {\n CommentClickPayload,\n CommentRef,\n} from '../commentAnchor/commentTypes';\nimport { baseExtensions, lowlight } from '../extensions';\nimport { isComposingKeyEvent } from '../findReplace';\nimport { FindReplaceBar } from './FindReplaceBar';\nimport { ImportPlaceholder } from '../importPlaceholder';\nimport { ImageUploadNode, type ImageUploadConfig } from '../imageUpload';\nimport {\n imageAlignAttribute,\n imageCaptionAttribute,\n imageFigureParseRule,\n imageMarkdownTokenizer,\n imageNodeHtml,\n imageParseMarkdown,\n imageRenderMarkdown,\n normalizeImageCaption,\n} from '../imageMarkdown';\nimport { setImageAlign } from '../imageAlign';\nimport { configureEmojiSuggestion } from '../emoji/EmojiSuggestion';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport { usePublishEditorLocale } from '../i18n/store';\nimport { countWords } from '../characterCount';\nimport { SlashMenu } from '../slashMenu/SlashMenuExtension';\nimport { ShortcutPanel } from '../shortcuts/ShortcutPanel';\nimport { KeyboardIcon } from '../icons';\nimport { MarkdownFileDrop } from '../markdownFileDrop';\nimport {\n pendingAnchorExtension,\n setPendingAnchors,\n type PendingAnchor,\n} from '../pendingAnchor';\nimport { selectionKind, type SelectionKind } from '../selectionKind';\nimport { MarkdownPaste } from '../markdownPaste';\nimport { parseCodeTokenAsChartOrCodeBlock } from '../chart/codeBlockChartParse';\nimport { createChart } from '../chart/createChart';\nimport { prepareChartMarkdown } from '../chart/prepareChartMarkdown';\nimport '../styles/chart.css';\nimport '../styles/pending.css';\nimport '../styles/find.css';\nimport styles from '../styles/content.module.css';\nimport type { TocItem } from '../toc/extractToc';\nimport { makeTocGetId } from '../toc/tocSlug';\nimport { CodeBlockView } from './CodeBlockView';\nimport { BlockDragHandle } from './BlockDragHandle';\nimport { EditorToc } from './EditorToc';\nimport { useIsMobile } from '../hooks/useIsMobile';\n\n// 带语言选择器的代码块(自定义 React NodeView)。base 不含代码块,此增强版由编辑器注入。\n// 文案从编辑器 locale store 读取,不写进扩展 options。\nconst CodeBlock = CodeBlockLowlight.extend({\n // Built-in `code` tokens often win over chart's custom fence tokenizer;\n // upgrade ```tmr-chart here so the mid-column card editor renders ChartView.\n parseMarkdown: (token, helpers) =>\n parseCodeTokenAsChartOrCodeBlock(token, helpers, 'codeBlock'),\n addNodeView() {\n return ReactNodeViewRenderer(CodeBlockView);\n },\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { state } = this.editor;\n const { selection } = state;\n const { empty, $anchor } = selection;\n\n // 1) 光标在「非空代码块」内容最前面:拦截,防止拆块漏文字(空块放行可删)\n if (\n empty &&\n $anchor.parent.type.name === this.name &&\n $anchor.parentOffset === 0 &&\n $anchor.parent.content.size > 0\n ) {\n return true;\n }\n\n // 2) 光标在「代码块后方块」的开头:先选中整个代码块(二次确认),不直接删\n if (empty && $anchor.parentOffset === 0) {\n const before = $anchor.before($anchor.depth);\n const prevNode = state.doc.resolve(before).nodeBefore;\n if (prevNode?.type.name === this.name) {\n const codeBlockPos = before - prevNode.nodeSize;\n this.editor.chain().setNodeSelection(codeBlockPos).run();\n return true;\n }\n }\n\n return false;\n },\n };\n },\n});\n\n// 块级图片:Backspace 二次确认删除(与代码块一致)+ 可缩放(官方 ResizableNodeView)\n// + 尺寸保真的 markdown 往返(见 imageMarkdown.ts)。导出供测试与自建管线使用。\nexport const ImageWithConfirmDelete = Image.extend({\n addAttributes() {\n return {\n ...(Image.config.addAttributes?.call(this) ?? {}),\n align: imageAlignAttribute,\n caption: imageCaptionAttribute,\n };\n },\n\n parseHTML() {\n // figure 规则要排在 img 前面:带描述(图注)的图整体归 figure\n return [\n imageFigureParseRule(),\n {\n tag: this.options.allowBase64\n ? 'img[src]'\n : 'img[src]:not([src^=\"data:\"])',\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n return imageNodeHtml({\n attrs: node.attrs,\n htmlAttributes: mergeAttributes(\n this.options.HTMLAttributes,\n HTMLAttributes,\n ),\n });\n },\n\n renderMarkdown: imageRenderMarkdown,\n parseMarkdown: imageParseMarkdown,\n markdownTokenizer: imageMarkdownTokenizer,\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { state } = this.editor;\n const { selection } = state;\n const { empty, $anchor } = selection;\n if (empty) {\n const nodeBefore = $anchor.nodeBefore;\n if (nodeBefore?.type.name === this.name) {\n const imagePos = $anchor.pos - nodeBefore.nodeSize;\n this.editor.chain().setNodeSelection(imagePos).run();\n return true;\n }\n }\n return false;\n },\n // 图片对齐:键位对齐官方 image-align-button(L 左 / E 中 / R 右),\n // 选中图片或光标紧邻图片时生效。工具函数见 imageAlign.ts。\n 'Alt-Shift-l': () => setImageAlign(this.editor, 'left'),\n 'Alt-Shift-e': () => setImageAlign(this.editor, 'center'),\n 'Alt-Shift-r': () => setImageAlign(this.editor, 'right'),\n };\n },\n\n addNodeView() {\n // 官方实现:resize 未启用 / SSR(无 document)时返回 null,落到默认 DOM 渲染\n const createView = Image.config.addNodeView?.call(this);\n if (!createView) return null;\n\n // 本库扩展的缩放上限:官方 `Image` 的 resize 配置只有 min、没有 max 入口,但\n // ResizableNodeView 有公开的 maxSize 字段(applyConstraints 每次拖拽都会读)。\n // maxWidth / maxHeight 由 <MarkdownWysiwygEditor> 展开进 resize 对象传到这里。\n const resizeOptions = this.options.resize as\n | { maxWidth?: number; maxHeight?: number }\n | false\n | undefined;\n const explicitMaxWidth =\n resizeOptions && typeof resizeOptions === 'object'\n ? resizeOptions.maxWidth\n : undefined;\n const explicitMaxHeight =\n resizeOptions && typeof resizeOptions === 'object'\n ? resizeOptions.maxHeight\n : undefined;\n\n return (props: NodeViewRendererProps) => {\n const nodeView = createView(props);\n if (!nodeView) return nodeView;\n\n // removeHandles / element / maxSize 是 ResizableNodeView 的成员——官方升级若动\n // 它们,「只读无手柄 / 程序化改尺寸视觉同步 / 缩放上下限」测试会先炸。\n const resizable = nodeView as unknown as {\n element?: HTMLElement;\n maxSize?: { width?: number; height?: number };\n removeHandles?: () => void;\n };\n\n // 「打开就是只读」的文档:官方只在收到第一个 update 事件后才同步 editable(摘手柄),\n // 等不到事件的手柄会一直露着、还能拖出残影(只读态拖了不提交,样式却改了)。\n // 创建时补摘一次;之后切回编辑态,官方自己的 update 监听会把手柄挂回来。\n if (!props.editor.isEditable) resizable.removeHandles?.();\n\n // 缩放上限:显式配置优先;没配则跟随内容区宽度(图片拖不出布局)。\n // 注意两点:其一,NodeView 是在 editor.view 构造**过程中**创建的,此刻\n // `editor.view` 还不可访问(tiptap 会抛 \"view is not available\")——所以初始\n // 只设显式上限,动态上限推迟到 mousedown;其二,监听挂在容器上、capture 阶段,\n // 先于官方的手柄 mousedown 处理跑,每次拖拽开始前刷新,窗口变化后也能跟上。\n const applyMaxSize = () => {\n resizable.maxSize =\n explicitMaxWidth || explicitMaxHeight\n ? { width: explicitMaxWidth, height: explicitMaxHeight }\n : { width: props.editor.view.dom.clientWidth || undefined };\n };\n if (explicitMaxWidth || explicitMaxHeight) applyMaxSize();\n\n // 图注(描述):挂进 wrapper(宽度 = 图片宽)→ text-align:center 就是\n // 「图片底部中间」。wrapper 因此变高,而 left/right 手柄的 top:50% 是相对\n // wrapper 的(会偏到描述上)——把图片的真实高度写进 --tmr-image-h,\n // CSS 那边用 calc 的降级写法取它(见 content.module.css)。\n let syncCaption: ((attrs: Record<string, unknown>) => void) | null = null;\n if (nodeView.dom instanceof HTMLElement && resizable.element) {\n const { element } = resizable;\n const container = nodeView.dom;\n const captionEl = document.createElement('div');\n captionEl.className = 'tmr-image-caption';\n captionEl.dataset.imageCaption = '';\n (element.parentElement ?? container).appendChild(captionEl);\n\n const syncHandleTop = () => {\n const height = element.offsetHeight;\n if (height > 0) {\n container.style.setProperty('--tmr-image-h', `${height}px`);\n } else {\n // 高度拿不到(图片未加载 / 无布局环境)→ 撤掉变量,CSS 落回 50%\n container.style.removeProperty('--tmr-image-h');\n }\n };\n syncCaption = (attrs) => {\n const caption = normalizeImageCaption(attrs.caption) ?? '';\n captionEl.textContent = caption;\n captionEl.hidden = caption === '';\n syncHandleTop();\n };\n syncCaption(props.node.attrs);\n element.addEventListener('load', syncHandleTop);\n container.addEventListener('mousedown', applyMaxSize, true);\n }\n\n // 官方 onUpdate 把 width/height 划进「拖拽自己管」的集合直接跳过,于是程序化改\n // 尺寸(resetImageSize 清掉、宿主 updateAttributes)后数据变了、视觉不动。\n // 这里在 update 后把 style 对齐 attrs(图注文案也顺带同步);拖拽路径不经过\n // update,互不干扰。\n if (resizable.element) {\n const { element } = resizable;\n const originalUpdate = nodeView.update?.bind(nodeView);\n if (originalUpdate) {\n nodeView.update = (...args) => {\n const ok = originalUpdate(...args);\n if (ok) {\n const attrs = (args[0] as { attrs: Record<string, unknown> }).attrs;\n element.style.width = attrs.width ? `${attrs.width}px` : '';\n element.style.height = attrs.height ? `${attrs.height}px` : '';\n syncCaption?.(attrs);\n }\n return ok;\n };\n }\n }\n\n return nodeView;\n };\n },\n});\n\n/** 浮动条相对其定位上下文的偏移(数字按 px)。内部路径的上下文是编辑器,portal 路径是宿主容器。 */\nexport interface FindBarOffset {\n top?: number | string;\n right?: number | string;\n bottom?: number | string;\n left?: number | string;\n}\n\nconst DEFAULT_FIND_BAR_OFFSET: FindBarOffset = { top: 4, right: 4 };\n\nfunction findBarOffsetStyle(offset: FindBarOffset): CSSProperties {\n const px = (v: number | string | undefined) =>\n typeof v === 'number' ? `${v}px` : v;\n return {\n position: 'absolute',\n top: px(offset.top),\n right: px(offset.right),\n bottom: px(offset.bottom),\n left: px(offset.left),\n };\n}\n\n/**\n * 全文统计条。数字来自官方 CharacterCount 的 storage,`useEditorState` 只在\n * 字符数 / 词数变化时重渲染。\n */\nfunction CharacterCountStatus({\n editor,\n locale,\n}: {\n editor: Editor;\n locale: LocaleCode;\n}) {\n const t = getLocaleBundle(locale).characterCount;\n const counts = useEditorState({\n editor,\n selector: ({ editor: current }) => {\n const storage = current?.storage.characterCount;\n if (!storage) return { characters: 0, words: 0 };\n return {\n characters: storage.characters(),\n words: storage.words(),\n };\n },\n });\n\n return (\n <div\n className={styles.charCount}\n role=\"group\"\n aria-label={t.label}\n data-characters={counts.characters}\n data-words={counts.words}\n >\n <span>{t.characters(counts.characters)}</span>\n <span className={styles.charCountSep} aria-hidden=\"true\">\n ·\n </span>\n <span>{t.words(counts.words)}</span>\n </div>\n );\n}\n\n/**\n * 这次按键归本编辑器管吗。Cmd/Ctrl+F 与 Esc 共用同一套边界:\n * - 焦点在本编辑器内 → 管\n * - 焦点不在任何输入控件里(body)且页面上只有本编辑器 → 也管(单编辑器页面点完工具栏能直接按)\n * - 其余一律不管:同页多编辑器不会一起抢键,宿主的输入框与其它浮层也不会被夺走按键。\n */\nfunction editorOwnsKeyboard(editor: Editor): boolean {\n const active = document.activeElement;\n const inEditor = !!active && editor.view.dom.contains(active);\n if (inEditor || editor.isFocused) return true;\n const nothingFocused = active === null || active === document.body;\n return nothingFocused && document.querySelectorAll('.ProseMirror').length <= 1;\n}\n\nexport interface MarkdownWysiwygEditorHandle {\n /** 取当前正文的 markdown 字符串。 */\n getMarkdown: () => string;\n /** 取当前正文的 HTML 字符串。 */\n getHTML: () => string;\n /** 取当前正文的 Tiptap JSON。 */\n getJSON: () => Record<string, unknown>;\n /** 底层 Tiptap Editor 实例(可能为 null,未就绪时)。 */\n getEditor: () => Editor | null;\n /** 聚焦评论:设 active + 滚动 + 光标落到区间起点。找不到返回 false。 */\n focusComment: (commentId: string) => boolean;\n /** 按文档顺序跳转评论(next / prev),返回目标 commentId。 */\n nextComment: (dir?: 'next' | 'prev') => string | null;\n /** 当前 doc 内已锚定的去重 commentId 列表(文档顺序)。 */\n getCommentIds: () => string[];\n /** 打开编辑器自带的查找浮动条(宿主自己绑快捷键/按钮时用)。findBar={false} 时不生效。 */\n openFind: () => void;\n /** 收起自带浮动条,清空高亮并把焦点还给编辑器。findBar={false} 时不生效。 */\n closeFind: () => void;\n /**\n * 反转自带浮动条:关着就开、开着就关。工具栏那个带 active 态的放大镜接它\n * (`onSearch={() => handle.current?.toggleFind()}`)。findBar={false} 时不生效。\n */\n toggleFind: () => void;\n}\n\n/** 图片缩放的透传选项(官方 `@tiptap/extension-image` 的 resize 配置)。 */\nexport interface ImageResizeOptions {\n /**\n * 手柄方向,默认左右两条竖条(对齐官方 image-upload-node demo 的形态)。\n * 例:`['top-right', 'bottom-right']` 只留右侧两角;八个方向全给则四角 + 四边。\n */\n directions?: ResizableNodeViewDirection[];\n /** 最小宽(px),默认 80。 */\n minWidth?: number;\n /** 最小高(px),默认 80。 */\n minHeight?: number;\n /**\n * 最大宽(px)。**默认跟随编辑器内容区宽度**(拖不出布局,窗口变化时在下次拖拽前刷新\n * 上限);显式传值则以此为准。官方 `Image` 的 resize 没有 max 入口,由本库注入\n * `ResizableNodeView.maxSize`(该字段官方是公开的,拖拽约束每次都会读)。\n */\n maxWidth?: number;\n /** 最大高(px)。默认不单独限制(等比缩放时高度随宽度走)。 */\n maxHeight?: number;\n /**\n * 是否始终锁定宽高比,默认 true(本库默认)。等比下\"编辑态精确尺寸\"与只读渲染的\n * 按比例自适应(CSS `height: auto`)数学等价;显式关掉后,非等比拖出的图在只读\n * 渲染里会按原图比例回弹——markdown 里存的仍是精确值。\n */\n alwaysPreserveAspectRatio?: boolean;\n}\n\nexport interface MarkdownWysiwygEditorProps {\n /** 初始 markdown 内容。 */\n initialMarkdown?: string;\n /**\n * 是否可编辑,默认 true。false = 只读渲染,与编辑态同源同路径:\n * 同一套扩展 / NodeView / 样式,交互件(代码块语言选择、删除等)自动收起。\n * 只读态不应用评论锚定(comments 被忽略),与 MarkdownPreview 语义一致。\n */\n editable?: boolean;\n /** 内置界面语言,默认英文。工具栏等并列组件需各自传同一个值。目录跟着编辑器。 */\n locale?: LocaleCode;\n /**\n * 初始脚注来源:按 index 对齐 `[^n]`,写入 citationRef 的 url/title。\n * 仅影响初始 content;后续插入请用 `insertMarkdown(editor, md, sources)`。\n */\n sources?: SourceRef[];\n /**\n * 脚注圆标 NodeView 插槽。消费方用 Popover 包住 `defaultDom`,\n * 用 `index` 查自己的数据源。仅初始化时生效。\n */\n renderCitation?: RenderCitation;\n placeholder?: string;\n /** editor 实例就绪 / 销毁时回调,供外部工具栏使用。 */\n onEditorReady?: (editor: Editor | null) => void;\n /**\n * 目录变化回调(正文标题增删改时)。编辑器自己画左侧目录,\n * 这个回调只把同一份 `TocItem[]` 交给还想自己用数据的宿主。\n */\n onTocChange?: (items: TocItem[]) => void;\n /**\n * 编辑器内目录(默认关闭)。传入后,有标题时在左缘显示短横线,悬停向右展开,\n * 点击滚到对应标题。宽度小于 768px 时不显示。\n */\n toc?: boolean;\n /**\n * 粘贴纯文本时启发式检测 markdown 并自动转富文本(Shift+粘贴保持纯文本)。\n * 默认 true。仅初始化时生效。\n */\n markdownPaste?: boolean;\n /**\n * 支持把 .md / .markdown 文件拖拽或粘贴进编辑器,解析后插入到落点/光标处。\n * 默认 true。仅初始化时生效。\n */\n markdownFileDrop?: boolean;\n /** 追加的 Tiptap 扩展(在内置扩展之后注册)。 */\n extraExtensions?: AnyExtension[];\n /**\n * 「这段正在被 AI 改写」的高亮:宿主在提交请求时给上、回填或冲突时传空数组清空。\n * 走 decoration,**不进 markdown、不进 undo 历史**,能盖住表格与图表这类块级节点。\n */\n pendingAnchors?: PendingAnchor[];\n /** 点击待改写高亮区域时回调,给出锚点 id(宿主可用来跳回/取消)。 */\n onAnchorClick?: (id: string) => void;\n /**\n * 选区变化。宿主用它做「选中即出现引用」——塌缩成光标也会报(`empty: true`),\n * 好让引用跟着消失;`kind` 区分普通文字 / 表格单元格 / 整节点(图表)。\n */\n onSelectionChange?: (selection: {\n from: number;\n to: number;\n empty: boolean;\n kind: SelectionKind;\n }) => void;\n /** 附加到滚动容器的 class。 */\n className?: string;\n /**\n * 编辑态评论锚定:评论列表(含已解码 segments)。装载时映射为 commentAnchor\n * mark 并高亮;markdown 导出自动剥离,只读态(MarkdownPreview)不渲染评论。\n * 注意:数组引用变化会触发重新铺 mark,宿主应 memo 该数组。\n */\n comments?: CommentRef[];\n /** 受控 active 评论 id(sidebar 联动)。null = 无 active。 */\n activeCommentId?: string | null;\n /** 编辑器内点击 mark / gutter 时回调。 */\n onCommentClick?: (payload: CommentClickPayload) => void;\n /** active 变化回调(点击 mark / gutter / nextComment 时)。 */\n onActiveCommentChange?: (commentId: string | null) => void;\n /** 是否在 block 左缘渲染评论 gutter 气泡,默认 true。 */\n showCommentGutter?: boolean;\n /**\n * 编辑器内点评论 mark 是否可交互(设 active / 上报 click),默认 true。\n * 只靠侧栏单项驱动滚动定位时传 false。\n */\n commentInteractive?: boolean;\n /**\n * 启用查找替换(默认 true):注册官方 `@tiptap/extension-find-and-replace`\n * (自带命令 `setSearchTerm / replace / replaceAll / goToNextResult …` 与\n * `editor.storage.findAndReplace`)。传 false 则连扩展一起不注册——此时命令与 storage 都不存在,\n * 宿主的自绘面板也无从驱动。\n */\n findReplace?: boolean;\n /**\n * 是否由编辑器渲染浮动条(默认跟随 {@link findReplace})。传 false = **定位交给宿主**:\n * 扩展照旧注册,但编辑器不出条子,宿主自己在任意位置渲染 `<FindReplaceBar editor={…} />`\n * (与工具栏同一套分工——组件归库,摆位归宿主)。此时 {@link findShortcut} 与\n * `handle.openFind/closeFind` 一并失效,因为内部没有条子可开。\n */\n findBar?: boolean;\n /**\n * 把自带的浮动条挂到宿主的容器里(popup container 模式):给元素或返回元素的函数\n * 即可,开合状态与 Cmd/Ctrl+F 仍归库——只换挂载点。适合条子被 `overflow: hidden` 祖先裁掉、\n * 或想让它落在自己的头部 / 侧栏里。挂进去后**定位由宿主负责**(库不再加绝对定位),\n * 且容器若在主题子树之外,记得把 `--tmr-*` 变量也带到那里。\n * 返回 null / 不传时回落到编辑器内自带的浮动条。\n */\n findBarContainer?: HTMLElement | null | (() => HTMLElement | null);\n /**\n * 浮动条相对其定位上下文的偏移,默认 `{ top: 4, right: 4 }`(右上角)。数字按 px,也收 CSS 字符串。\n * 内部路径的上下文是编辑器(粘在滚动容器顶部的那层锚点),`findBarContainer` 路径的上下文是那个容器\n * ——那条路径下库会保证容器是定位上下文(computed position 为 static 时补 `position: relative`)。\n */\n findBarOffset?: FindBarOffset;\n /**\n * 焦点在编辑器内时接管 Cmd/Ctrl+F 打开**编辑器自带的**浮动条(默认 true;`findBar={false}`\n * 时不接管,免得抢了键却不弹东西)。传 false 则只保留 `handle.openFind()`——例如宿主想\n * 保留浏览器原生查找,或自己摆入口。焦点不在编辑器内时不接管,同页多编辑器不会互相抢。\n */\n findShortcut?: boolean;\n /**\n * 浮动条开合状态变化的通知(条子开着时 `handle.openFind()`、Esc / × 关掉都会触发)。\n * 典型用法是驱动别处的入口按钮:`<EditorToolbar onSearch={open} searchActive={isOpen} />`。\n * 只在真正变化时回调,挂载时不会先报一次 `false`;报的是「条子真的在屏幕上」——\n * `findBar={false}`(宿主自己摆条子)时恒为 `false`,不会因为 `openFind()` 空转就亮起入口。\n */\n onFindOpenChange?: (open: boolean) => void;\n /**\n * 启用快捷键抽屉:编辑器右下角出现键盘悬浮按钮,点开为\n * 「格式 / 快捷键 / Markdown」三列对照抽屉(默认 true,只读态隐藏)。\n */\n shortcutPanel?: boolean;\n /**\n * 把快捷键悬浮键挂到宿主的容器里(与 `findBarContainer` 同一套):\n * 给元素或返回元素的函数即可,开合状态与抽屉归库——只换按钮落点。\n * 挂进去后**定位由宿主负责**(库不再给按钮加粘性定位与右下角外边距),\n * 适合把按钮叠在宿主自己的浮层控件旁边(如「回到顶部」按钮上方)。\n * 返回 null / 不传时回落到编辑器内容右下的粘性按钮。\n */\n shortcutFabContainer?: HTMLElement | null | (() => HTMLElement | null);\n /**\n * 启用斜杠菜单:键入 `/`(行首或空白后)唤起块级插入弹窗(默认 true)。\n * 代码块内不触发,表格单元格内触发;只读态不激活。\n * 追加自定义命令项请传 `extraExtensions` 注册 `SlashMenu.configure({ items })`。\n */\n slashMenu?: boolean;\n /**\n * 智能标点(官方 `@tiptap/extension-typography`,MIT,不是 Pro)。\n * 键入时把直引号收成弯引号、`--` 收成破折号、`...` 收成省略号,以及箭头、©、分数等。\n * 代码块和行内代码里不触发;Backspace 撤销刚发生的替换。`---` 仍是分隔线。\n * 默认 true。仅初始化时生效。\n */\n typography?: boolean;\n /**\n * 表情建议列表:键入 `:`(行首或空白后)弹出,选中后插入 emoji 节点。\n * Markdown 存成 `:shortcode:`。传 false 只是不弹列表——节点、完整短码输入和往返仍在。\n * 只读态不弹出。默认 true。仅初始化时生效。\n */\n emojiSuggestion?: boolean;\n /**\n * 块拖拽(对齐 Tiptap Drag Context Menu):悬停块时左侧出现手柄,拖动重排,\n * 点击打开菜单(转成别的块 / 复制 / 再制一份 / 删除,图片还可下载)。\n * 手柄旁的「+」在块首插入 `/`,唤起斜杠菜单(`slashMenu={false}` 时不显示加号)。\n * 默认 true。只读态和窄屏(宽度小于 768px)不显示,并收起左侧留白。\n */\n blockDrag?: boolean;\n /**\n * 图片上传(对齐官方 ImageUploadNode 的交互):配置后工具栏的图片按钮变成\n * 「插入上传块」——文档里出现拖拽 / 点击的占位块,上传进度就地显示,全部成功后就地\n * 替换为图片。`upload` 是唯一必填项(网络 I/O 归宿主),签名 `(file, onProgress,\n * signal) => Promise<url>`;收 `signal` 后宿主应在移除 / 清空 / 占位块被删时中断请求。\n *\n * 挂在这里而不是工具栏的 `onImageUpload`:按钮的行为由「编辑器里有没有注册\n * imageUpload 扩展」决定,两处都传会以本配置为准。未配置时工具栏走旧的直传路径\n * (点按钮直接开文件框 → 上传 → 插图片)。仅初始化时生效。\n */\n imageUpload?: ImageUploadConfig;\n /**\n * 图片缩放(默认开):拖手柄改尺寸,松手把整数像素写进节点 attrs;markdown 里带尺寸的\n * 图以 `<img src alt width height>` 保真(没拖过的图保持标准 `![alt](url)`,见\n * imageMarkdown.ts)。传 `false` 关闭;传对象透传官方 resize 配置。仅初始化时生效。\n */\n imageResize?: boolean | ImageResizeOptions;\n /**\n * 全文统计(默认关):传 `true` 才注册 MIT 包 `@tiptap/extensions` 的 CharacterCount\n * (开源扩展,不是付费的 `@tiptap-pro`),在编辑器底部实时显示字符数与词数。\n * 统计条是本库自绘。词数把每个汉字算作 1 个词(该扩展默认按空格切,整段中文会是 1)。\n * 数字也可从 `editor.storage.characterCount` 读取。仅初始化时生效。\n */\n characterCount?: boolean;\n}\n\nfunction sameTocItems(a: TocItem[], b: TocItem[]): boolean {\n if (a.length !== b.length) return false;\n return a.every(\n (item, index) =>\n item.id === b[index].id &&\n item.level === b[index].level &&\n item.text === b[index].text,\n );\n}\n\n/**\n * 所见即所得 Markdown 编辑器(基于 Tiptap v3 + 官方 @tiptap/markdown)。\n * 内容进出均为 markdown 字符串。工具栏由外部渲染——通过 onEditorReady 拿到 editor 实例。\n */\nexport const MarkdownWysiwygEditor = forwardRef<\n MarkdownWysiwygEditorHandle,\n MarkdownWysiwygEditorProps\n>(function MarkdownWysiwygEditor(\n {\n initialMarkdown = '',\n editable = true,\n locale = 'en',\n sources,\n renderCitation,\n placeholder,\n onEditorReady,\n onTocChange,\n toc = false,\n markdownPaste = true,\n markdownFileDrop = true,\n extraExtensions,\n className,\n comments,\n activeCommentId,\n onCommentClick,\n onActiveCommentChange,\n showCommentGutter = true,\n commentInteractive = true,\n pendingAnchors,\n onAnchorClick,\n onSelectionChange,\n findReplace = true,\n findBar = findReplace,\n findBarContainer,\n findBarOffset = DEFAULT_FIND_BAR_OFFSET,\n findShortcut = true,\n onFindOpenChange,\n slashMenu = true,\n typography = true,\n emojiSuggestion = true,\n blockDrag = true,\n shortcutPanel = true,\n shortcutFabContainer,\n imageUpload,\n imageResize = true,\n characterCount = false,\n },\n ref,\n) {\n const preparedInitial = prepareChartMarkdown(\n enrichMarkdownCitations(initialMarkdown, sources ?? []),\n );\n\n const blockDragMobile = useIsMobile(768);\n const showBlockDrag = blockDrag && editable && !blockDragMobile;\n\n const [findOpen, setFindOpen] = useState(false);\n const [shortcutsOpen, setShortcutsOpen] = useState(false);\n const [tocItems, setTocItems] = useState<TocItem[]>([]);\n const findInputRef = useRef<HTMLInputElement | null>(null);\n const onTocChangeRef = useRef(onTocChange);\n onTocChangeRef.current = onTocChange;\n const tocItemsRef = useRef<TocItem[]>([]);\n\n const editor = useEditor({\n extensions: [\n ...baseExtensions.map((ext) =>\n ext.name === 'emoji'\n ? configureEmojiSuggestion(ext, emojiSuggestion)\n : ext,\n ),\n CodeBlock.configure({ lowlight }),\n ImageWithConfirmDelete.configure({\n inline: false,\n // 图片缩放(默认开、默认等比、默认左右两条手柄):false 关掉;对象透传官方配置\n // ——等比与手柄方向都有本库默认,显式传值可覆盖。\n resize:\n imageResize === false\n ? false\n : {\n enabled: true,\n alwaysPreserveAspectRatio: true,\n directions: ['left', 'right'] as ResizableNodeViewDirection[],\n // 默认下限。上限默认跟随内容区宽度(maxWidth/maxHeight 由 addNodeView\n // 包装读取并注入 maxSize,见 ImageWithConfirmDelete)。\n minWidth: 80,\n minHeight: 80,\n ...(typeof imageResize === 'object' ? imageResize : null),\n },\n }),\n // 图片上传占位块:空态拖拽 / 点选 → 进度就地 → 成功原地换成 image。\n // 不配置 imageUpload 就不注册(工具栏退回旧的直传路径)。\n ...(imageUpload ? [ImageUploadNode.configure(imageUpload)] : []),\n ImportPlaceholder,\n // 空文档占位符:官方 Placeholder 把 data-placeholder 属性与\n // is-empty / is-editor-empty 类打在**空文本块节点**上——CSS\n // (styles/content.module.css)正是在节点上 attr(data-placeholder)\n // 取值。早先只把属性挂在根 div 上,选择器永远匹配不到,占位符从没显示过。\n // showOnlyWhenEditable 默认 true,只读态自动不显示。\n ...(placeholder ? [Placeholder.configure({ placeholder })] : []),\n createChart({ editable: false }),\n createCitationRef({ renderCitation }),\n CommentMark,\n commentAnchorExtension({\n showGutter: showCommentGutter,\n interactive: commentInteractive,\n }),\n Markdown,\n pendingAnchorExtension,\n // 官方查找替换:只出命令 / storage / 装饰,UI 自绘(见 FindReplaceBar)。\n // - injectCSS 关掉:它默认会往页面插一个 <style>,绕开宿主的 --tmr-* 主题与 style.css,\n // 命中样式改由 src/styles/find.css 承担。\n // - searchDebounceMs 关掉:官方防抖走 setTimeout,抛错会落在异步回调里兜不住;\n // 防抖改由浮动条自己做(见 findReplace.ts)。\n ...(findReplace\n ? [FindAndReplace.configure({ injectCSS: false, searchDebounceMs: 0 })]\n : []),\n // 失焦选区:MIT 的 Selection。非空文本选区失焦后打上 .tmr-blur-selection,\n // 样式在 content.module.css。节点选区(图片、代码块整块)官方不保留。\n Selection.configure({ className: 'tmr-blur-selection' }),\n // 字数统计:MIT 的 @tiptap/extensions CharacterCount(不是 @tiptap-pro)。\n // 只调用它的 storage,统计条是本库自绘。无 limit,不裁剪正文。\n // 词数换成自写的 countWords,否则整段中文会被默认的按空格切分算成 1 个词。\n ...(characterCount\n ? [CharacterCount.configure({ wordCounter: countWords })]\n : []),\n TableOfContents.configure({\n getId: makeTocGetId(),\n // 官方扩展在滚动时也会 onUpdate(只为刷新 isActive)。目录项没变就不通知,\n // 否则每次滚动都 setState。回调走 ref:扩展只在创建时绑一次。\n onUpdate: (anchors) => {\n const next = anchors\n .filter((a) => a.originalLevel <= 6 && a.textContent.trim())\n .map((a) => ({\n id: a.id,\n level: a.originalLevel,\n text: a.textContent,\n }));\n if (sameTocItems(tocItemsRef.current, next)) return;\n tocItemsRef.current = next;\n setTocItems(next);\n onTocChangeRef.current?.(next);\n },\n }),\n ...(markdownPaste ? [MarkdownPaste] : []),\n ...(markdownFileDrop ? [MarkdownFileDrop] : []),\n // 斜杠菜单(键入 / 唤起):默认开,只读态由 suggestion 状态机自身短路\n ...(slashMenu ? [SlashMenu] : []),\n // 智能标点:输入规则,不增加节点,所以不进 baseExtensions。\n ...(typography ? [Typography] : []),\n ...(extraExtensions ?? []),\n ],\n content: preparedInitial,\n contentType: 'markdown',\n editable,\n // Next.js SSR:服务端不立即渲染,避免 hydration 不一致\n immediatelyRender: false,\n editorProps: {\n attributes: {\n class: styles.editorContent,\n },\n },\n });\n\n usePublishEditorLocale(editor, locale);\n\n useEffect(() => {\n onEditorReady?.(editor);\n return () => onEditorReady?.(null);\n }, [editor, onEditorReady]);\n\n // 运行时切换编辑/只读(同一实例,NodeView 交互件随 editor.isEditable 收起)。\n useEffect(() => {\n editor?.setEditable(editable);\n }, [editor, editable]);\n\n /**\n * 打开查找条。带着「再按一次」的语义:已经开着时聚焦并全选当前查询(首次打开由条子自己的\n * autoFocus 负责,此时查询多半是空串,`select()` 是空操作)。\n */\n const openFind = useCallback(() => {\n setFindOpen(true);\n requestAnimationFrame(() => findInputRef.current?.select());\n }, []);\n\n const closeFind = useCallback(() => {\n setFindOpen(false);\n // 焦点归还:Tiptap 的 focus 命令内部就是 requestAnimationFrame 延后执行的\n // (`delayedFocus`),正好落在 React 卸载浮动条之后——否则被卸载的输入框会把焦点丢给 body。\n editor?.commands.focus();\n }, [editor]);\n\n /**\n * 反转查找条:关着就开、开着就关。工具栏那个放大镜接的是它——带 active 态的按钮点一下得能灭。\n * `openFind()` 保持「已开着时重聚焦并全选查询」的语义,和 Cmd/Ctrl+F(同一个手势)一致。\n */\n const toggleFind = useCallback(() => {\n if (findOpen) closeFind();\n else openFind();\n }, [findOpen, closeFind, openFind]);\n\n // 开合状态上报宿主(回调走 ref 防 stale)。报的是「条子真的在屏幕上」——`findBar={false}`\n // 时宿主自己摆条子,`findOpen` 翻起来也不该点亮别处的入口。只在真正变化时回调,\n // 挂载时不先报一次 false。\n const findBarOpen = findOpen && findBar && findReplace;\n const onFindOpenChangeRef = useRef(onFindOpenChange);\n onFindOpenChangeRef.current = onFindOpenChange;\n // 初值取 `false`(`findOpen` 的初值):挂载那次比较相等,不会回调一次「本来就是 false」。\n const notifiedFindOpenRef = useRef(false);\n useEffect(() => {\n if (notifiedFindOpenRef.current === findBarOpen) return;\n notifiedFindOpenRef.current = findBarOpen;\n onFindOpenChangeRef.current?.(findBarOpen);\n }, [findBarOpen]);\n\n // 条子随本实例一起消失时(换 key、换文档、切路由),宿主的入口不该继续亮着——新实例的\n // `notifiedFindOpenRef` 初值是 false、`findBarOpen` 也是 false,两者相等就不会回调,于是\n // 上一实例报过的那个 `true` 没人撤销。所以卸载时补发一次。StrictMode 的模拟卸载不会误报\n // (那时条子并没开着,值就是 false)。\n const findBarOpenRef = useRef(findBarOpen);\n findBarOpenRef.current = findBarOpen;\n useEffect(\n () => () => {\n if (findBarOpenRef.current) onFindOpenChangeRef.current?.(false);\n },\n [],\n );\n\n // Cmd/Ctrl+F → 打开查找条。接管边界见 editorOwnsKeyboard()。\n // 只有容器里真有那条浮动条(findBar)时才绑:否则会白抢浏览器原生查找,却什么都不弹。\n useEffect(() => {\n if (!editor || !findReplace || !findBar || !findShortcut) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) {\n return;\n }\n if (event.key !== 'f' && event.key !== 'F') return;\n if (!editorOwnsKeyboard(editor)) return;\n event.preventDefault();\n openFind();\n };\n document.addEventListener('keydown', onKeyDown, true);\n return () => document.removeEventListener('keydown', onKeyDown, true);\n }, [editor, findReplace, findBar, findShortcut, openFind]);\n\n // Esc → 关掉查找条。条子自己那份 onKeyDown 只在焦点位于条子内时才收得到,用户点回正文继续\n // 编辑后想收条子就只能去点 ×;这里按同一套边界(editorOwnsKeyboard)补上「焦点在正文 /\n // 页面只有本编辑器且焦点在 body」的情况。消费掉这次 Esc(preventDefault + stopPropagation),\n // 免得宿主的浮动层跟着一起关;输入法组合中的 Esc 是「取消候选词」,放行。\n //\n // 注意**不要**用 `event.defaultPrevented` 判断「内层已经处理过、让给它」:实测在真实编辑器里\n // Escape 一定已被 preventDefault——prosemirror-view 的 keydown 一旦有 handleKeyDown 返回 true\n // 就 preventDefault(`node_modules/prosemirror-view/src/input.ts:136`),斜杠菜单等就在这条路上。\n // 那样写这条分支永远不会走。边界交给 editorOwnsKeyboard:焦点在别的浮层 / 宿主的输入框里时\n // 它本来就是 false,不会去抢。\n useEffect(() => {\n if (!editor || !findBarOpen) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n if (isComposingKeyEvent(event)) return;\n if (!editorOwnsKeyboard(editor)) return;\n event.preventDefault();\n event.stopPropagation();\n closeFind();\n };\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [editor, findBarOpen, closeFind]);\n\n // 评论列表 → 铺 mark。用 commentId 签名做防抖:宿主每次渲染传新数组引用时\n // 不会反复清/铺(清空再重铺会丢掉 mark 随编辑移动后的位置)。\n const commentsSignatureRef = useRef<string>('');\n useEffect(() => {\n // 只读态不接评论(与库的「评论锚定 = 编辑会话专属」契约一致)。\n if (!editor || !editable) return;\n const signature = (comments ?? [])\n .map((c) => `${c.commentId}:${c.segments.length}`)\n .join('|');\n if (signature === commentsSignatureRef.current) return;\n commentsSignatureRef.current = signature;\n applyCommentAnchorsToEditor(editor, comments ?? []);\n }, [editor, editable, comments]);\n\n // active 同步到插件(mark/block 高亮装饰);滚动定位走 handle.focusComment。\n useEffect(() => {\n if (!editor) return;\n editor.view.dispatch(\n editor.state.tr.setMeta(\n COMMENT_ACTIVE_META,\n activeCommentId ?? null,\n ),\n );\n }, [editor, activeCommentId]);\n\n // 待改写高亮:会话级装饰,不进 markdown / 不进 undo(宿主推入,回填或冲突时清空)。\n useEffect(() => {\n if (!editor) return;\n setPendingAnchors(editor, pendingAnchors ?? []);\n }, [editor, pendingAnchors]);\n\n // 点击待改写高亮 → 上报锚点 id。走 DOM 监听而不是插件 meta:这里只要「点了哪一段」。\n const onAnchorClickRef = useRef(onAnchorClick);\n useEffect(() => {\n onAnchorClickRef.current = onAnchorClick;\n }, [onAnchorClick]);\n useEffect(() => {\n if (!editor) return;\n const dom = editor.view.dom;\n const onClick = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n const id = target?.closest?.('[data-pending-id]')?.getAttribute('data-pending-id');\n if (id) onAnchorClickRef.current?.(id);\n };\n dom.addEventListener('click', onClick);\n return () => dom.removeEventListener('click', onClick);\n }, [editor]);\n\n // 选区变化:宿主据此做「选中即出现引用 / 引用跟着选区走」。\n const onSelectionChangeRef = useRef(onSelectionChange);\n useEffect(() => {\n onSelectionChangeRef.current = onSelectionChange;\n }, [onSelectionChange]);\n useEffect(() => {\n if (!editor) return;\n const onSelectionUpdate = () => {\n const { from, to, empty } = editor.state.selection;\n onSelectionChangeRef.current?.({\n from,\n to,\n empty,\n kind: selectionKind(editor.state.selection),\n });\n };\n editor.on('selectionUpdate', onSelectionUpdate);\n return () => {\n editor.off('selectionUpdate', onSelectionUpdate);\n };\n }, [editor]);\n\n // mark / gutter 点击 → 通过 transaction meta 上报宿主(回调走 ref 防 stale)。\n const onCommentClickRef = useRef(onCommentClick);\n const onActiveCommentChangeRef = useRef(onActiveCommentChange);\n onCommentClickRef.current = onCommentClick;\n onActiveCommentChangeRef.current = onActiveCommentChange;\n useEffect(() => {\n if (!editor) return;\n const onTransaction = ({\n transaction,\n }: {\n transaction: { getMeta: (key: string) => unknown };\n }) => {\n const click = transaction.getMeta(COMMENT_CLICK_META) as\n | CommentClickPayload\n | undefined;\n if (click) {\n onCommentClickRef.current?.(click);\n onActiveCommentChangeRef.current?.(click.commentIds[0] ?? null);\n }\n const gutter = transaction.getMeta(COMMENT_GUTTER_META) as\n | { commentIds: string[]; pos: number }\n | undefined;\n if (gutter) {\n onCommentClickRef.current?.({\n commentIds: gutter.commentIds,\n anchorEl: null,\n pos: gutter.pos,\n });\n onActiveCommentChangeRef.current?.(gutter.commentIds[0] ?? null);\n }\n };\n editor.on('transaction', onTransaction);\n return () => {\n editor.off('transaction', onTransaction);\n };\n }, [editor]);\n\n useImperativeHandle(\n ref,\n () => ({\n getMarkdown: () => editor?.getMarkdown?.() ?? '',\n getHTML: () => editor?.getHTML?.() ?? '',\n getJSON: () => (editor?.getJSON?.() as Record<string, unknown>) ?? {},\n getEditor: () => editor ?? null,\n focusComment: (commentId: string) =>\n editor ? focusComment(editor, commentId) : false,\n nextComment: (dir?: 'next' | 'prev') =>\n editor ? nextComment(editor, dir ?? 'next') : null,\n getCommentIds: () => (editor ? collectCommentIds(editor) : []),\n openFind: () => openFind(),\n closeFind,\n toggleFind: () => toggleFind(),\n }),\n [editor, closeFind, openFind, toggleFind],\n );\n\n // 宿主指定了容器就把条子 portal 进去;解析不到则回落到编辑器内的粘性锚点。\n const findBarTarget =\n typeof findBarContainer === 'function'\n ? findBarContainer()\n : (findBarContainer ?? null);\n\n // 条子是绝对定位的:容器得先是定位上下文。宿主容器是 static 时补一个 relative——\n // 不补的话条子会以「最近的定位祖先」为基准(可能是页面),位置会莫名其妙。\n useEffect(() => {\n if (!findBarTarget) return;\n if (getComputedStyle(findBarTarget).position === 'static') {\n findBarTarget.style.position = 'relative';\n }\n }, [findBarTarget]);\n\n const findBarNode =\n editor && findBar && findOpen ? (\n <FindReplaceBar\n editor={editor}\n locale={locale}\n onClose={closeFind}\n inputRef={findInputRef}\n className={styles.findBar}\n style={findBarOffsetStyle(findBarOffset)}\n />\n ) : null;\n\n const shortcutT = getLocaleBundle(locale).shortcut;\n\n // 宿主指定了容器就把悬浮键 portal 进去(定位归宿主),解析不到回落编辑器内粘性按钮。\n // 用 layout effect(同步于提交后、绘制前)解析:函数形式读 ref 时首渲染拿到的是\n // null,等 DOM 提交后 ref 才就绪——这里在绘制前再解析一次,避免闪一帧编辑器内按钮;\n // setState 同值会 bail out,不会死循环。\n const [shortcutFabTarget, setShortcutFabTarget] = useState<HTMLElement | null>(\n null,\n );\n const useIsoLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n useIsoLayoutEffect(() => {\n const target =\n typeof shortcutFabContainer === 'function'\n ? shortcutFabContainer()\n : (shortcutFabContainer ?? null);\n setShortcutFabTarget((prev) => (prev === target ? prev : target));\n });\n\n const shortcutFabNode =\n shortcutPanel && editable ? (\n <button\n type=\"button\"\n className={\n shortcutFabTarget\n ? `${styles.shortcutFab} ${styles.shortcutFabHosted}`\n : styles.shortcutFab\n }\n title={shortcutT.panelTitle}\n aria-label={shortcutT.panelTitle}\n aria-expanded={shortcutsOpen}\n onClick={() => setShortcutsOpen((v) => !v)}\n >\n <KeyboardIcon size={18} />\n </button>\n ) : null;\n\n return (\n <div\n className={styles.editorHost}\n data-block-drag-gutter={showBlockDrag ? '' : undefined}\n >\n {/* 粘性锚点排在正文之前:浮动条跟着最近的滚动容器走,正文滚动时不会被卷上去 */}\n {findBarNode && !findBarTarget ? (\n <div className={styles.findBarAnchor}>{findBarNode}</div>\n ) : null}\n {findBarNode && findBarTarget\n ? createPortal(findBarNode, findBarTarget)\n : null}\n {editor && toc && !blockDragMobile && tocItems.length > 0 ? (\n <EditorToc\n items={tocItems}\n editorDom={editor.view.dom}\n locale={locale}\n />\n ) : null}\n <EditorContent\n editor={editor}\n className={\n [\n styles.editorScroll,\n showBlockDrag ? styles.withBlockDrag : '',\n toc && !blockDragMobile && tocItems.length > 0 ? styles.withToc : '',\n className ?? '',\n ]\n .filter(Boolean)\n .join(' ')\n }\n />\n {editor && showBlockDrag ? (\n <BlockDragHandle editor={editor} />\n ) : null}\n {/* 统计开着时,计数与键盘悬浮键共用一条贴在右下的 sticky 底栏。\n 统计关着时悬浮键保持自己的粘性定位,不套这层——外层再包一个不粘的盒子会让 sticky 失效。 */}\n {editor && characterCount ? (\n <div className={styles.editorStatus}>\n <CharacterCountStatus editor={editor} locale={locale} />\n {shortcutFabNode && !shortcutFabTarget ? shortcutFabNode : null}\n </div>\n ) : shortcutFabNode && !shortcutFabTarget ? (\n shortcutFabNode\n ) : null}\n {/* 快捷键抽屉入口:宿主给了 shortcutFabContainer 就 portal 进去,定位归宿主。只读态隐藏 */}\n {shortcutFabNode && shortcutFabTarget\n ? createPortal(shortcutFabNode, shortcutFabTarget)\n : null}\n {shortcutPanel && editable ? (\n <ShortcutPanel\n open={shortcutsOpen}\n onClose={() => setShortcutsOpen(false)}\n locale={locale}\n />\n ) : null}\n </div>\n );\n});\n","'use client';\n\nimport { TableOfContents } from '@tiptap/extension-table-of-contents';\nimport { Markdown } from '@tiptap/markdown';\nimport { EditorContent, ReactNodeViewRenderer, useEditor } from '@tiptap/react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { type LocaleCode } from '../i18n';\nimport { usePublishEditorLocale } from '../i18n/store';\nimport { useIsMobile } from '../hooks/useIsMobile';\nimport type { TocItem } from '../toc/extractToc';\nimport { makeTocGetId } from '../toc/tocSlug';\nimport { sameTocItems } from '../toc/readTocItems';\nimport { EditorToc } from './EditorToc';\nimport { createChart } from '../chart/createChart';\nimport { prepareChartMarkdown } from '../chart/prepareChartMarkdown';\nimport {\n enrichMarkdownCitations,\n type SourceRef,\n} from '../citationUtils';\nimport type { RenderCitation } from '../citationTypes';\nimport { createCitationRef } from '../createCitationRef';\nimport { baseExtensions, pureCodeBlock, pureImage } from '../extensions';\nimport { CodeBlockView } from './CodeBlockView';\nimport '../styles/chart.css';\nimport styles from '../styles/content.module.css';\n\nexport interface MarkdownPreviewProps {\n /** 要预览的 markdown 字符串。 */\n markdown: string;\n /**\n * 脚注来源列表:按 `index` 与正文 `[^n]` 对齐,写入节点的 url/title。\n * 业务数据与 Popover 仍由消费方通过 {@link renderCitation} 完成。\n */\n sources?: SourceRef[];\n /**\n * 脚注圆标 NodeView 插槽。用 Popover 等包住 `defaultDom`;\n * 用 `index` / `attrs` 查消费方自己的数据源。\n */\n renderCitation?: RenderCitation;\n className?: string;\n /** 内置界面语言,默认英文。代码块、图表和目录文案跟着变。 */\n locale?: LocaleCode;\n /**\n * 内置左缘目录(默认关闭)。传入后,有标题时显示短横线,悬停展开,点击滚动。\n * 宽度小于 768px 时不显示。\n */\n toc?: boolean;\n}\n\n/**\n * 轻量只读 markdown 预览(Tiptap + 官方 @tiptap/markdown,editable:false)。\n * 与 MarkdownWysiwygEditor 共用同一套 base 扩展与正文样式类,渲染同源——\n * 预览所见即编辑/插入后所得。用纯版 CodeBlock/Image(无 React 视图 / 删除快捷键);\n * 图表用只读 NodeView(可渲染,不可编辑弹层)。\n *\n * 这是「客户端只读预览」;若需 SEO / 无 JS 静态渲染,请改用 server 入口的\n * renderReportHtml + ReportContent / ReportContentWithCharts。\n */\nexport function MarkdownPreview({\n markdown,\n sources,\n renderCitation,\n className,\n locale = 'en',\n toc = false,\n}: MarkdownPreviewProps) {\n const mobile = useIsMobile(768);\n const [tocItems, setTocItems] = useState<TocItem[]>([]);\n const tocItemsRef = useRef<TocItem[]>([]);\n const prepared = useMemo(\n () =>\n prepareChartMarkdown(\n enrichMarkdownCitations(markdown, sources ?? []),\n ),\n [markdown, sources],\n );\n\n // 预览代码块挂只读 NodeView(语言标签 + 复制 + 展开收起 + 行号槽)。\n // 仅客户端入口挂;server 入口(renderReportHtml)保持纯版,静态 HTML 零 React 依赖。\n const previewCodeBlock = useMemo(\n () =>\n pureCodeBlock.extend({\n addNodeView() {\n return ReactNodeViewRenderer(CodeBlockView);\n },\n }),\n [],\n );\n\n const extensions = useMemo(\n () => [\n ...baseExtensions,\n previewCodeBlock,\n pureImage,\n createChart({ editable: false }),\n createCitationRef({ renderCitation }),\n Markdown,\n TableOfContents.configure({\n getId: makeTocGetId(),\n onUpdate: (anchors) => {\n const next = anchors\n .filter((anchor) => anchor.originalLevel <= 6 && anchor.textContent.trim())\n .map((anchor) => ({\n id: anchor.id,\n level: anchor.originalLevel,\n text: anchor.textContent,\n }));\n if (sameTocItems(tocItemsRef.current, next)) return;\n tocItemsRef.current = next;\n setTocItems(next);\n },\n }),\n ],\n [previewCodeBlock, renderCitation],\n );\n\n const editor = useEditor(\n {\n extensions,\n content: prepared,\n contentType: 'markdown',\n editable: false,\n immediatelyRender: false,\n editorProps: {\n attributes: { class: styles.editorContent },\n },\n },\n [extensions],\n );\n\n usePublishEditorLocale(editor, locale);\n\n // 文档尾随空段不在这里处理:它是 StarterKit 的 TrailingNode 补的(文末是代码块/\n // 表格/图表时),事后删节点会被它重建;改成 appendTransaction 里删则会与它互相\n // 触发形成死循环。只读预览由消费方用 CSS 隐藏该空段即可。\n\n useEffect(() => {\n if (!editor) return;\n editor.commands.setContent(prepared, { contentType: 'markdown' });\n }, [editor, prepared]);\n\n const showToc = Boolean(editor && toc && !mobile && tocItems.length > 0);\n\n return (\n <div className={styles.editorHost}>\n {showToc && editor ? (\n <EditorToc items={tocItems} editorDom={editor.view.dom} locale={locale} />\n ) : null}\n <EditorContent\n editor={editor}\n className={[className ?? styles.editorScroll, showToc ? styles.withToc : '']\n .filter(Boolean)\n .join(' ')}\n />\n </div>\n );\n}\n","import type { ToolbarLabels } from './labels';\n\n/** 包内唯一识别的扩展名:命中即本地读文本,其余一律交给宿主回调。 */\nexport const MD_FILE_RE = /\\.(md|markdown)$/i;\nexport const MD_ACCEPT = '.md,.markdown,text/markdown';\n\n/** 主栏导入下拉里的一项。`accept` 写到隐藏 file input。 */\nexport interface ImportMenuItem {\n key: string;\n label: string;\n accept: string;\n}\n\nexport function resolveImportMenuItems({\n labels,\n onImportDocument,\n importAccept,\n importMenuItems,\n}: {\n labels: ToolbarLabels;\n onImportDocument?: unknown;\n importAccept?: string;\n importMenuItems?: ImportMenuItem[];\n}): ImportMenuItem[] {\n if (importMenuItems?.length) return importMenuItems;\n const markdown: ImportMenuItem = {\n key: 'markdown',\n label: labels.importMarkdown,\n accept: MD_ACCEPT,\n };\n if (onImportDocument && importAccept) {\n return [\n markdown,\n {\n key: 'document',\n label: labels.importDocumentOther,\n accept: importAccept,\n },\n ];\n }\n return [markdown];\n}\n","import '@tiptap/markdown';\nimport type { Editor } from '@tiptap/react';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\nimport {\n enrichMarkdownCitations,\n type SourceRef,\n} from './citationUtils';\n\n/**\n * 在光标处插入 markdown 片段(封装 contentType: 'markdown')。\n * 传入 `sources` 时会先 enrich `[^n]` → 带 url 的 citation-ref,再插入。\n *\n * 整段过不了 schema(嵌套 bold、块级图进 listItem 等)时按块重试,\n * 单块仍挂则剥标记当段落。某一处脏输出不至于整次插入失败。\n */\nexport function insertMarkdown(\n editor: Editor,\n markdown: string,\n sources: SourceRef[] = [],\n): void {\n const prepared = prepareChartMarkdown(\n enrichMarkdownCitations(markdown, sources),\n );\n if (!prepared) return;\n if (tryInsertMarkdown(editor, prepared)) return;\n for (const block of splitTopLevelBlocks(prepared)) {\n if (tryInsertMarkdown(editor, block)) continue;\n insertPlainParagraph(editor, stripToPlainText(block));\n }\n}\n\nfunction tryInsertMarkdown(editor: Editor, markdown: string): boolean {\n try {\n return editor\n .chain()\n .focus()\n .insertContent(markdown, { contentType: 'markdown' })\n .run();\n } catch {\n return false;\n }\n}\n\nfunction insertPlainParagraph(editor: Editor, text: string): void {\n if (!text) return;\n editor\n .chain()\n .focus()\n .insertContent({\n type: 'paragraph',\n content: [{ type: 'text', text }],\n })\n .run();\n}\n\n/** 围栏代码整块留下,其余按空行切。 */\nexport function splitTopLevelBlocks(markdown: string): string[] {\n const out: string[] = [];\n for (const [i, part] of markdown.split(/(```[\\s\\S]*?```)/).entries()) {\n if (!part) continue;\n if (i % 2 === 1) {\n out.push(part);\n continue;\n }\n for (const block of part.split(/\\n{2,}/)) {\n if (block.trim()) out.push(block);\n }\n }\n return out;\n}\n\nexport function stripToPlainText(markdown: string): string {\n return markdown\n .replace(/!\\[[^\\]]*\\]\\([^)]+\\)/g, '')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/^#{1,6}\\s+/gm, '')\n .replace(/^[ \\t]*[-*+]\\s+/gm, '')\n .replace(/^[ \\t]*\\d+\\.\\s+/gm, '')\n .replace(/[*_~`]+/g, '')\n .replace(/<[^>]+>/g, '')\n .replace(/\\|/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n","'use client';\n\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport styles from '../styles/colorPalette.module.css';\n\n/**\n * 色板矩阵:10 个色相 × 6 个明度。\n * 第 1 行为纯色(最饱和),下方 5 行为同色相的明→暗变体。\n */\nconst PALETTE: string[][] = [\n ['#8b0000', '#ff0000', '#ff9100', '#ffeb00', '#00e000', '#00e5ff', '#4a86e8', '#0000ff', '#9900ff', '#ff00ff'],\n ['#dba0a0', '#e8b4c0', '#f5d9b8', '#f7f0c0', '#cfe8c4', '#bcd9d4', '#c4d4ec', '#cdd4e8', '#d6c4e0', '#e8c4dc'],\n ['#d28a8a', '#dd8c9c', '#eec49a', '#f0e29c', '#a9d4a0', '#94c0bc', '#a0bce4', '#a4b0d8', '#bca0d0', '#d49cc0'],\n ['#cc4444', '#d4506c', '#e89a5c', '#e8d05c', '#7cc06c', '#5ca09c', '#5c90e0', '#6478c8', '#9460c0', '#c45c98'],\n ['#a02020', '#b01030', '#c87410', '#c8a810', '#449030', '#2c706c', '#3060c0', '#1850a0', '#6428a0', '#a02868'],\n ['#5c1010', '#700818', '#7c4808', '#7c6808', '#205818', '#10403c', '#183c78', '#0c3060', '#3c1460', '#601838'],\n];\n\n// THEME 品牌色\nconst THEME_COLORS = ['#ff6719', '#ff5500'];\n\nexport interface ColorPaletteProps {\n /** 当前已应用的颜色(用于打勾),无则不选中。 */\n value?: string;\n /** 点击某色块。null 表示「None」(清除)。 */\n onPick: (color: string | null) => void;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n}\n\n/** 颜色面板:None 清除行 + 色板网格 + THEME 品牌色。 */\nexport function ColorPalette({ value, onPick, locale = 'en' }: ColorPaletteProps) {\n const t = getLocaleBundle(locale).palette;\n const current = value?.toLowerCase();\n\n return (\n <div className={styles.palette}>\n <button\n type=\"button\"\n className={styles.noneRow}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => onPick(null)}\n >\n <span className={styles.noneIcon} aria-hidden />\n <span className={styles.noneLabel}>{t.none}</span>\n </button>\n\n <div className={styles.grid}>\n {PALETTE.flat().map((c) => (\n <button\n key={c}\n type=\"button\"\n className={`${styles.swatch} ${\n current === c.toLowerCase() ? styles.swatchActive : ''\n }`}\n style={{ background: c }}\n title={c}\n aria-label={c}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => onPick(c)}\n />\n ))}\n </div>\n\n <div className={styles.themeLabel}>{t.theme}</div>\n <div className={styles.themeRow}>\n {THEME_COLORS.map((c) => (\n <button\n key={c}\n type=\"button\"\n className={`${styles.swatch} ${\n current === c.toLowerCase() ? styles.swatchActive : ''\n }`}\n style={{ background: c }}\n title={c}\n aria-label={c}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => onPick(c)}\n />\n ))}\n </div>\n </div>\n );\n}\n","import type { Editor } from '@tiptap/core';\nimport { findImagePos } from './imageAlign';\nimport { normalizeImageCaption } from './imageMarkdown';\n\n/**\n * 按文档位置设置图片描述(图注)——悬停工具条用(鼠标飘过图片时图片未必被\n * 选中,走不了 selection;也不该为此改变选区)。空串 / null 清除描述。\n */\nexport function setImageCaptionAtPos(\n editor: Editor,\n pos: number,\n caption: string | null,\n): boolean {\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return false;\n const next = normalizeImageCaption(caption);\n const current = normalizeImageCaption(node.attrs.caption);\n if (current === next) return true; // 未变化:不写空事务\n editor.view.dispatch(editor.state.tr.setNodeAttribute(pos, 'caption', next));\n return true;\n}\n\n/**\n * 设置「当前图片」(选中 / 光标紧邻)的描述。attrs 存 `caption`;markdown 里带描述的\n * 图以 `<figure><img …><figcaption>…</figcaption></figure>` 保真(见 imageMarkdown.ts)。\n * 返回是否找到图片。\n */\nexport function setImageCaption(editor: Editor, caption: string | null): boolean {\n const pos = findImagePos(editor);\n if (pos == null) return false;\n return setImageCaptionAtPos(editor, pos, caption);\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/core';\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ComponentType,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n AlignCenterIcon,\n AlignLeftIcon,\n AlignRightIcon,\n CaptionsIcon,\n type IconProps,\n} from '../icons';\nimport type { ToolbarLabels } from '../labels';\nimport { setImageCaptionAtPos } from '../imageCaption';\nimport { setImageAlignAtPos } from '../imageAlign';\nimport { normalizeImageAlign, normalizeImageCaption, type ImageAlign } from '../imageMarkdown';\nimport styles from '../styles/imageAlignTools.module.css';\n\nexport interface ImageAlignToolsProps {\n /** 编辑器实例。只读态不出工具条。 */\n editor: Editor;\n /** 已合并默认值的工具栏文案(工具条与三个按钮的 aria-label 都取自它)。 */\n labels: ToolbarLabels;\n}\n\n/** 悬停图片时的几何与位置(工具条 fixed 定位,直接用视口坐标)。 */\ninterface HoverImage {\n /**\n * 图片节点在文档里的位置:attrs 的读写都走它,**不要长期持有 img 元素引用**——\n * attrs 更新时 prosemirror 会替换裸渲染的 img(旧引用脱离文档、读不到新值)。\n */\n pos: number;\n /** 当前渲染的元素(只用于量几何;每次重测都按 pos 重取)。 */\n img: HTMLImageElement;\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\n/** 工具条高度(含内边距)与它和图片之间的间距;上方放不下就翻到图片下方。 */\nconst TOOLBAR_HEIGHT = 34;\nconst TOOLBAR_GAP = 8;\n/**\n * 指针离开图片后工具条再活一会儿(毫秒):走向工具条的路上有一段「既不在图片上、\n * 也不在工具条上」的缝隙,立刻收起就会点不到(与表格手柄同一套悬停意图)。\n */\nconst HOVER_KEEP_MS = 160;\n\nconst ALIGNS: ImageAlign[] = ['left', 'center', 'right'];\n\nconst ICONS: Record<ImageAlign, ComponentType<IconProps>> = {\n left: AlignLeftIcon,\n center: AlignCenterIcon,\n right: AlignRightIcon,\n};\n\nfunction measureImage(editor: Editor, img: HTMLImageElement): HoverImage | null {\n if (!img.isConnected) return null;\n let pos: number;\n try {\n pos = editor.view.posAtDOM(img, 0);\n } catch {\n return null;\n }\n const node = editor.state.doc.nodeAt(pos);\n if (!node || node.type.name !== 'image') return null;\n const rect = img.getBoundingClientRect();\n return {\n pos,\n img,\n left: rect.left,\n top: rect.top,\n right: rect.right,\n bottom: rect.bottom,\n };\n}\n\n/**\n * 图片对齐工具条(悬停式,与表格悬停手柄同一套交互):鼠标落在图片上时,图片上方\n * (放不下就下方)浮出一排左 / 中 / 右按钮,点一下即对齐,当前对齐方向高亮。\n *\n * fixed 定位 + portal 到 body:它要贴着图片边缘,而工具栏并不在编辑器 DOM 内。\n * 滚动 / 缩放时重新量悬停的那张图让工具条跟着走。\n *\n * 点击走 {@link setImageAlignAt}(按 DOM 位置改 attrs):鼠标只是飘过图片时图片\n * 并未被选中,不能像快捷键那样走 selection。\n */\nexport function ImageAlignTools({ editor, labels }: ImageAlignToolsProps) {\n const [hover, setHover] = useState<HoverImage | null>(null);\n const hoverRef = useRef<HoverImage | null>(null);\n /** 描述编辑态:null = 未在编辑;string = 输入中的草稿。 */\n const [captionDraft, setCaptionDraft] = useState<string | null>(null);\n const captionDraftRef = useRef<string | null>(null);\n captionDraftRef.current = captionDraft;\n\n const clearHover = useCallback(() => {\n if (!hoverRef.current) return;\n hoverRef.current = null;\n setHover(null);\n setCaptionDraft(null);\n }, []);\n\n /** 重新量一遍悬停的那张图(滚动 / 缩放 / 属性更新后坐标与元素都会变样)。 */\n const remeasureHover = useCallback(() => {\n const current = hoverRef.current;\n if (!current) return;\n // 按 pos 重取元素:attrs 更新会替换裸渲染的 img,旧引用已脱离文档。\n // 开缩放时图片有 NodeView,nodeDOM 返回的是容器([data-resize-container]),\n // 要从容器里再找 img。\n const el = editor.view.nodeDOM(current.pos);\n const img =\n el instanceof HTMLImageElement\n ? el\n : el instanceof HTMLElement\n ? el.querySelector('img')\n : null;\n const next = img ? measureImage(editor, img) : null;\n hoverRef.current = next;\n setHover(next);\n }, [editor]);\n\n const clearTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const cancelPendingClear = useCallback(() => {\n if (clearTimerRef.current === null) return;\n clearTimeout(clearTimerRef.current);\n clearTimerRef.current = null;\n }, []);\n\n useEffect(() => {\n const onMove = (event: MouseEvent) => {\n if (!editor.isEditable) return;\n const target = event.target as HTMLElement | null;\n\n // 指针已经到工具条上:留着,并取消待收起\n if (target?.closest?.('[data-image-align]')) {\n cancelPendingClear();\n return;\n }\n\n const img =\n target && editor.view.dom.contains(target)\n ? (target.closest('img') as HTMLImageElement | null)\n : null;\n\n if (!img) {\n // 正在输入描述:不自动收(用户可能在拖选文字 / 鼠标短暂移开)\n if (captionDraftRef.current !== null) return;\n // 既不在图片也不在工具条上:宽限一段再收(指针可能正走向工具条)\n if (hoverRef.current && clearTimerRef.current === null) {\n clearTimerRef.current = setTimeout(() => {\n clearTimerRef.current = null;\n clearHover();\n }, HOVER_KEEP_MS);\n }\n return;\n }\n\n cancelPendingClear();\n if (hoverRef.current?.img === img) return;\n const next = measureImage(editor, img);\n hoverRef.current = next;\n setHover(next);\n };\n const onBlurWindow = () => {\n cancelPendingClear();\n clearHover();\n };\n document.addEventListener('mousemove', onMove);\n document.addEventListener('mouseleave', onBlurWindow);\n window.addEventListener('scroll', remeasureHover, true);\n window.addEventListener('resize', remeasureHover);\n return () => {\n cancelPendingClear();\n document.removeEventListener('mousemove', onMove);\n document.removeEventListener('mouseleave', onBlurWindow);\n window.removeEventListener('scroll', remeasureHover, true);\n window.removeEventListener('resize', remeasureHover);\n };\n }, [editor, clearHover, cancelPendingClear, remeasureHover]);\n\n useEffect(() => {\n // 文档任何变化(点对齐按钮 / 快捷键 / undo)都可能让图片挪位:重新量一遍,\n // 工具条跟着走(无 hover 时 remeasureHover 会直接返回,不进热路径)。\n const onUpdate = () => remeasureHover();\n editor.on('update', onUpdate);\n return () => {\n editor.off('update', onUpdate);\n };\n }, [editor, remeasureHover]);\n\n if (typeof document === 'undefined') return null;\n if (!hover || !editor.isEditable) return null;\n\n // 位置:默认贴图片左缘、浮在上方;顶上放不下(图片贴着视口上沿)就翻到下方\n const above = hover.top - TOOLBAR_HEIGHT - TOOLBAR_GAP >= 8;\n // active 从文档状态读(不读 DOM 属性:attrs 更新会换元素,DOM 引用不可靠)\n const nodeAttrs = editor.state.doc.nodeAt(hover.pos)?.attrs;\n const active = normalizeImageAlign(nodeAttrs?.align);\n const caption = normalizeImageCaption(nodeAttrs?.caption);\n const labelOf: Record<ImageAlign, string> = {\n left: labels.imageAlignLeft,\n center: labels.imageAlignCenter,\n right: labels.imageAlignRight,\n };\n\n const commitCaption = () => {\n if (captionDraft === null) return;\n setImageCaptionAtPos(editor, hover.pos, captionDraft);\n setCaptionDraft(null);\n // 描述显示在图片下方 → 几何变了,重新量一遍让工具条归位\n remeasureHover();\n };\n\n return createPortal(\n <div\n data-image-align=\"\"\n className={styles.bar}\n role=\"toolbar\"\n aria-label={labels.imageAlign}\n style={{\n left: hover.left,\n top: above\n ? hover.top - TOOLBAR_HEIGHT - TOOLBAR_GAP\n : hover.bottom + TOOLBAR_GAP,\n }}\n // 保住编辑器选区:点工具条不该先把光标丢掉;输入框例外(要能聚焦)\n onMouseDown={(e) => {\n if ((e.target as HTMLElement | null)?.closest?.('input')) return;\n e.preventDefault();\n }}\n >\n {ALIGNS.map((align) => {\n const Icon = ICONS[align];\n return (\n <button\n key={align}\n type=\"button\"\n data-image-align-action={align}\n className={`${styles.btn} ${active === align ? styles.btnActive : ''}`}\n title={labelOf[align]}\n aria-label={labelOf[align]}\n aria-pressed={active === align}\n onClick={() => {\n setImageAlignAtPos(editor, hover.pos, align);\n // 对齐后图片会挪位:立即重测一遍让工具条跟过去(顺带刷新 active 态)\n remeasureHover();\n }}\n >\n <Icon size={14} />\n </button>\n );\n })}\n\n <span className={styles.divider} aria-hidden />\n\n <button\n type=\"button\"\n data-image-align-action=\"caption\"\n className={`${styles.btn} ${caption ? styles.btnActive : ''}`}\n title={labels.imageCaption}\n aria-label={labels.imageCaption}\n aria-pressed={!!caption}\n onClick={() => {\n // 已打开就交给输入框的 blur 提交;这里只管打开\n if (captionDraftRef.current !== null) return;\n setCaptionDraft(caption ?? '');\n }}\n >\n <CaptionsIcon size={14} />\n </button>\n\n {captionDraft !== null ? (\n <div className={styles.captionRow}>\n <input\n className={styles.captionInput}\n value={captionDraft}\n placeholder={labels.imageCaptionPlaceholder}\n aria-label={labels.imageCaptionPlaceholder}\n autoFocus\n onChange={(e) => setCaptionDraft(e.target.value)}\n onKeyDown={(e) => {\n // 按键别冒进编辑器:Enter 提交、Esc 取消\n e.stopPropagation();\n if (e.key === 'Enter') commitCaption();\n else if (e.key === 'Escape') setCaptionDraft(null);\n }}\n onBlur={commitCaption}\n />\n </div>\n ) : null}\n </div>,\n document.body,\n );\n}\n","/**\n * 链接编辑的适配层。\n *\n * 逻辑按官方 UI Components 的 `useLinkPopover` 搬(MIT),外壳自绘。相对原来的\n * `window.prompt` 增量也都在这里:\n *\n * - 改地址时把范围扩到**整条链接**(`extendMarkRange`),而不是只作用于光标那一段;\n * - 选区为空时把地址本身当文本插进去(空选区上没有文字可以套 mark);\n * - 移除时带上 `preventAutolink` 元数据(与官方同款),免得自动链接插件把它加回来;\n * - 「在新窗口打开」前过一遍协议白名单,`javascript:` 之类直接拒掉;\n * - 裸域名补协议。官方面板不补,但 Tiptap 的 `setLink` 是把值**原样**存下来的,而它自己的\n * 自动链接(linkify)会按 `defaultProtocol` 补——两边不一致的结果是:手打 `example.com`\n * 存成相对链接,markdown 导出的就是 `[x](example.com)`。这里按扩展配的 `defaultProtocol`\n * 补齐,让「打字」和「粘贴」落到同一个地址上。\n *\n * 放在这里而不是组件里,是为了能脱离 DOM 直接跑单测(同 replaceRange / insertMarkdown)。\n */\n\nimport type { Editor } from '@tiptap/react';\n\nimport { isComposingKeyEvent } from './findReplace';\n\n/** 「在新窗口打开」允许的协议(对齐官方 `sanitizeUrl` / Tiptap Link 的默认允准集)。 */\nconst OPENABLE_PROTOCOLS = new Set([\n 'http:',\n 'https:',\n 'ftp:',\n 'ftps:',\n 'mailto:',\n 'tel:',\n 'callto:',\n 'sms:',\n 'cid:',\n 'xmpp:',\n]);\n\n/** 已经有协议了(`https:` / `mailto:` …),或者是个站内相对地址(`/x`、`#x`、`?x`)。 */\nconst HAS_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;\nconst SITE_RELATIVE_RE = /^[/#?]/;\n/**\n * 主机:端口 形态(`localhost:3000`、`127.0.0.1:8080`、`[::1]:3000`)。它们**看着像协议**\n * (`localhost:` 也满足 `HAS_SCHEME_RE`),但显然是主机,要按主机补协议——否则会被当成\n * 未知协议,Tiptap 的 `isAllowedUri` 也会直接拒掉。\n */\nconst HOST_PORT_RE = /^(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|\\[[0-9a-f:]+\\]):\\d+($|[/#?])/i;\n/** 第一段里带点的裸域名(`example.com/path`)。 */\nconst BARE_HOST_RE = /^[^/?#]*\\.[^/?#]*/;\n\n/** 链接可编辑吗:只读态、或当前选区不允许套 link mark(例如整块选中)时为 false。 */\nexport function canSetLink(editor: Editor | null | undefined): boolean {\n if (!editor || editor.isEditable === false) return false;\n try {\n return editor.can().setMark('link');\n } catch {\n return false;\n }\n}\n\n/** 光标 / 选区是否落在链接里。 */\nexport function isLinkActive(editor: Editor | null | undefined): boolean {\n if (!editor) return false;\n return editor.isActive('link');\n}\n\n/** 读当前链接的 href(不在链接里时是空串)。 */\nexport function readLinkHref(editor: Editor | null | undefined): string {\n if (!editor) return '';\n const { href } = editor.getAttributes('link');\n return typeof href === 'string' ? href : '';\n}\n\n/** 扩展上配的默认协议(`StarterKit` 里是 `http`),读不到就按 http 算。 */\nfunction defaultProtocol(editor: Editor): string {\n const link = editor.extensionManager?.extensions.find(\n (extension) => extension.name === 'link',\n );\n const configured = (link?.options as { defaultProtocol?: string } | undefined)\n ?.defaultProtocol;\n return configured || 'http';\n}\n\n/**\n * 把用户输入整理成要存进 href 的值。\n *\n * 原样保留:空、站内相对地址(`/docs`、`#sec`、`?q=1`)、已带协议的(`https:`、`mailto:`)。\n * 补协议:裸域名(`example.com`、`example.com/a`)与主机:端口(`localhost:3000`)——补的是\n * 扩展配置的 `defaultProtocol`,与 Tiptap 自动链接同一套,避免手打和粘贴落到不同地址。\n */\nexport function normalizeLinkHref(editor: Editor, raw: string): string {\n const value = raw.trim();\n if (!value) return '';\n if (SITE_RELATIVE_RE.test(value)) return value;\n if (HOST_PORT_RE.test(value)) return `${defaultProtocol(editor)}://${value}`;\n if (HAS_SCHEME_RE.test(value)) return value;\n if (!BARE_HOST_RE.test(value)) return value;\n return `${defaultProtocol(editor)}://${value}`;\n}\n\n/**\n * 应用链接:把地址套到**整条**链接(或选区)上;选区为空且原本不在链接里时,把地址当文本插进去。\n * 返回扩展是否接受了这个地址——`setLink` 会走 Tiptap 的 `isAllowedUri`。\n *\n * 两处与官方那版不同,都是单测逼出来的:\n * 1. 先 `can().setLink()` 干跑一遍。官方把 `insertContent` 挂在失败的 `setLink` 后面直接\n * `run()`,结果是地址被拒时正文仍被改(链接文字被换成那个地址)。\n * 2. 「当文本插入」的判据是「选区为空**且光标不在已有链接里**」。官方只看选区是否为空,\n * 于是「光标在链接里、改一个地址」会把整条链接的文字替换成地址——而这正是这个浮层的主用途。\n */\nexport function applyLink(editor: Editor | null | undefined, raw: string): boolean {\n if (!editor) return false;\n const href = normalizeLinkHref(editor, raw);\n if (!href) return false;\n if (!editor.can().setLink({ href })) return false;\n\n const isEmpty = editor.state.selection.empty;\n const insideLink = isLinkActive(editor);\n const chain = editor\n .chain()\n .focus()\n .extendMarkRange('link')\n .setLink({ href });\n\n if (isEmpty && !insideLink) chain.insertContent({ type: 'text', text: href });\n\n return chain.run();\n}\n\n/** 移除链接:范围同样扩到整条,并挡住自动链接立刻加回来。 */\nexport function removeLink(editor: Editor | null | undefined): boolean {\n if (!editor) return false;\n return editor\n .chain()\n .focus()\n .extendMarkRange('link')\n .unsetLink()\n .setMeta('preventAutolink', true)\n .run();\n}\n\n/**\n * 把待打开的地址解析成绝对地址;解析不了、或协议不在白名单里时返回 `'#'` 表示「别打开」。\n *\n * 与官方 `sanitizeUrl(inputUrl, baseUrl)` 同语义:先按 base 解析成绝对地址(相对地址因此也\n * 能用),再查协议白名单。调用方拿到 `'#'` 就不开新窗口。\n */\nexport function sanitizeLinkUrl(input: string, baseUrl: string): string {\n try {\n const url = new URL(input, baseUrl);\n if (OPENABLE_PROTOCOLS.has(url.protocol)) return url.href;\n } catch {\n // 解析失败 = 非法地址\n }\n return '#';\n}\n\n/**\n * 打开地址(新窗口 + `noopener,noreferrer`)。空输入或非法地址都不开窗——调用方在此之前\n * 已经用 `sanitizeLinkUrl` 判过一次,这里再判一次是为了让这个入口单独用也安全。\n */\nexport function openLinkUrl(input: string, baseUrl: string): boolean {\n if (!input.trim()) return false;\n const safe = sanitizeLinkUrl(input, baseUrl);\n if (safe === '#') return false;\n window.open(safe, '_blank', 'noopener,noreferrer');\n return true;\n}\n\n/** 输入框的回车是否该当作「应用」(输入法组合中的回车是提交候选词,放行)。 */\nexport function isApplyKey(event: {\n key: string;\n nativeEvent: Pick<KeyboardEvent, 'isComposing' | 'keyCode'>;\n}): boolean {\n if (event.key !== 'Enter') return false;\n return !isComposingKeyEvent(event.nativeEvent);\n}\n","'use client';\n\nimport * as Popover from '@radix-ui/react-popover';\nimport type { Editor } from '@tiptap/react';\nimport {\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from 'react';\nimport { CheckIcon, ExternalLinkIcon, TrashIcon } from '../icons';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport type { LinkPopoverLabels } from '../labels';\nimport {\n applyLink,\n canSetLink,\n isApplyKey,\n normalizeLinkHref,\n openLinkUrl,\n readLinkHref,\n removeLink,\n} from '../linkEditing';\nimport styles from '../styles/linkPopover.module.css';\n\nexport interface LinkPopoverProps {\n /** 编辑器实例。 */\n editor: Editor;\n /**\n * 触发器:传宿主自己的按钮元素。Radix 的 `asChild` 要接管它的点击 / ref / aria——\n * 传函数组件时得先 `forwardRef`(本库的 `<EditorToolbar>` 就是这么接的)。\n */\n trigger: ReactNode;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n /** 挂到浮层根节点上的 class / 内联样式(定位与层级归宿主,Radix 只管浮层方向)。 */\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * 链接编辑浮层:输入地址 + 应用 / 在新窗口打开 / 移除。\n *\n * 逻辑全在 `linkEditing.ts`(从官方 UI Components 的 `useLinkPopover` 搬的),这里只负责输入、\n * 按钮与状态。取代表驱动器的 `window.prompt`:能**编辑已有链接**(打开即预填当前地址,\n * 而旧实现是「在链接里点一下就删除」)、能打开、能移除,且 `window.prompt` 在 Electron /\n * 部分 WebView 里根本不可用。\n *\n * 地址被编辑器拒掉时(Tiptap 的 `isAllowedUri`)浮层不关,输入框转成危险色并带上 title,\n * 让用户改——静默什么都不发生是最难查的。\n */\nexport function LinkPopover({\n editor,\n trigger,\n locale = 'en',\n className,\n style,\n}: LinkPopoverProps) {\n const [open, setOpen] = useState(false);\n\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger asChild>{trigger}</Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n sideOffset={4}\n align=\"start\"\n // 关闭后不要把焦点夺回触发器:应用后该留在编辑器里(applyLink 里已经 focus 过)\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <LinkPanel\n editor={editor}\n locale={locale}\n className={className}\n style={style}\n onClose={() => setOpen(false)}\n />\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\nfunction LinkPanel({\n editor,\n locale,\n className,\n style,\n onClose,\n}: {\n editor: Editor;\n locale: LocaleCode;\n className?: string;\n style?: CSSProperties;\n onClose: () => void;\n}) {\n const t: LinkPopoverLabels = getLocaleBundle(locale).link;\n // Radix 关闭时会卸载内容,所以这里的初值就是「每次打开读一次当前地址」——光标在链接里\n // 打开时预填它的 href,便于改地址而不是先删再加。\n const [url, setUrl] = useState(() => readLinkHref(editor));\n const [invalid, setInvalid] = useState(false);\n\n const canEdit = canSetLink(editor);\n const hasUrl = url.trim().length > 0;\n const keepFocus = (e: ReactMouseEvent) => e.preventDefault();\n\n const apply = () => {\n if (!applyLink(editor, url)) {\n setInvalid(true);\n return;\n }\n setInvalid(false);\n onClose();\n };\n\n const openInNewWindow = () => {\n // 先补协议再交给白名单:裸域名不该按「相对本页」打开\n openLinkUrl(normalizeLinkHref(editor, url), window.location.href);\n };\n\n const remove = () => {\n removeLink(editor);\n onClose();\n };\n\n const onInputKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {\n // 输入法组合中的回车是「提交候选词」,放行(isApplyKey 里判)\n if (!isApplyKey(e)) return;\n e.preventDefault();\n apply();\n };\n\n return (\n <div\n className={className ? `${styles.panel} ${className}` : styles.panel}\n style={style}\n data-link-popover=\"\"\n role=\"dialog\"\n aria-label={t.field}\n >\n <span className={`${styles.field}${invalid ? ` ${styles.fieldInvalid}` : ''}`}>\n <input\n className={styles.input}\n data-link-field=\"\"\n data-link-invalid={invalid ? '' : undefined}\n type=\"url\"\n value={url}\n onChange={(e) => {\n setUrl(e.target.value);\n setInvalid(false);\n }}\n onKeyDown={onInputKeyDown}\n placeholder={t.field}\n aria-label={t.field}\n aria-invalid={invalid || undefined}\n title={invalid ? t.invalid : undefined}\n autoFocus\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n />\n </span>\n\n <button\n type=\"button\"\n className={`${styles.btn} ${styles.apply}`}\n data-link-action=\"apply\"\n title={t.apply}\n aria-label={t.apply}\n disabled={!canEdit || !hasUrl}\n onMouseDown={keepFocus}\n onClick={apply}\n >\n <CheckIcon size={16} />\n </button>\n\n <span className={styles.divider} aria-hidden=\"true\" />\n\n <button\n type=\"button\"\n className={styles.btn}\n data-link-action=\"open\"\n title={t.open}\n aria-label={t.open}\n disabled={!hasUrl}\n onMouseDown={keepFocus}\n onClick={openInNewWindow}\n >\n <ExternalLinkIcon size={16} />\n </button>\n <button\n type=\"button\"\n className={styles.btn}\n data-link-action=\"remove\"\n title={t.remove}\n aria-label={t.remove}\n disabled={!canEdit}\n onMouseDown={keepFocus}\n onClick={remove}\n >\n <TrashIcon size={16} />\n </button>\n </div>\n );\n}\n","import type { Editor } from '@tiptap/core';\nimport type { Node as PMNode } from '@tiptap/pm/model';\nimport type { EditorState } from '@tiptap/pm/state';\nimport {\n cellAround,\n CellSelection,\n isInTable,\n rowIsHeader,\n selectedRect,\n TableMap,\n} from '@tiptap/pm/tables';\n\nexport interface TableSelectionInfo {\n rowCount: number;\n colCount: number;\n /** 选区跨越多行或多列时为 true(用于带 N 的文案)。 */\n isMultiCell: boolean;\n includesHeaderRow: boolean;\n coversAllRows: boolean;\n coversAllCols: boolean;\n}\n\n/** 当前若不在表格内返回 null。 */\nexport function getTableSelectionInfo(\n state: EditorState,\n): TableSelectionInfo | null {\n if (!isInTable(state)) return null;\n\n const rect = selectedRect(state);\n const rowCount = Math.max(1, rect.bottom - rect.top);\n const colCount = Math.max(1, rect.right - rect.left);\n\n let includesHeaderRow = false;\n for (let row = rect.top; row < rect.bottom; row++) {\n if (rowIsHeader(rect.map, rect.table, row)) {\n includesHeaderRow = true;\n break;\n }\n }\n\n return {\n rowCount,\n colCount,\n isMultiCell: rowCount > 1 || colCount > 1,\n includesHeaderRow,\n coversAllRows: rect.top === 0 && rect.bottom === rect.map.height,\n coversAllCols: rect.left === 0 && rect.right === rect.map.width,\n };\n}\n\n/** 文档坐标是否落在 CellSelection 覆盖的某个单元格内(含单元格节点本身)。 */\nexport function isPosInCellSelection(\n selection: CellSelection,\n pos: number,\n): boolean {\n let found = false;\n selection.forEachCell((node, cellPos) => {\n if (pos >= cellPos && pos <= cellPos + node.nodeSize) {\n found = true;\n }\n });\n return found;\n}\n\n\nexport function addRowsBefore(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addRowBefore()) return false;\n }\n return true;\n}\n\nexport function addRowsAfter(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addRowAfter()) return false;\n }\n return true;\n}\n\nexport function addColumnsBefore(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addColumnBefore()) return false;\n }\n return true;\n}\n\nexport function addColumnsAfter(editor: Editor, count: number): boolean {\n const n = Math.max(1, count);\n for (let i = 0; i < n; i++) {\n if (!editor.commands.addColumnAfter()) return false;\n }\n return true;\n}\n\n/** 删行:全表行被选中时改为删整表;含表头时调用方应先禁用。 */\nexport function deleteSelectedRows(editor: Editor): boolean {\n const info = getTableSelectionInfo(editor.state);\n if (!info) return false;\n if (info.coversAllRows) return editor.commands.deleteTable();\n return editor.commands.deleteRow();\n}\n\n/** 删列:选区覆盖全部列且同时覆盖全部行时删整表;仅覆盖全部列时失败(由 UI 禁用)。 */\nexport function deleteSelectedColumns(editor: Editor): boolean {\n const info = getTableSelectionInfo(editor.state);\n if (!info) return false;\n if (info.coversAllCols) {\n if (info.coversAllRows) return editor.commands.deleteTable();\n return false;\n }\n return editor.commands.deleteColumn();\n}\n\n/** 结构属性:改它们等于改变表格形状,合并 / 拆分有专门的命令,重置样式时不碰。 */\nconst STRUCTURAL_CELL_ATTRS = new Set(['colspan', 'rowspan']);\n\n/**\n * 选区覆盖到的单元格:CellSelection 的全部单元格;若只是个文本光标,就取它所在的那一格。\n * (菜单里永远是 CellSelection;带光标这条是为了让命令单独用也说得通。)\n */\nfunction selectedCellEntries(\n editor: Editor,\n): Array<{ pos: number; node: PMNode }> {\n const { selection } = editor.state;\n const entries: Array<{ pos: number; node: PMNode }> = [];\n\n if (selection instanceof CellSelection) {\n selection.forEachCell((node, pos) => {\n entries.push({ pos, node });\n });\n return entries;\n }\n\n const $cell = cellAround(selection.$from);\n if (!$cell) return entries;\n const pos = $cell.before($cell.depth);\n const node = editor.state.doc.nodeAt(pos);\n if (node) entries.push({ pos, node });\n return entries;\n}\n\n/**\n * 清空选中单元格的**内容**,保留单元格本身与它的属性(表头行因此不会被降级)。\n *\n * 单元格必须至少留一个块,所以内容替换成一个空段落。已经空的单元格跳过——不改动、\n * 也不产生多余的撤销步。返回是否真的改了文档。\n */\nexport function clearSelectedCellContents(editor: Editor): boolean {\n const entries = selectedCellEntries(editor);\n if (!entries.length) return false;\n const paragraph = editor.state.schema.nodes.paragraph;\n if (!paragraph) return false;\n\n const tr = editor.state.tr;\n // 从后往前改:替换内容会改变 nodeSize,先算好后面的位置才不会位移串味\n for (const { pos, node } of [...entries].sort((a, b) => b.pos - a.pos)) {\n const isEmpty =\n node.childCount === 1 &&\n !!node.firstChild?.isTextblock &&\n node.firstChild.content.size === 0;\n if (isEmpty) continue;\n tr.replaceWith(pos + 1, pos + node.nodeSize - 1, paragraph.createAndFill()!);\n }\n\n if (!tr.docChanged) return false;\n editor.view.dispatch(tr.scrollIntoView());\n return true;\n}\n\n/**\n * 把选中单元格的**样式属性**复位(列宽、对齐等,按各属性的 schema 默认值)。\n *\n * 不动 `colspan` / `rowspan`:那是结构,复位它们会让表格形状不合法(缺格)。\n * (扩展有 `mergeCells` / `splitCell`,但本库不暴露入口——GFM 没有 colspan,合并导出成\n * `| 1<br>2 | |` 且回不去,见 README。)\n */\nexport function resetSelectedCellStyles(editor: Editor): boolean {\n const entries = selectedCellEntries(editor);\n if (!entries.length) return false;\n\n const tr = editor.state.tr;\n for (const { pos, node } of entries) {\n const attrs = (node.type.spec.attrs ?? {}) as Record<\n string,\n { default?: unknown }\n >;\n const next: Record<string, unknown> = { ...node.attrs };\n let changed = false;\n for (const [key, spec] of Object.entries(attrs)) {\n if (STRUCTURAL_CELL_ATTRS.has(key)) continue;\n const fallback = spec?.default ?? null;\n if (next[key] !== fallback) {\n next[key] = fallback;\n changed = true;\n }\n }\n if (changed) tr.setNodeMarkup(pos, undefined, next);\n }\n\n if (!tr.docChanged) return false;\n editor.view.dispatch(tr);\n return true;\n}\n\n/** 找当前要操作的那张表:优先用光标 / 选区所在的那张(同页多表时才不会改错),否则文档里的第一张。 */\nfunction findTable(editor: Editor): { pos: number; node: PMNode } | null {\n const $from = editor.state.selection.$from;\n for (let d = $from.depth; d > 0; d -= 1) {\n if ($from.node(d).type.name === 'table') {\n return { pos: $from.before(d), node: $from.node(d) };\n }\n }\n\n let found: { pos: number; node: PMNode } | null = null;\n editor.state.doc.descendants((node, pos) => {\n if (found !== null) return false;\n if (node.type.name === 'table') {\n found = { pos, node };\n return false;\n }\n return true;\n });\n return found;\n}\n\n/** 把第 index 行 / 列整体选成 CellSelection(末尾追加这类操作要先「瞄准」)。 */\nfunction selectLineAt(\n editor: Editor,\n table: { pos: number; node: PMNode },\n kind: 'row' | 'col',\n index: number,\n): void {\n const map = TableMap.get(table.node);\n const start = table.pos + 1;\n const anchorCell = start + map.positionAt(kind === 'row' ? index : 0, kind === 'col' ? index : 0, table.node);\n const headCell =\n start +\n map.positionAt(\n kind === 'row' ? index : map.height - 1,\n kind === 'col' ? index : map.width - 1,\n table.node,\n );\n editor.view.dispatch(\n editor.state.tr.setSelection(\n new CellSelection(\n editor.state.doc.resolve(anchorCell),\n editor.state.doc.resolve(headCell),\n ),\n ),\n );\n}\n\n/**\n * 在表格末尾追加一行(表底那个 `+`)。\n * 先把最后一行选成 CellSelection——扩展的 `addRowAfter` 是按选区办事的。\n */\nexport function appendRowToTable(editor: Editor): boolean {\n const table = findTable(editor);\n if (!table) return false;\n selectLineAt(editor, table, 'row', TableMap.get(table.node).height - 1);\n return addRowsAfter(editor, 1);\n}\n\n/**\n * 在表格末尾追加一列(表格右侧那个 `+`)。\n * 同理先把最后一列选成 CellSelection。\n */\nexport function appendColumnToTable(editor: Editor): boolean {\n const table = findTable(editor);\n if (!table) return false;\n selectLineAt(editor, table, 'col', TableMap.get(table.node).width - 1);\n return addColumnsAfter(editor, 1);\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/core';\nimport { CellSelection, cellAround, TableMap } from '@tiptap/pm/tables';\nimport {\n Fragment,\n useCallback,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n ColumnAfterIcon,\n ColumnBeforeIcon,\n ColumnDeleteIcon,\n EraserIcon,\n RotateCcwIcon,\n RowAfterIcon,\n RowBeforeIcon,\n RowDeleteIcon,\n TableDeleteIcon,\n} from '../icons';\nimport type { ToolbarLabels } from '../labels';\nimport {\n addColumnsAfter,\n addColumnsBefore,\n appendColumnToTable,\n appendRowToTable,\n addRowsAfter,\n addRowsBefore,\n clearSelectedCellContents,\n deleteSelectedColumns,\n deleteSelectedRows,\n resetSelectedCellStyles,\n getTableSelectionInfo,\n isPosInCellSelection,\n} from '../tableSelection';\nimport styles from '../styles/tableHandles.module.css';\n\nexport interface TableHandlesProps {\n /** 编辑器实例。只读态不出手柄。 */\n editor: Editor;\n /** 已合并默认值的工具栏文案(行列菜单项与两个手柄的 aria-label 都取自它)。 */\n labels: ToolbarLabels;\n}\n\n/** 悬停到某个单元格时量出来的几何:手柄贴边 + 建 CellSelection 用。 */\ninterface HoverGeom {\n /** 当前悬停的单元格元素(用来跳过同一格的重复测量)。 */\n cell: HTMLElement;\n rowTop: number;\n rowHeight: number;\n colLeft: number;\n colWidth: number;\n tableLeft: number;\n tableTop: number;\n /** 表格右缘 / 下缘:末尾追加行列的两个 `+` 手柄贴在它们外侧。 */\n tableRight: number;\n tableBottom: number;\n /** 悬停的这格是不是表格的最后一行 / 最后一列——决定要不要出对应的 `+`。 */\n onLastRow: boolean;\n onLastCol: boolean;\n}\n\n/**\n * 悬停的这格落在表格的最后一行 / 最后一列吗(按 TableMap 算,colspan 也算得对)。\n * 末尾追加的 `+` 只在贴着那两行/列时才出现,否则鼠标停在表格中间也会冒出两个加号。\n */\nfunction edgeFlags(\n editor: Editor,\n cell: HTMLElement,\n): { onLastRow: boolean; onLastCol: boolean } {\n try {\n const $cell = cellAround(editor.state.doc.resolve(editor.view.posAtDOM(cell, 0)));\n if (!$cell) return { onLastRow: false, onLastCol: false };\n // 官方(prosemirror-tables)自己的换算写法:findCell($pos.pos - $pos.start(-1))。\n // 注意别用 $pos.before($pos.depth)——cellAround 给的是「行层」的 ResolvedPos,\n // before(depth) 拿到的是行首的位置,不是这一格的。\n const map = TableMap.get($cell.node(-1));\n const rect = map.findCell($cell.pos - $cell.start(-1));\n return {\n onLastRow: rect.bottom === map.height,\n onLastCol: rect.right === map.width,\n };\n } catch {\n return { onLastRow: false, onLastCol: false };\n }\n}\n\n/** 菜单挂在哪个手柄(multi = 右键多格选区)上。 */\ninterface HandleMenu {\n kind: 'row' | 'col' | 'multi';\n x: number;\n y: number;\n}\n\n/**\n * 行手柄拉满行高,列手柄拉满列宽。GAP 是胶囊外侧到表格边缘的距离,\n * 减去厚度后仍留一段空隙,靠伪元素补桥才能把鼠标移过去。\n */\nconst COL_HANDLE_GAP = 18;\nconst COL_HANDLE_HEIGHT = 12;\nconst ROW_HANDLE_GAP = 20;\nconst ROW_HANDLE_WIDTH = 12;\n\n/** 末尾追加行列的 `+` 手柄贴在表格右缘 / 下缘外侧的间距。 */\nconst EXTEND_GAP = 6;\n/** `+` 手柄的粗细,与行 / 列手柄一致。长度仍跟着整表走。 */\nconst EXTEND_THICKNESS = 12;\n/**\n * 指针离开单元格后手柄再活一会儿(毫秒):走向手柄的路上有一段「既非单元格也非手柄」的空隙,\n * 立刻收起就会点不到。见下面悬停追踪那段注释。\n */\nconst HOVER_KEEP_MS = 160;\n\n/** 量单元格与它所在行 / 表格的视口矩形(手柄是 fixed 定位,直接用视口坐标)。 */\nfunction measureCell(editor: Editor, cell: HTMLElement): HoverGeom | null {\n const table = cell.closest('table');\n const row = cell.closest('tr');\n if (!table || !row) return null;\n // 校验这个 DOM 还对应文档里的位置(视图可能刚重建过)\n try {\n editor.view.posAtDOM(cell, 0);\n } catch {\n return null;\n }\n const cellRect = cell.getBoundingClientRect();\n const rowRect = row.getBoundingClientRect();\n const tableRect = table.getBoundingClientRect();\n const edges = edgeFlags(editor, cell);\n return {\n cell,\n ...edges,\n rowTop: rowRect.top,\n rowHeight: rowRect.height,\n colLeft: cellRect.left,\n colWidth: cellRect.width,\n tableLeft: tableRect.left,\n tableTop: tableRect.top,\n tableRight: tableRect.right,\n tableBottom: tableRect.bottom,\n };\n}\n\n/** 把整行 / 整列选成 CellSelection:菜单里的命令都按选区办事,选中即「瞄准」。 */\nfunction selectRowOrColumn(\n editor: Editor,\n cell: HTMLElement,\n kind: 'row' | 'col',\n): boolean {\n let pos: number;\n try {\n pos = editor.view.posAtDOM(cell, 0);\n } catch {\n return false;\n }\n const $cell = cellAround(editor.state.doc.resolve(pos));\n if (!$cell) return false;\n const selection =\n kind === 'row'\n ? CellSelection.rowSelection($cell)\n : CellSelection.colSelection($cell);\n editor.view.dispatch(editor.state.tr.setSelection(selection));\n return true;\n}\n\ninterface MenuItemSpec {\n key: string;\n label: string;\n icon: ReactNode;\n /** 分组:组变了就插一条分隔线(列操作 / 行操作 / 单元格操作 / 删除表格)。 */\n group: 'col' | 'row' | 'cells';\n disabled?: boolean;\n run: () => void;\n}\n\n/** 按手柄种类组装菜单项:行手柄只管行、列手柄只管列,右键多格选区两样都给。 */\nfunction menuItemsFor(\n editor: Editor,\n labels: ToolbarLabels,\n kind: HandleMenu['kind'],\n): MenuItemSpec[] {\n const info = getTableSelectionInfo(editor.state);\n const rowN = info?.rowCount ?? 1;\n const colN = info?.colCount ?? 1;\n // 「几行几列」的文案**只**给多格选区用:点行手柄时选区是「整行 × 全部列」,\n // 按 isMultiCell 判会得到「插入 1 行」这种拗口文案(而且列数会被算进去)。\n const multi = kind === 'multi';\n const items: MenuItemSpec[] = [];\n\n if (kind === 'col' || kind === 'multi') {\n items.push(\n {\n key: 'col-before',\n group: 'col',\n label: multi ? labels.tableAddColumnBeforeN(colN) : labels.tableAddColumnBefore,\n icon: <ColumnBeforeIcon />,\n run: () => addColumnsBefore(editor, colN),\n },\n {\n key: 'col-after',\n group: 'col',\n label: multi ? labels.tableAddColumnAfterN(colN) : labels.tableAddColumnAfter,\n icon: <ColumnAfterIcon />,\n run: () => addColumnsAfter(editor, colN),\n },\n {\n key: 'col-delete',\n group: 'col',\n label: multi ? labels.tableDeleteColumnN(colN) : labels.tableDeleteColumn,\n icon: <ColumnDeleteIcon />,\n // 只选了全部列、却没选全部行:删列会把表格删没,禁掉\n disabled: !!info?.coversAllCols && !info?.coversAllRows,\n run: () => deleteSelectedColumns(editor),\n },\n );\n }\n\n if (kind === 'row' || kind === 'multi') {\n items.push(\n {\n key: 'row-before',\n group: 'row',\n label: multi ? labels.tableAddRowBeforeN(rowN) : labels.tableAddRowBefore,\n icon: <RowBeforeIcon />,\n // 上方是表头行时插行没意义\n disabled: !!info?.includesHeaderRow,\n run: () => addRowsBefore(editor, rowN),\n },\n {\n key: 'row-after',\n group: 'row',\n label: multi ? labels.tableAddRowAfterN(rowN) : labels.tableAddRowAfter,\n icon: <RowAfterIcon />,\n run: () => addRowsAfter(editor, rowN),\n },\n {\n key: 'row-delete',\n group: 'row',\n label: multi ? labels.tableDeleteRowN(rowN) : labels.tableDeleteRow,\n icon: <RowDeleteIcon />,\n disabled: !!info?.includesHeaderRow,\n run: () => deleteSelectedRows(editor),\n },\n );\n }\n\n // 单元格级操作:清空内容、重置样式。两者都不改变表格形状,所以行 / 列 / 多格选区都给。\n items.push(\n {\n key: 'cells-clear',\n group: 'cells',\n label: labels.tableClearContent,\n icon: <EraserIcon />,\n run: () => clearSelectedCellContents(editor),\n },\n {\n key: 'cells-reset',\n group: 'cells',\n label: labels.tableResetCellStyles,\n icon: <RotateCcwIcon />,\n run: () => resetSelectedCellStyles(editor),\n },\n );\n\n return items;\n}\n\n/**\n * 表格的悬停手柄(Notion 式):鼠标落在单元格上时,行左缘出一条竖胶囊(⋮)、表格上方出一条\n * 横胶囊(⋯),点开就是该行 / 该列的操作菜单。\n *\n * 这是原来「表内右键出菜单」的替代——右键只保留**多格选区**的批量增删(手柄只覆盖单行单列,\n * 那个场景手柄表达不了)。命令全部复用 `tableSelection.ts`,与工具栏的「插入表格」共用一套。\n *\n * 手柄是 fixed 定位、渲染在 body 的 portal 里:它要贴着单元格边缘,而工具栏并不在编辑器 DOM\n * 内。滚动 / 缩放时重新量一遍悬停的那颗单元格,让手柄跟着走。\n */\nexport function TableHandles({ editor, labels }: TableHandlesProps) {\n const [hover, setHover] = useState<HoverGeom | null>(null);\n const [menu, setMenu] = useState<HandleMenu | null>(null);\n\n const hoverRef = useRef<HoverGeom | null>(null);\n const menuRef = useRef<HandleMenu | null>(null);\n menuRef.current = menu;\n\n const clearHover = useCallback(() => {\n if (!hoverRef.current) return;\n hoverRef.current = null;\n setHover(null);\n }, []);\n\n const closeMenu = useCallback(() => setMenu(null), []);\n\n /** 重新量一遍悬停的那颗单元格(滚动 / 缩放 / 追加行列后手柄要跟着走)。 */\n const remeasureHover = useCallback(() => {\n const cell = hoverRef.current?.cell;\n if (!cell) return;\n const next = cell.isConnected ? measureCell(editor, cell) : null;\n hoverRef.current = next;\n setHover(next);\n }, [editor]);\n /** 离开后的收起定时器(悬停意图,见下面的注释)。 */\n const clearTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const cancelPendingClear = useCallback(() => {\n if (clearTimerRef.current === null) return;\n clearTimeout(clearTimerRef.current);\n clearTimerRef.current = null;\n }, []);\n\n // 悬停追踪挂在 document 上。\n //\n // 关键点:**离开单元格 ≠ 离开**。指针从单元格走向手柄时,中间必然经过一小段既不是单元格、\n // 也不在手柄上的地方(手柄贴在表格外侧,本身只有 28px 高,斜着移动更是会绕过它),\n // 那一刻若立刻收起,手柄就在你点到之前消失了。所以这里用「悬停意图」:不在单元格 / 手柄上时\n // 只挂一个 ~160ms 的收起定时器,指针在这段时间内碰到手柄或单元格就取消。\n useEffect(() => {\n const onMove = (event: MouseEvent) => {\n if (!editor.isEditable || menuRef.current) return;\n const target = event.target as HTMLElement | null;\n\n // 指针已经到手柄上:留着,并取消待收起\n if (target?.closest?.('[data-table-handle], [data-table-extend]')) {\n cancelPendingClear();\n return;\n }\n\n const cell =\n target && editor.view.dom.contains(target)\n ? (target.closest('td, th') as HTMLElement | null)\n : null;\n\n if (!cell) {\n // 既不在单元格也不在手柄上:宽限一段再收(指针可能正走向手柄)\n if (hoverRef.current && clearTimerRef.current === null) {\n clearTimerRef.current = setTimeout(() => {\n clearTimerRef.current = null;\n clearHover();\n }, HOVER_KEEP_MS);\n }\n return;\n }\n\n cancelPendingClear();\n if (hoverRef.current?.cell === cell) return;\n const next = measureCell(editor, cell);\n hoverRef.current = next;\n setHover(next);\n };\n const onBlurWindow = () => {\n cancelPendingClear();\n clearHover();\n };\n // 滚动 / 缩放后手柄的 fixed 坐标会失真:重新量那颗单元格让手柄跟着走,而不是直接收起\n // (窗口一动手柄就没了很烦)。单元格已经不在文档里了才收起。\n document.addEventListener('mousemove', onMove);\n document.addEventListener('mouseleave', onBlurWindow);\n window.addEventListener('scroll', remeasureHover, true);\n window.addEventListener('resize', remeasureHover);\n return () => {\n cancelPendingClear();\n document.removeEventListener('mousemove', onMove);\n document.removeEventListener('mouseleave', onBlurWindow);\n window.removeEventListener('scroll', remeasureHover, true);\n window.removeEventListener('resize', remeasureHover);\n };\n }, [editor, clearHover, cancelPendingClear, remeasureHover]);\n\n // 右键:只服务多格选区(单行 / 单列交给手柄)\n useEffect(() => {\n const dom = editor.view.dom;\n const onContextMenu = (event: MouseEvent) => {\n const info = getTableSelectionInfo(editor.state);\n const { selection } = editor.state;\n if (!info?.isMultiCell || !(selection instanceof CellSelection)) return;\n const coords = editor.view.posAtCoords({\n left: event.clientX,\n top: event.clientY,\n });\n if (!coords || !isPosInCellSelection(selection, coords.pos)) return;\n event.preventDefault();\n hoverRef.current = null;\n setHover(null);\n setMenu({ kind: 'multi', x: event.clientX, y: event.clientY });\n };\n dom.addEventListener('contextmenu', onContextMenu);\n return () => dom.removeEventListener('contextmenu', onContextMenu);\n }, [editor]);\n\n // 菜单开着时:点外面 / Esc 收起(菜内点击不关,交给菜单项自己收)\n useEffect(() => {\n if (!menu) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') closeMenu();\n };\n const onDown = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n if (target?.closest?.('[data-table-menu]')) return;\n closeMenu();\n };\n document.addEventListener('keydown', onKey, true);\n document.addEventListener('mousedown', onDown);\n return () => {\n document.removeEventListener('keydown', onKey, true);\n document.removeEventListener('mousedown', onDown);\n };\n }, [menu, closeMenu]);\n\n if (typeof document === 'undefined') return null;\n\n const openHandleMenu = (kind: 'row' | 'col') => {\n if (!hover) return;\n if (!selectRowOrColumn(editor, hover.cell, kind)) return;\n setHover(null);\n hoverRef.current = null;\n setMenu(\n kind === 'row'\n ? { kind, x: hover.tableLeft + 6, y: hover.rowTop }\n : { kind, x: hover.colLeft, y: hover.tableTop + 6 },\n );\n };\n\n const items = menu ? menuItemsFor(editor, labels, menu.kind) : [];\n const showHandles = !!hover && !menu && editor.isEditable;\n\n return createPortal(\n <>\n {showHandles && hover ? (\n <>\n <button\n type=\"button\"\n data-table-handle=\"row\"\n className={`${styles.handle} ${styles.handleRow}`}\n style={{\n left: hover.tableLeft - ROW_HANDLE_GAP,\n top: hover.rowTop,\n width: ROW_HANDLE_WIDTH,\n height: hover.rowHeight,\n }}\n title={labels.tableRowMenu}\n aria-label={labels.tableRowMenu}\n // 保住编辑器选区:点手柄不该先把光标丢掉\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => openHandleMenu('row')}\n >\n <span aria-hidden>⋮</span>\n </button>\n <button\n type=\"button\"\n data-table-handle=\"col\"\n className={`${styles.handle} ${styles.handleCol}`}\n style={{\n left: hover.colLeft,\n top: hover.tableTop - COL_HANDLE_GAP,\n width: hover.colWidth,\n height: COL_HANDLE_HEIGHT,\n }}\n title={labels.tableColumnMenu}\n aria-label={labels.tableColumnMenu}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => openHandleMenu('col')}\n >\n <span aria-hidden>⋯</span>\n </button>\n\n {/* 末尾追加:只有悬停贴着最后一列 / 最后一行时才出现对应的一条 `+`\n (鼠标停在表格中间不该冒加号)。长度跟着表格尺寸走,点完重新量一次。 */}\n {hover.onLastCol ? (\n <button\n type=\"button\"\n data-table-extend=\"col\"\n className={styles.extend}\n style={{\n left: hover.tableRight + EXTEND_GAP,\n top: hover.tableTop,\n width: EXTEND_THICKNESS,\n height: Math.max(EXTEND_THICKNESS, hover.tableBottom - hover.tableTop),\n }}\n title={labels.tableAppendColumn}\n aria-label={labels.tableAppendColumn}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n appendColumnToTable(editor);\n remeasureHover();\n }}\n >\n <span aria-hidden>+</span>\n </button>\n ) : null}\n {hover.onLastRow ? (\n <button\n type=\"button\"\n data-table-extend=\"row\"\n className={styles.extend}\n style={{\n left: hover.tableLeft,\n top: hover.tableBottom + EXTEND_GAP,\n width: Math.max(EXTEND_THICKNESS, hover.tableRight - hover.tableLeft),\n height: EXTEND_THICKNESS,\n }}\n title={labels.tableAppendRow}\n aria-label={labels.tableAppendRow}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n appendRowToTable(editor);\n remeasureHover();\n }}\n >\n <span aria-hidden>+</span>\n </button>\n ) : null}\n </>\n ) : null}\n\n {menu ? (\n <div\n className={styles.tableBubble}\n data-table-menu=\"\"\n style={{ left: menu.x, top: menu.y }}\n role=\"menu\"\n onMouseDown={(e) => e.stopPropagation()}\n onContextMenu={(e) => e.preventDefault()}\n >\n {items.map((item, index) => (\n <Fragment key={item.key}>\n {index > 0 && item.group !== items[index - 1].group ? (\n <span className={styles.tableMenuDivider} aria-hidden />\n ) : null}\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.tableMenuItem}\n disabled={item.disabled}\n onClick={() => {\n item.run();\n closeMenu();\n }}\n >\n <span className={styles.tableMenuItemIcon} aria-hidden>\n {item.icon}\n </span>\n <span className={styles.tableMenuItemLabel}>{item.label}</span>\n </button>\n </Fragment>\n ))}\n <span className={styles.tableMenuDivider} aria-hidden />\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.tableMenuItem}\n onClick={() => {\n editor.commands.deleteTable();\n closeMenu();\n }}\n >\n <span className={styles.tableMenuItemIcon} aria-hidden>\n <TableDeleteIcon />\n </span>\n <span className={styles.tableMenuItemLabel}>\n {labels.tableDeleteTable}\n </span>\n </button>\n </div>\n ) : null}\n </>,\n document.body,\n );\n}\n","'use client';\n\nimport type { Editor } from '@tiptap/react';\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type RefObject,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { renderMathInto, type MathKind } from '../math';\nimport styles from '../styles/toolbar.module.css';\n\nexport interface MathEditorPopoverProps {\n editor: Editor;\n kind: MathKind;\n mode: 'insert' | 'edit';\n pos?: number;\n latex: string;\n /** 工具栏新插入:取消时删掉占位节点 */\n isNew?: boolean;\n newLabel: string;\n placeholder: string;\n doneLabel: string;\n onConfirm: (latex: string) => void;\n onCancel: () => void;\n}\n\nfunction resolveNodeEl(editor: Editor, pos: number): HTMLElement | null {\n const dom = editor.view.nodeDOM(pos);\n if (dom instanceof HTMLElement) return dom;\n if (dom instanceof Node) return dom.parentElement;\n return null;\n}\n\nfunction mathRenderTarget(el: HTMLElement, isBlock: boolean): HTMLElement {\n if (!isBlock) return el;\n return (el.querySelector('.block-math-inner') as HTMLElement | null) ?? el;\n}\n\nfunction getMathLayerStyle(\n editor: Editor,\n kind: MathKind,\n pos?: number,\n): CSSProperties {\n const host = editor.view.dom.parentElement;\n if (!host) return {};\n const hostRect = host.getBoundingClientRect();\n const editorRect = editor.view.dom.getBoundingClientRect();\n const gap = 6;\n const blockWidth = editor.view.dom.clientWidth;\n\n if (pos != null) {\n const el = resolveNodeEl(editor, pos);\n if (el) {\n const r = el.getBoundingClientRect();\n const top = r.bottom - hostRect.top + gap;\n if (kind === 'block') {\n return {\n left: editorRect.left - hostRect.left,\n top,\n width: blockWidth,\n };\n }\n return {\n left: r.left - hostRect.left,\n top,\n };\n }\n }\n\n const coords = editor.view.coordsAtPos(editor.state.selection.from);\n if (kind === 'block') {\n return {\n left: editorRect.left - hostRect.left,\n top: coords.bottom - hostRect.top + gap,\n width: blockWidth,\n };\n }\n return {\n left: coords.left - hostRect.left,\n top: coords.bottom - hostRect.top + gap,\n };\n}\n\nexport function MathEditorPopover({\n editor,\n kind,\n mode,\n pos,\n latex,\n isNew = false,\n newLabel,\n placeholder,\n doneLabel,\n onConfirm,\n onCancel,\n}: MathEditorPopoverProps) {\n const [draft, setDraft] = useState(latex);\n const [layerStyle, setLayerStyle] = useState<CSSProperties>(() =>\n getMathLayerStyle(editor, kind, pos),\n );\n const titleId = useId();\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);\n const rootRef = useRef<HTMLDivElement>(null);\n const isBlock = kind === 'block';\n const host = editor.view.dom.parentElement;\n\n useEffect(() => {\n setDraft(latex);\n }, [latex]);\n\n useEffect(() => {\n const update = () => setLayerStyle(getMathLayerStyle(editor, kind, pos));\n update();\n window.addEventListener('resize', update);\n return () => window.removeEventListener('resize', update);\n }, [editor, kind, pos, draft]);\n\n useEffect(() => {\n inputRef.current?.focus();\n }, []);\n\n useEffect(() => {\n if (pos == null) return;\n const el = resolveNodeEl(editor, pos);\n if (!el) return;\n el.classList.add('tmr-math-editing');\n return () => {\n el.classList.remove('tmr-math-editing');\n };\n }, [editor, pos]);\n\n useEffect(() => {\n if (pos == null) return;\n const el = resolveNodeEl(editor, pos);\n if (!el) return;\n const target = mathRenderTarget(el, isBlock);\n const src = draft.trim();\n if (src) renderMathInto(target, src, isBlock);\n else target.textContent = '';\n }, [draft, pos, editor, isBlock]);\n\n useEffect(() => {\n if (pos == null || isNew) return;\n const el = resolveNodeEl(editor, pos);\n if (!el) return;\n const target = mathRenderTarget(el, isBlock);\n return () => {\n const restore = latex.trim();\n if (restore) renderMathInto(target, restore, isBlock);\n else target.textContent = '';\n };\n }, [pos, editor, isBlock, latex, isNew]);\n\n useEffect(() => {\n const onPtr = (e: PointerEvent) => {\n const target = e.target;\n if (!(target instanceof Node)) return;\n if (rootRef.current?.contains(target)) return;\n if (pos != null) {\n const el = resolveNodeEl(editor, pos);\n if (el?.contains(target)) return;\n }\n onCancel();\n };\n document.addEventListener('pointerdown', onPtr);\n return () => document.removeEventListener('pointerdown', onPtr);\n }, [onCancel, editor, pos]);\n\n const canSubmit = draft.trim().length > 0;\n const showHint = !canSubmit && (mode === 'insert' || isNew);\n\n const submit = () => {\n if (!canSubmit) return;\n onConfirm(draft.trim());\n };\n\n const onKeyDown = (\n e: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>,\n ) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n onCancel();\n return;\n }\n if (e.key !== 'Enter') return;\n if (isBlock && !(e.metaKey || e.ctrlKey)) return;\n e.preventDefault();\n submit();\n };\n\n const field = isBlock ? (\n <textarea\n ref={inputRef as RefObject<HTMLTextAreaElement>}\n className={styles.mathInput}\n value={draft}\n placeholder={placeholder}\n rows={Math.max(3, draft.split('\\n').length)}\n aria-labelledby={titleId}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n />\n ) : (\n <input\n ref={inputRef as RefObject<HTMLInputElement>}\n className={styles.mathInput}\n value={draft}\n placeholder={placeholder}\n aria-labelledby={titleId}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={onKeyDown}\n />\n );\n\n if (!host) return null;\n\n return createPortal(\n <div\n ref={rootRef}\n className={styles.mathLayer}\n style={layerStyle}\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby={titleId}\n >\n <div\n className={\n isBlock\n ? `${styles.mathStack} ${styles.mathStackBlock}`\n : styles.mathStack\n }\n >\n {showHint ? (\n <div\n id={titleId}\n className={\n isBlock\n ? `${styles.mathHint} ${styles.mathHintBlock}`\n : styles.mathHint\n }\n >\n <span className={styles.mathHintIcon} aria-hidden>\n {isBlock ? 'TeX' : '√x'}\n </span>\n {newLabel}\n </div>\n ) : null}\n <div\n id={showHint ? undefined : titleId}\n className={\n isBlock\n ? `${styles.mathField} ${styles.mathFieldBlock}`\n : styles.mathField\n }\n >\n {field}\n <button\n type=\"button\"\n className={styles.mathDone}\n disabled={!canSubmit}\n title={\n isBlock ? `${doneLabel} (⌘/Ctrl+Enter)` : `${doneLabel} (Enter)`\n }\n onClick={submit}\n >\n {doneLabel}\n <span className={styles.mathDoneKbd} aria-hidden>\n ↵\n </span>\n </button>\n </div>\n </div>\n </div>,\n host,\n );\n}\n","'use client';\n\nimport * as Popover from '@radix-ui/react-popover';\nimport { useEditorState, type Editor } from '@tiptap/react';\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ButtonHTMLAttributes,\n type ReactNode,\n} from 'react';\nimport {\n AlignCenterIcon,\n AlignJustifyIcon,\n AlignLeftIcon,\n AlignRightIcon,\n BoldIcon,\n ChevronDownIcon,\n CodeIcon,\n DetailsIcon,\n ImageIcon,\n IndentDecreaseIcon,\n IndentIncreaseIcon,\n ItalicIcon,\n LineHeightIcon,\n LinkIcon,\n ListChecksIcon,\n ListOrderedIcon,\n ListUnorderedIcon,\n RedoIcon,\n SearchIcon,\n StrikethroughIcon,\n UnderlineIcon,\n UndoIcon,\n} from '../icons';\nimport { getLocaleBundle, type LocaleCode } from '../i18n';\nimport type { ToolbarLabels } from '../labels';\nimport {\n applyNumberingFormat,\n NUMBERING_FORMAT_IDS,\n NUMBERING_FORMAT_LABELS,\n NUMBERING_FORMAT_PREVIEWS,\n readCurrentNumberingFormat,\n type NumberingFormat,\n} from '../numberingFormat';\nimport {\n MD_FILE_RE,\n resolveImportMenuItems,\n type ImportMenuItem,\n} from '../importMenu';\nimport type { SourceRef } from '../citationUtils';\nimport { hasImageUpload } from '../imageUpload';\nimport { insertMarkdown } from '../insertMarkdown';\nimport { subscribeMathClick, type MathKind } from '../math';\nimport styles from '../styles/toolbar.module.css';\nimport { ColorPalette } from './ColorPalette';\nimport { ImageAlignTools } from './ImageAlignTools';\nimport { LinkPopover } from './LinkPopover';\nimport { TableHandles } from './TableHandles';\nimport { MathEditorPopover } from './MathEditorPopover';\n\n/** `onImportDocument` 的成功返回。给字符串等价于 `{ markdown }`。 */\nexport interface ImportDocumentResult {\n markdown: string;\n sources?: SourceRef[];\n}\n\n/** 导入进度。包用来更新光标占位文案并禁用导入按钮。 */\nexport interface ImportDocumentProgress {\n /** 0–1。通常只有上传阶段可测;转换阶段可省略。 */\n ratio?: number;\n phase: 'upload' | 'convert';\n}\n\nexport interface ImportDocumentContext {\n /** 用户点取消或组件卸载时 abort,宿主据此中断请求。 */\n signal: AbortSignal;\n onProgress?: (p: ImportDocumentProgress) => void;\n}\n\n/** More 菜单里注入的自定义项(宿主用它扩展工具栏,如「导入项目报告」)。 */\nexport interface ExtraToolbarItem {\n key: string;\n /** 左侧图标插槽(可选,文字或 SVG 皆可)。 */\n icon?: ReactNode;\n label: string;\n disabled?: boolean;\n /** 点击回调,拿到 editor 实例自行操作(如 insertContent)。 */\n onSelect: (editor: Editor) => void;\n}\n\nexport interface EditorToolbarProps {\n editor: Editor;\n /**\n * 图片上传回调:收到文件,返回可访问的图片 URL。未提供则不显示图片按钮。\n * 抛错会转交 onError(..., 'image')。\n */\n onImageUpload?: (file: File) => Promise<string>;\n /**\n * 副作用错误回调(图片上传 / Markdown 导入失败),供宿主弹自己的提示。\n * `source` 区分来源,避免一律显示「图片上传失败」。\n */\n onError?: (err: unknown, source?: 'image' | 'markdown' | 'import') => void;\n /**\n * 非 Markdown 文件的转换:收到文件,返回可插入的 Markdown。未提供则导入只吃 .md。\n * 包内不校验扩展名——格式白名单由宿主的 `importAccept` 与本回调自己负责。\n * 抛错转交 onError(..., 'import');`AbortError` 视为用户取消,不报错。\n */\n onImportDocument?: (\n file: File,\n ctx: ImportDocumentContext,\n ) => Promise<ImportDocumentResult | string>;\n /**\n * 追加到导入 input 的 accept(默认已含 `.md,.markdown,text/markdown`)。\n * 例:`.doc,.docx,.csv,.pdf`。未传 `importMenuItems` 且传了 `onImportDocument` 时,\n * 下拉会多一项「Document」。\n */\n importAccept?: string;\n /**\n * 主栏导入下拉的选项。不传则 Markdown;再加 `onImportDocument` + `importAccept`\n * 时多一项 Document。宿主要拆成 Word / PDF 时自己传入。\n */\n importMenuItems?: ImportMenuItem[];\n /** 是否显示导入下拉,默认 `true`。卡片等场景可关掉。 */\n showImport?: boolean;\n /** 内置语言,默认英文。 */\n locale?: LocaleCode;\n /**\n * 工具栏最右端的搜索入口(放大镜)。**不传就不渲染**——条子本身归\n * `<MarkdownWysiwygEditor>`,这里只给一个入口,宿主接上自己的编辑器句柄即可:\n * `onSearch={() => handle.current?.openFind()}`。\n *\n * 条子归编辑器渲染(`findBar` 开)时才接;`findBar={false}` 的宿主自己摆条子,\n * 这个入口点了也不会弹东西。只读态照样可查,所以不随 `editable` 灰显。\n */\n onSearch?: () => void;\n /** 条子当前是否开着(驱动按钮的 aria-pressed / 高亮)。不传恒为未激活。 */\n searchActive?: boolean;\n /** 追加到 More 菜单末尾的自定义项。 */\n extraToolbarItems?: ExtraToolbarItem[];\n className?: string;\n}\n\nexport type { ImportMenuItem };\n\n/**\n * 单个工具栏按钮:激活态高亮,disabled 时灰显。\n *\n * forwardRef + 透传其余属性,是为了让它能当 Radix 的触发器(`<LinkPopover>` 的 `asChild`\n * 要挂 ref,并把 `aria-expanded` / `data-state` 传下来)。\n */\nconst ToolbarButton = forwardRef<\n HTMLButtonElement,\n {\n title: string;\n active?: boolean;\n disabled?: boolean;\n busy?: boolean;\n onClick?: () => void;\n children: ReactNode;\n } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'title' | 'onClick' | 'children'>\n>(function ToolbarButton(\n { title, active, disabled, busy, onClick, children, className, ...rest },\n ref,\n) {\n return (\n <button\n {...rest}\n ref={ref}\n type=\"button\"\n title={title}\n aria-label={title}\n aria-pressed={active}\n aria-busy={busy}\n disabled={disabled}\n className={`${styles.btn} ${active ? styles.btnActive : ''}${className ? ` ${className}` : ''}`}\n // mousedown + preventDefault 防止点击工具栏时编辑器失焦丢选区\n onMouseDown={(e) => e.preventDefault()}\n onClick={onClick}\n >\n {children}\n </button>\n );\n});\n\nfunction Divider() {\n return <span className={styles.divider} aria-hidden />;\n}\n\n/** 行高下拉的档位(对齐语雀 / Notion 的习惯值;不含\"默认\")。 */\nconst LINE_HEIGHTS = ['1', '1.15', '1.5', '2', '2.5', '3'] as const;\n\n/** 段落级样式读自段落或标题(光标所在处)——对齐 / 缩进 / 行高共用。 */\nfunction readParaAttr<T>(editor: Editor, name: string): T | null {\n return (\n ((editor.getAttributes('paragraph')[name] ??\n editor.getAttributes('heading')[name]) as T | undefined) ?? null\n );\n}\n\n// 关闭当前菜单的回调(由 MenuPopover 注入,MenuItem 选中后调用以收起)。\nconst MenuCloseCtx = createContext<() => void>(() => {});\n\n/**\n * 菜单气泡:用 Radix Popover 而非 DropdownMenu——后者会强制抢焦点(menu 语义),\n * 打开即让编辑器 blur、丢选区;Popover 可 onOpenAutoFocus preventDefault 保持编辑器焦点。\n * 菜单项为普通 button(onMouseDown preventDefault 不夺焦),行为与原生菜单一致。\n */\nfunction MenuPopover({\n trigger,\n children,\n contentClassName,\n align = 'start',\n}: {\n trigger: ReactNode;\n children: ReactNode;\n contentClassName?: string;\n align?: 'start' | 'center' | 'end';\n}) {\n const [open, setOpen] = useState(false);\n const close = useCallback(() => setOpen(false), []);\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger asChild>{trigger}</Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n className={\n contentClassName ? `${styles.menu} ${contentClassName}` : styles.menu\n }\n sideOffset={4}\n align={align}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <MenuCloseCtx.Provider value={close}>\n {children}\n </MenuCloseCtx.Provider>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\nfunction MenuItem({\n selected,\n disabled,\n onSelect,\n children,\n}: {\n selected?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n children: ReactNode;\n}) {\n const close = useContext(MenuCloseCtx);\n return (\n <button\n type=\"button\"\n disabled={disabled}\n className={`${styles.menuItem} ${selected ? styles.menuItemSelected : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onSelect();\n close();\n }}\n >\n {children}\n </button>\n );\n}\n\n/** 编号格式:单列预览卡片,每张是五行嵌套编号。 */\nfunction NumberingFormatGrid({\n current,\n onSelect,\n}: {\n current: NumberingFormat | null;\n onSelect: (format: NumberingFormat) => void;\n}) {\n const close = useContext(MenuCloseCtx);\n return (\n <div className={styles.numberingGrid} role=\"group\" aria-label=\"Numbering formats\">\n {NUMBERING_FORMAT_IDS.map((format) => {\n const active = current === format;\n return (\n <button\n key={format}\n type=\"button\"\n role=\"menuitem\"\n aria-label={NUMBERING_FORMAT_LABELS[format]}\n aria-pressed={active}\n className={`${styles.numberingOption}${active ? ` ${styles.numberingOptionActive}` : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n onSelect(format);\n close();\n }}\n >\n <span className={styles.numberingPreview}>\n {NUMBERING_FORMAT_PREVIEWS[format].map((row, index) => (\n <span\n key={`${format}-${index}`}\n className={styles.numberingPreviewRow}\n data-depth={row.depth}\n >\n <span className={styles.numberingPreviewMarker}>{row.marker}</span>\n <span className={styles.numberingPreviewLine} />\n </span>\n ))}\n </span>\n </button>\n );\n })}\n </div>\n );\n}\n\nconst TABLE_PICKER_COLS = 10;\nconst TABLE_PICKER_ROWS = 8;\n\n/**\n * More 菜单里的「插入表格」:二级面板用网格悬停选行列,点击插入。\n * 保持 Popover(不夺编辑器焦点),与 MenuItem 一致。\n */\nfunction TableInsertItem({\n disabled,\n labels,\n onInsert,\n}: {\n disabled?: boolean;\n labels: Pick<ToolbarLabels, 'tableInsert' | 'tableSizeSelected'>;\n onInsert: (rows: number, cols: number) => void;\n}) {\n const closeMenu = useContext(MenuCloseCtx);\n const [open, setOpen] = useState(false);\n const [hover, setHover] = useState({ cols: 0, rows: 0 });\n\n const resetHover = useCallback(() => setHover({ cols: 0, rows: 0 }), []);\n\n return (\n <Popover.Root\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) resetHover();\n }}\n >\n <Popover.Trigger asChild>\n <button\n type=\"button\"\n disabled={disabled}\n className={`${styles.menuItem} ${open ? styles.menuItemSelected : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>⊞</span>\n {labels.tableInsert}\n </span>\n <ChevronDownIcon size={12} className={styles.tableInsertCaret} />\n </button>\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n className={styles.tableSizePicker}\n side=\"right\"\n align=\"start\"\n sideOffset={6}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <div\n className={styles.tableSizeGrid}\n style={{\n gridTemplateColumns: `repeat(${TABLE_PICKER_COLS}, 1fr)`,\n }}\n onMouseLeave={resetHover}\n >\n {Array.from({ length: TABLE_PICKER_ROWS * TABLE_PICKER_COLS }, (_, i) => {\n const col = (i % TABLE_PICKER_COLS) + 1;\n const row = Math.floor(i / TABLE_PICKER_COLS) + 1;\n const active = hover.cols > 0 && col <= hover.cols && row <= hover.rows;\n return (\n <button\n key={i}\n type=\"button\"\n aria-label={labels.tableSizeSelected(col, row)}\n className={`${styles.tableSizeCell} ${active ? styles.tableSizeCellActive : ''}`}\n onMouseDown={(e) => e.preventDefault()}\n onMouseEnter={() => setHover({ cols: col, rows: row })}\n onClick={() => {\n onInsert(row, col);\n setOpen(false);\n resetHover();\n closeMenu();\n }}\n />\n );\n })}\n </div>\n <div className={styles.tableSizeLabel}>\n {hover.cols > 0\n ? labels.tableSizeSelected(hover.cols, hover.rows)\n : labels.tableInsert}\n </div>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** 颜色气泡:点色块即应用并关闭。 */\nfunction ColorPopover({\n title,\n glyph,\n bar,\n value,\n onPick,\n locale,\n}: {\n title: string;\n glyph: string;\n bar: string;\n value?: string;\n onPick: (color: string | null) => void;\n locale: LocaleCode;\n}) {\n const [open, setOpen] = useState(false);\n return (\n <Popover.Root open={open} onOpenChange={setOpen}>\n <Popover.Trigger asChild>\n <button\n type=\"button\"\n className={styles.btn}\n title={title}\n aria-label={title}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.colorIcon}>\n <span className={styles.colorIconGlyph}>{glyph}</span>\n <span className={styles.colorIconBar} style={{ background: bar }} />\n </span>\n </button>\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Content\n className={styles.popoverContent}\n sideOffset={4}\n align=\"start\"\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n <ColorPalette\n value={value}\n onPick={(c) => {\n onPick(c);\n setOpen(false);\n }}\n locale={locale}\n />\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\nconst HEADING_LEVELS = [1, 2, 3, 4, 5, 6] as const;\nconst FONT_SIZES = ['12px', '14px', '16px', '18px', '20px', '24px', '30px'] as const;\n\n/**\n * 顶部操作栏(零 UI 框架依赖,Radix Popover 菜单 + 内联 SVG 图标)。位置由父级控制,\n * 这里只渲染按钮 + 反映 editor 激活态。不含业务耦合,扩展项经 extraToolbarItems 注入。\n */\nexport function EditorToolbar({\n editor,\n onImageUpload,\n onError,\n onImportDocument,\n importAccept,\n importMenuItems,\n showImport = true,\n locale = 'en',\n onSearch,\n searchActive = false,\n extraToolbarItems,\n className,\n}: EditorToolbarProps) {\n const t: ToolbarLabels = getLocaleBundle(locale).toolbar;\n\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n bold: e.isActive('bold'),\n italic: e.isActive('italic'),\n underline: e.isActive('underline'),\n strike: e.isActive('strike'),\n code: e.isActive('code'),\n link: e.isActive('link'),\n taskList: e.isActive('taskList'),\n headingLevel: ([1, 2, 3, 4, 5, 6] as const).find((lv) =>\n e.isActive('heading', { level: lv }),\n ),\n fontSize: (e.getAttributes('textStyle').fontSize as string) || undefined,\n isParagraph: e.isActive('paragraph'),\n bulletList: e.isActive('bulletList'),\n orderedList: e.isActive('orderedList'),\n numberingFormat: readCurrentNumberingFormat(e),\n blockquote: e.isActive('blockquote'),\n details: e.isActive('details'),\n textColor: (e.getAttributes('textStyle').color as string) || undefined,\n highlightColor:\n (e.getAttributes('highlight').color as string) || undefined,\n superscript: e.isActive('superscript'),\n subscript: e.isActive('subscript'),\n textAlign: readParaAttr<string>(e, 'textAlign'),\n indent: readParaAttr<number>(e, 'indent'),\n lineHeight: readParaAttr<number>(e, 'lineHeight'),\n inTable: e.isActive('table'),\n canUndo: e.can().undo(),\n canRedo: e.can().redo(),\n }),\n });\n\n // 编辑器是否聚焦:仅正文聚焦时才允许调字号。Popover 菜单开/关已阻止抢焦点,\n // 故打开下拉不会误触 blur。\n const [editorFocused, setEditorFocused] = useState(() => editor.isFocused);\n useEffect(() => {\n const onFocus = () => setEditorFocused(true);\n const onBlur = () => setEditorFocused(false);\n editor.on('focus', onFocus);\n editor.on('blur', onBlur);\n return () => {\n editor.off('focus', onFocus);\n editor.off('blur', onBlur);\n };\n }, [editor]);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const chain = () => editor.chain().focus();\n\n // ── 表格工具条(右键召唤):右键单元格时在鼠标处弹出,点别处/Esc 隐藏 ──\n // ── 图片:编辑器注册了 imageUpload 扩展就插上传占位块(块内拖拽/点选 → 进度 → 换图,\n // 配置见 <MarkdownWysiwygEditor imageUpload>);否则退回旧的直传路径 ──\n const imageUploadEnabled = hasImageUpload(editor);\n const handlePickImage = () => {\n if (imageUploadEnabled) {\n chain().setImageUploadNode().run();\n return;\n }\n fileInputRef.current?.click();\n };\n const handleImageChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = '';\n if (!file || !onImageUpload) return;\n try {\n const url = await onImageUpload(file);\n if (!url) throw new Error('empty url');\n chain().setImage({ src: url }).run();\n } catch (err) {\n console.error('image upload failed:', err);\n onError?.(err, 'image');\n }\n };\n\n // ── 导入:.md 包内读文本插入;其余原样交给宿主 onImportDocument ──\n const importInputRef = useRef<HTMLInputElement>(null);\n const [importing, setImporting] = useState<ImportDocumentProgress | null>(\n null,\n );\n const importAbortRef = useRef<AbortController | null>(null);\n\n // 卸载(如切路由)中断在飞的导入,避免请求泄漏与卸载后 setState\n useEffect(() => () => importAbortRef.current?.abort(), []);\n\n const handleImportChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = '';\n if (!file) return;\n\n if (MD_FILE_RE.test(file.name)) {\n try {\n insertMarkdown(editor, await file.text());\n } catch (err) {\n console.error('markdown import failed:', err);\n onError?.(err, 'markdown');\n }\n return;\n }\n\n if (!onImportDocument) {\n onError?.(new Error(`unsupported file: ${file.name}`), 'import');\n return;\n }\n\n const ctrl = new AbortController();\n importAbortRef.current = ctrl;\n setImporting({ phase: 'upload', ratio: 0 });\n const labelOf = (p: ImportDocumentProgress) =>\n p.phase === 'convert'\n ? t.importDocumentConverting\n : t.importDocumentUploading(Math.round((p.ratio ?? 0) * 100));\n editor.commands.insertImportPlaceholder(labelOf({ phase: 'upload', ratio: 0 }));\n try {\n const raw = await onImportDocument(file, {\n signal: ctrl.signal,\n onProgress: (p) => {\n if (ctrl.signal.aborted) return;\n setImporting(p);\n editor.commands.updateImportPlaceholder(labelOf(p));\n },\n });\n editor.commands.removeImportPlaceholder();\n const md = typeof raw === 'string' ? raw : raw.markdown;\n const sources = typeof raw === 'string' ? [] : raw.sources ?? [];\n insertMarkdown(editor, md, sources);\n } catch (err) {\n editor.commands.removeImportPlaceholder();\n // 卸载 abort 不算错误\n if (!ctrl.signal.aborted) {\n console.error('document import failed:', err);\n onError?.(err, 'import');\n }\n } finally {\n if (importAbortRef.current === ctrl) importAbortRef.current = null;\n setImporting(null);\n }\n };\n\n const importItems = resolveImportMenuItems({\n labels: t,\n onImportDocument,\n importAccept,\n importMenuItems,\n });\n\n const pickImport = (accept: string) => {\n const input = importInputRef.current;\n if (!input) return;\n input.accept = accept;\n input.value = '';\n input.click();\n };\n\n // 进度写在光标占位上;按钮只区分空闲 / 忙碌。\n const importTitle = importing ? t.importDocumentBusy : t.importDocumentHint;\n\n const applyTextColor = (color: string | null) => {\n if (color) chain().setColor(color).run();\n else chain().unsetColor().run();\n };\n const applyHighlight = (color: string | null) => {\n if (color) chain().setHighlight({ color }).run();\n else chain().unsetHighlight().run();\n };\n\n const setBlockStyle = (level: number) => {\n if (level === 0) chain().setParagraph().run();\n else\n chain()\n .toggleHeading({ level: level as 1 | 2 | 3 | 4 | 5 | 6 })\n .run();\n };\n\n const applyFontSize = (size: string) => {\n if (size) chain().setFontSize(size).run();\n else chain().unsetFontSize().run();\n };\n const canEditFontSize = editorFocused && state.isParagraph;\n\n const [mathEdit, setMathEdit] = useState<{\n kind: MathKind;\n mode: 'insert' | 'edit';\n pos?: number;\n latex: string;\n isNew?: boolean;\n } | null>(null);\n\n useEffect(() => subscribeMathClick((payload) => {\n setMathEdit({\n kind: payload.kind,\n mode: 'edit',\n pos: payload.pos,\n latex: payload.latex,\n });\n }), []);\n\n const openInsertMath = (kind: MathKind) => {\n const { from, to } = editor.state.selection;\n const selected =\n from === to ? '' : editor.state.doc.textBetween(from, to, ' ');\n const type = kind === 'inline' ? 'inlineMath' : 'blockMath';\n const searchFrom = from;\n chain().insertContent({ type, attrs: { latex: selected } }).run();\n let pos: number | null = null;\n editor.state.doc.nodesBetween(\n searchFrom,\n editor.state.doc.content.size,\n (node, nodePos) => {\n if (pos != null) return false;\n if (\n node.type.name === type &&\n String(node.attrs.latex ?? '') === selected\n ) {\n pos = nodePos;\n return false;\n }\n },\n );\n if (pos == null) {\n setMathEdit({ kind, mode: 'insert', latex: selected, isNew: true });\n return;\n }\n setMathEdit({\n kind,\n mode: 'edit',\n pos,\n latex: selected,\n isNew: true,\n });\n };\n\n const applyMath = (latex: string) => {\n if (!mathEdit) return;\n const { kind, mode, pos } = mathEdit;\n setMathEdit(null);\n if (mode === 'edit' && pos != null) {\n if (kind === 'inline') {\n chain().updateInlineMath({ latex, pos }).run();\n } else {\n chain().updateBlockMath({ latex, pos }).run();\n }\n return;\n }\n if (kind === 'inline') {\n chain().insertContent({ type: 'inlineMath', attrs: { latex } }).run();\n } else {\n chain().insertContent({ type: 'blockMath', attrs: { latex } }).run();\n }\n };\n\n const cancelMath = () => {\n if (!mathEdit) return;\n const { isNew, pos } = mathEdit;\n if (isNew && pos != null) {\n const node = editor.state.doc.nodeAt(pos);\n if (\n node &&\n (node.type.name === 'inlineMath' || node.type.name === 'blockMath')\n ) {\n editor\n .chain()\n .focus()\n .deleteRange({ from: pos, to: pos + node.nodeSize })\n .run();\n }\n }\n setMathEdit(null);\n };\n\n const scriptActive = state.superscript\n ? 'superscript'\n : state.subscript\n ? 'subscript'\n : 'normal';\n\n return (\n <>\n <div\n className={\n className ? `${styles.toolbar} ${className}` : styles.toolbar\n }\n >\n <div className={styles.inner}>\n {/* 撤销 / 重做 */}\n <ToolbarButton\n title={t.undo}\n disabled={!state.canUndo}\n onClick={() => chain().undo().run()}\n >\n <UndoIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.redo}\n disabled={!state.canRedo}\n onClick={() => chain().redo().run()}\n >\n <RedoIcon />\n </ToolbarButton>\n\n <Divider />\n\n {/* 段落样式下拉(正文 / 标题 1~6) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={t.style}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>{t.style}</span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={(state.headingLevel ?? 0) === 0}\n onSelect={() => setBlockStyle(0)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>¶</span>\n {t.normalText}\n </span>\n </MenuItem>\n {HEADING_LEVELS.map((lv) => (\n <MenuItem\n key={lv}\n selected={state.headingLevel === lv}\n onSelect={() => setBlockStyle(lv)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{`H${lv}`}</span>\n {t.headingLabel(lv)}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n {/* 正文字号下拉:仅正文可调 */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={t.fontSize}\n aria-label={t.fontSize}\n disabled={!canEditFontSize}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>\n {state.fontSize || t.fontSize}\n </span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={!state.fontSize}\n onSelect={() => applyFontSize('')}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>A</span>\n {t.fontSizeDefault}\n </span>\n </MenuItem>\n {FONT_SIZES.map((size) => (\n <MenuItem\n key={size}\n selected={state.fontSize === size}\n onSelect={() => applyFontSize(size)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{parseInt(size, 10)}</span>\n {size}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n <Divider />\n\n {/* 文字格式 */}\n <ToolbarButton\n title={t.bold}\n active={state.bold}\n onClick={() => chain().toggleBold().run()}\n >\n <BoldIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.italic}\n active={state.italic}\n onClick={() => chain().toggleItalic().run()}\n >\n <ItalicIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.underline}\n active={state.underline}\n onClick={() => chain().toggleUnderline().run()}\n >\n <UnderlineIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.strike}\n active={state.strike}\n onClick={() => chain().toggleStrike().run()}\n >\n <StrikethroughIcon />\n </ToolbarButton>\n <ToolbarButton\n title={t.code}\n active={state.code}\n onClick={() => chain().toggleCode().run()}\n >\n <CodeIcon />\n </ToolbarButton>\n\n {/* 文字颜色 */}\n <ColorPopover\n title={t.textColor}\n glyph=\"T\"\n bar={state.textColor || '#ff3b30'}\n value={state.textColor}\n onPick={applyTextColor}\n locale={locale}\n />\n {/* 文字高亮 */}\n <ColorPopover\n title={t.highlight}\n glyph=\"✎\"\n bar={state.highlightColor || '#ffd83d'}\n value={state.highlightColor}\n onPick={applyHighlight}\n locale={locale}\n />\n\n {/* 上标 / 下标下拉(A▾) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.scriptTrigger} ${\n scriptActive !== 'normal' ? styles.btnActive : ''\n }`}\n title={t.scriptMenu}\n aria-label={t.scriptMenu}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.scriptTriggerGlyph}>A</span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={scriptActive === 'normal'}\n onSelect={() => chain().unsetSuperscript().unsetSubscript().run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>A</span>\n {t.scriptNormal}\n </span>\n </MenuItem>\n <MenuItem\n selected={scriptActive === 'superscript'}\n onSelect={() => chain().unsetSubscript().toggleSuperscript().run()}\n >\n <span className={styles.styleItem}>\n <span className={`${styles.styleIcon} ${styles.scriptGlyph}`}>\n x<sup>2</sup>\n </span>\n {t.superscript}\n </span>\n </MenuItem>\n <MenuItem\n selected={scriptActive === 'subscript'}\n onSelect={() => chain().unsetSuperscript().toggleSubscript().run()}\n >\n <span className={styles.styleItem}>\n <span className={`${styles.styleIcon} ${styles.scriptGlyph}`}>\n x<sub>2</sub>\n </span>\n {t.subscript}\n </span>\n </MenuItem>\n </MenuPopover>\n\n <Divider />\n\n {/* 插入媒体。链接走浮层:输入地址 + 应用 / 在新窗口打开 / 移除,\n 光标在链接里打开时预填它的 href(旧实现是 window.prompt,在链接里点一下就删)。 */}\n <LinkPopover\n editor={editor}\n locale={locale}\n trigger={\n <ToolbarButton title={t.link} active={state.link}>\n <LinkIcon />\n </ToolbarButton>\n }\n />\n {imageUploadEnabled || onImageUpload ? (\n <>\n <ToolbarButton title={t.image} onClick={handlePickImage}>\n <ImageIcon />\n </ToolbarButton>\n {!imageUploadEnabled && (\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n hidden\n onChange={handleImageChange}\n />\n )}\n </>\n ) : null}\n {showImport ? (\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={importTitle}\n aria-label={importTitle}\n aria-busy={!!importing}\n disabled={!!importing}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>\n {t.importDocument}\n </span>\n <ChevronDownIcon\n size={12}\n className={styles.styleTriggerCaret}\n />\n </button>\n }\n >\n {importItems.map((item) => (\n <MenuItem\n key={item.key}\n onSelect={() => pickImport(item.accept)}\n >\n {item.label}\n </MenuItem>\n ))}\n </MenuPopover>\n ) : null}\n <ToolbarButton\n title={t.blockquote}\n active={state.blockquote}\n onClick={() => chain().toggleBlockquote().run()}\n >\n <span className={styles.txtIcon}>❝</span>\n </ToolbarButton>\n <ToolbarButton\n title={t.details}\n active={state.details}\n onClick={() => {\n const next = editor.chain().focus();\n if (editor.isActive('details')) next.unsetDetails().run();\n else next.setDetails().run();\n }}\n >\n <DetailsIcon />\n </ToolbarButton>\n\n <Divider />\n\n {/* 段落排版:对齐 / 缩进 / 行高(三组下拉) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.styleTrigger} ${styles.iconTrigger}`}\n title={t.align}\n aria-label={t.align}\n onMouseDown={(e) => e.preventDefault()}\n >\n {state.textAlign === 'center' ? (\n <AlignCenterIcon />\n ) : state.textAlign === 'right' ? (\n <AlignRightIcon />\n ) : state.textAlign === 'justify' ? (\n <AlignJustifyIcon />\n ) : (\n <AlignLeftIcon />\n )}\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n {(\n [\n { value: 'left', label: t.alignLeft, Icon: AlignLeftIcon },\n { value: 'center', label: t.alignCenter, Icon: AlignCenterIcon },\n { value: 'right', label: t.alignRight, Icon: AlignRightIcon },\n { value: 'justify', label: t.alignJustify, Icon: AlignJustifyIcon },\n ] as const\n ).map(({ value, label, Icon }) => (\n <MenuItem\n key={value}\n selected={state.textAlign === value}\n onSelect={() => chain().setTextAlign(value).run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>\n <Icon size={14} />\n </span>\n {label}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.styleTrigger} ${styles.iconTrigger}`}\n title={t.indent}\n aria-label={t.indent}\n onMouseDown={(e) => e.preventDefault()}\n >\n <IndentIncreaseIcon />\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem onSelect={() => chain().increaseIndent().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>\n <IndentIncreaseIcon size={14} />\n </span>\n {t.indentIncrease}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => chain().decreaseIndent().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>\n <IndentDecreaseIcon size={14} />\n </span>\n {t.indentDecrease}\n </span>\n </MenuItem>\n </MenuPopover>\n\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={`${styles.styleTrigger} ${styles.iconTrigger}`}\n title={t.lineHeight}\n aria-label={t.lineHeight}\n onMouseDown={(e) => e.preventDefault()}\n >\n <LineHeightIcon />\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem\n selected={!state.lineHeight}\n onSelect={() => chain().setLineHeight(null).run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>—</span>\n {t.lineHeightDefault}\n </span>\n </MenuItem>\n {LINE_HEIGHTS.map((value) => (\n <MenuItem\n key={value}\n selected={state.lineHeight === Number(value)}\n onSelect={() => chain().setLineHeight(Number(value)).run()}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>≡</span>\n {value}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n\n <Divider />\n\n {/* 列表。有序列表是「切换 + 编号格式」一组:箭头打开编号预览。 */}\n <ToolbarButton\n title={t.bulletList}\n active={state.bulletList}\n onClick={() => chain().toggleBulletList().run()}\n >\n <ListUnorderedIcon />\n </ToolbarButton>\n <span\n className={`${styles.listSplit}${\n state.orderedList ? ` ${styles.listSplitActive}` : ''\n }`}\n >\n <ToolbarButton\n title={t.orderedList}\n active={state.orderedList}\n onClick={() => chain().toggleOrderedList().run()}\n >\n <ListOrderedIcon />\n </ToolbarButton>\n <MenuPopover\n align=\"end\"\n contentClassName={styles.numberingMenu}\n trigger={\n <button\n type=\"button\"\n className={`${styles.numberingCaret}${\n state.numberingFormat ? ` ${styles.numberingCaretActive}` : ''\n }`}\n title={t.numberingFormat}\n aria-label={t.numberingFormat}\n onMouseDown={(e) => e.preventDefault()}\n >\n <ChevronDownIcon size={12} />\n </button>\n }\n >\n <NumberingFormatGrid\n current={state.numberingFormat}\n onSelect={(format) => applyNumberingFormat(editor, format)}\n />\n </MenuPopover>\n </span>\n <ToolbarButton\n title={t.taskList}\n active={state.taskList}\n onClick={() => chain().toggleTaskList().run()}\n >\n <ListChecksIcon />\n </ToolbarButton>\n\n <Divider />\n\n {/* 查找替换入口:条子归编辑器,这里只开一道门。宿主接 onSearch 时才出现。\n 位置在 More 左侧——More 是「其余都以它收口」的兜底菜单,惯例留在最右;\n 而且行内本来就排满,谁在最后谁就先被挤到下一行。 */}\n {onSearch ? (\n <ToolbarButton\n title={t.search}\n active={searchActive}\n onClick={onSearch}\n >\n <SearchIcon />\n </ToolbarButton>\n ) : null}\n\n {/* More 下拉(代码块、分割线、表格 + 宿主注入项) */}\n <MenuPopover\n trigger={\n <button\n type=\"button\"\n className={styles.styleTrigger}\n title={t.more}\n onMouseDown={(e) => e.preventDefault()}\n >\n <span className={styles.styleTriggerLabel}>{t.more}</span>\n <ChevronDownIcon size={12} className={styles.styleTriggerCaret} />\n </button>\n }\n >\n <MenuItem onSelect={() => chain().toggleCodeBlock().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{'{ }'}</span>\n {t.codeBlock}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => openInsertMath('inline')}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>√x</span>\n {t.inlineMath}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => openInsertMath('block')}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>∑</span>\n {t.blockMath}\n </span>\n </MenuItem>\n <MenuItem onSelect={() => chain().setHorizontalRule().run()}>\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>—</span>\n {t.hr}\n </span>\n </MenuItem>\n <TableInsertItem\n disabled={state.inTable}\n labels={t}\n onInsert={(rows, cols) =>\n chain()\n .insertTable({ rows, cols, withHeaderRow: true })\n .run()\n }\n />\n {extraToolbarItems?.map((item) => (\n <MenuItem\n key={item.key}\n disabled={item.disabled}\n onSelect={() => item.onSelect(editor)}\n >\n <span className={styles.styleItem}>\n <span className={styles.styleIcon}>{item.icon}</span>\n {item.label}\n </span>\n </MenuItem>\n ))}\n </MenuPopover>\n </div>\n </div>\n\n {/* 导入的隐藏文件选择器:accept 由下拉选项写入 */}\n {showImport ? (\n <input\n ref={importInputRef}\n type=\"file\"\n hidden\n onChange={handleImportChange}\n />\n ) : null}\n\n {mathEdit ? (\n <MathEditorPopover\n editor={editor}\n kind={mathEdit.kind}\n mode={mathEdit.mode}\n pos={mathEdit.pos}\n latex={mathEdit.latex}\n isNew={mathEdit.isNew}\n newLabel={\n mathEdit.kind === 'inline' ? t.mathNewInline : t.mathNewBlock\n }\n placeholder={t.mathPlaceholder}\n doneLabel={t.mathDone}\n onConfirm={applyMath}\n onCancel={cancelMath}\n />\n ) : null}\n\n {/* 表格悬停手柄(行左缘 ⋮ / 表格上方 ⋯)+ 它的操作菜单;\n 原来那套「表内右键出菜单」只剩多格选区的批量增删,由这个组件自己接管 */}\n <TableHandles editor={editor} labels={t} />\n {/* 图片对齐工具条(悬停图片时浮出;与表格手柄同一套就地交互) */}\n <ImageAlignTools editor={editor} labels={t} />\n </>\n );\n}\n","import type { Editor } from '@tiptap/core';\nimport { findImagePos } from './imageAlign';\n\n/**\n * 清掉「当前图片」的尺寸(width / height attrs),让它回到标准 `![alt](url)` 语法\n * (对齐若仍在,则回落到 `<img … data-align=\"…\">` 形式)。\n *\n * 拖拽缩放只有入口没有出口:拖过尺寸的图在 markdown 里是 `<img …>`,想在保存形态上\n * 反悔(比如拖歪了、想恢复原图全尺寸)时——undo 之外没有别的路。这个工具补上出口:\n * 宿主可以接自己的按钮 / 菜单(库内暂不渲染 UI)。\n *\n * @returns 是否找到并重置了图片。没找到图(光标不在图旁)时返回 false。\n */\nexport function resetImageSize(editor: Editor): boolean {\n const pos = findImagePos(editor);\n if (pos == null) return false;\n return editor\n .chain()\n .focus()\n .setNodeSelection(pos)\n .updateAttributes('image', { width: null, height: null })\n .run();\n}\n","'use client';\n\nimport * as Popover from '@radix-ui/react-popover';\nimport type { ReactNode } from 'react';\nimport styles from '../styles/comment.module.css';\n\nexport interface CommentPopoverProps {\n open: boolean;\n onOpenChange?: (open: boolean) => void;\n /** 被点击的 mark 元素(from onCommentClick 的 anchorEl)。 */\n anchorEl: HTMLElement | null;\n children: ReactNode;\n side?: 'top' | 'bottom' | 'left' | 'right';\n align?: 'start' | 'center' | 'end';\n sideOffset?: number;\n}\n\n/**\n * 评论浮层(Radix)。库只负责锚定与外观,内容由宿主通过 children 提供\n * (典型:评论正文 + Resolve/Dismiss/Reopen 操作)。\n * 锚点是点击 mark 的动态元素:渲染一个 fixed 1x1 span 挂在 anchorEl 的\n * getBoundingClientRect 位置,Radix 以它为锚。\n */\nexport function CommentPopover({\n open,\n onOpenChange,\n anchorEl,\n children,\n side = 'top',\n align = 'start',\n sideOffset = 6,\n}: CommentPopoverProps) {\n const rect = open && anchorEl ? anchorEl.getBoundingClientRect() : null;\n return (\n <Popover.Root open={open} onOpenChange={onOpenChange}>\n {rect && (\n <Popover.Anchor asChild>\n <span\n className={styles.popoverAnchor}\n style={{ left: rect.left, top: rect.top }}\n />\n </Popover.Anchor>\n )}\n <Popover.Portal>\n <Popover.Content\n className={styles.popoverContent}\n side={side}\n align={align}\n sideOffset={sideOffset}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n >\n {children}\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n","import type { Editor } from '@tiptap/react';\nimport {\n mapCommentAnchors,\n type CommentAnchorStatus,\n type CommentSegment,\n} from './commentAnchor/commentMapper';\n\n/**\n * 内容锚点 → 当前文档里的区间。\n *\n * 用途:宿主在提交一次「圈选改写」时记下选区的内容锚点(`prefix` / `exact` / `suffix`,\n * `blockHash` 可选),等 AI 改完、推送回来时再用它把那段区间找回来——**不能用位置**,\n * 因为编辑器在这期间可能整体重建过(保存、切卡、SWR 刷新都会重挂),位置活不过重建。\n *\n * 锚点类型直接复用评论锚定那套(`commentMapper`),匹配走同一条 fallback 链:\n * `blockHash` → 前后文 → `exact` → 前缀降级(前 20 字)。**这条链是会降级的**,所以:\n *\n * > 调用方必须自己比对返回的 `text` 是否仍等于提交时那一段。本函数只回答「在哪」,\n * > 不回答「还算不算同一段」——后者决定了是原地回填还是弹冲突。\n */\nexport interface AnchorRange {\n /** doc 坐标 */\n from: number;\n to: number;\n /**\n * 区间当前的纯文本。**按匹配器的口径**给出:`commentMapper` 把区间内的 text 节点\n * 直接拼起来(块之间**没有分隔符**),所以这里也是 `textBetween(from, to)`(不传\n * blockSeparator)。调用方拿它跟「建锚点时用的那段文本」比对——两边必须同一口径,\n * 否则多块区间(表格 / 列表)会永远对不上。\n */\n text: string;\n status: CommentAnchorStatus;\n}\n\n/** 探针 id:借 commentMapper 的匹配链路,但我们只要区间,不要评论语义。 */\nconst PROBE_ID = '__anchor-range-probe__';\n\nexport function findRangeByAnchor(\n editor: Editor,\n anchor: CommentSegment | null | undefined,\n): AnchorRange | null {\n if (!anchor || !anchor.exact) return null;\n\n const [result] = mapCommentAnchors(editor.state.doc, [\n { commentId: PROBE_ID, segments: [anchor] },\n ]);\n const range = result?.ranges[0];\n if (!range) return null;\n\n return {\n from: range.from,\n to: range.to,\n // 无 blockSeparator:与 commentMapper 的 `collectText`(text 节点直接拼接)同一口径\n text: editor.state.doc.textBetween(range.from, range.to),\n status: result.status,\n };\n}\n","import '@tiptap/markdown';\nimport type { JSONContent } from '@tiptap/core';\nimport type { Editor } from '@tiptap/react';\n\n/**\n * 取区间对应的 markdown,并给出它在 `editor.getMarkdown()` 输出里的精确偏移。\n *\n * 为什么需要它:后端改卡走的是「在正文里逐字找到这段,替换」(`replace_in_text` +\n * `count=1`),所以发过去的那段必须是**存库正文的逐字子串**;调用方还要知道它在整篇里\n * 的偏移,才能从存库正文取前后文(`ref`)并界定「这次改了哪一段」。官方 OSS 只有整篇\n * `Editor.getMarkdown()`;官方 Pro 的 `getMarkdownRange` 只返回字符串、不给偏移,也不做\n * 块扩展。\n *\n * **默认扩块**(`expandToBlocks: true`):把区间扩展到与它相交的顶层块的首尾。一是序列化\n * 稳定(半句话里跨着加粗,片段序列化会在断口插入多余的 `**`),二是**只有块对齐时偏移才是\n * 算出来的,而不是猜出来的**。\n *\n * 偏移怎么来的:把 doc 的顶层块逐个序列化、用 `\\n\\n` 拼起来,等于整篇序列化(有测试钉住\n * 这条不变量),于是「前 i 块拼接的长度」就是第 i 块的起点偏移。这条不变量若在某篇文档上\n * 不成立,退回按内容在整篇里定位;定位不到或出现多处就返回 `null`——宁可让调用方知道\n * 拿不到偏移,也不要给一个错的。\n */\nexport interface MarkdownRange {\n /** 区间对应的 markdown 片段(扩块后)。 */\n markdown: string;\n /** 片段在 `editor.getMarkdown()` 输出里的起始偏移。 */\n from: number;\n /** 结束偏移(不含)。恒有 `to - from === markdown.length`。 */\n to: number;\n /**\n * **同一段**在 doc 坐标里的范围(扩块之后)。\n *\n * 宿主需要它把「读」和「写」对齐:`from/to` 是 markdown 偏移,而编辑器里的替换要按\n * doc 坐标做。少了它,宿主只能自己拿原始选区去定位,于是「用来定位的那段」和\n * 「按 markdown 偏移算出来的那段」不是同一段——把 A 段写进 B 区间,内容就会重复。\n */\n docFrom: number;\n docTo: number;\n}\n\nexport interface GetMarkdownForRangeOptions {\n /**\n * 是否把区间扩展到与它相交的顶层块(段落 / 表格 / 列表 / 图表 …)的首尾。\n * 默认 `true`:调用方要「存库正文的逐字子串 + 精确偏移」时应该保持默认。\n * 设 `false` 按原区间取,此时偏移依赖内容定位,可能返回 `null`。\n */\n expandToBlocks?: boolean;\n}\n\n/** 顶层块之间的分隔符——与 MarkdownManager 的 doc 级拼接一致。 */\nconst BLOCK_SEPARATOR = '\\n\\n';\n\ninterface TopLevelBlock {\n json: JSONContent;\n /** doc 坐标 */\n from: number;\n to: number;\n}\n\nfunction topLevelBlocks(editor: Editor): TopLevelBlock[] {\n const blocks: TopLevelBlock[] = [];\n editor.state.doc.forEach((node, offset) => {\n blocks.push({\n json: node.toJSON() as JSONContent,\n from: offset,\n to: offset + node.nodeSize,\n });\n });\n return blocks;\n}\n\nexport function getMarkdownForRange(\n editor: Editor,\n from: number,\n to: number,\n options: GetMarkdownForRangeOptions = {},\n): MarkdownRange | null {\n const manager = editor.markdown;\n if (!manager) return null;\n\n const size = editor.state.doc.content.size;\n let start = Math.max(0, Math.min(from, to));\n let end = Math.min(size, Math.max(from, to));\n if (start >= end) return null;\n\n const blocks = topLevelBlocks(editor);\n if (blocks.length === 0) return null;\n\n const expand = options.expandToBlocks !== false;\n\n // 与 [start, end) 相交的顶层块:第一个末端越过 start 的、到最后一个起点早于 end 的。\n // 单元格内、列表项内的选区都会扩到它们所属的顶层块(表格 / 列表整体)。\n let first = 0;\n let last = blocks.length - 1;\n if (expand) {\n first = blocks.findIndex((b) => b.to > start);\n if (first < 0) return null;\n for (let i = blocks.length - 1; i >= first; i -= 1) {\n if (blocks[i].from < end) {\n last = i;\n break;\n }\n }\n start = blocks[first].from;\n end = blocks[last].to;\n }\n\n const parts = blocks.map((b) => manager.serialize({ type: 'doc', content: [b.json] }));\n const full = editor.getMarkdown();\n\n if (expand && parts.join(BLOCK_SEPARATOR) === full) {\n const markdown = parts.slice(first, last + 1).join(BLOCK_SEPARATOR);\n // 空段落拼出来是空串:语义上等于「这段没内容」,交给调用方去判空不如这里就返回 null\n if (!markdown) return null;\n const at =\n first === 0\n ? 0\n : parts.slice(0, first).join(BLOCK_SEPARATOR).length + BLOCK_SEPARATOR.length;\n return { markdown, from: at, to: at + markdown.length, docFrom: start, docTo: end };\n }\n\n // 非块对齐,或整篇拼接与 getMarkdown() 不一致:只能按内容定位,且必须唯一\n const markdown = manager.serialize(\n editor.state.doc.cut(start, end).toJSON() as JSONContent,\n );\n if (!markdown) return null;\n const at = full.indexOf(markdown);\n if (at < 0 || full.indexOf(markdown, at + 1) >= 0) return null;\n return { markdown, from: at, to: at + markdown.length, docFrom: start, docTo: end };\n}\n","import { MarkdownManager } from '@tiptap/markdown';\nimport { CitationRef } from './CitationRef';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\nimport { baseExtensions, pureChart, pureCodeBlock, pureImage } from './extensions';\n\n/**\n * 把一段正文规范成「编辑器读进来再吐出去」的那种写法。\n *\n * **为什么需要它**:编辑器加载 markdown 时要先解析成文档结构,而 markdown 里有些东西是\n * 排版装饰、解析时就丢了(表格列宽的空格、注释 JSON 里的空格、数值尾零 …)。所以\n * 「存库正文」和「编辑器序列化结果」常常**逐字不同**——这与用户改没改无关,加载那一刻就\n * 发生了。而后端改卡走的是「在正文里逐字找到这段再替换」,两边写法不一致就会搜不到。\n * 宿主在要动正文之前(例如圈选改写提交前)先用本函数比对/对齐一次即可。\n *\n * **已知会被规范化的结构**(都是排版层面,内容语义不变):\n *\n * | 结构 | 存库可能是 | 规范化后 |\n * |------|-----------|---------|\n * | GFM 表格 | `\\| 收入 \\| 100 \\|` | 按列内容宽度重排对齐(多/少空格) |\n * | 图表注释 JSON | `<!-- {\"chartType\": \"column\"} -->` | `<!-- {\"chartType\":\"column\"} -->`(去空格,键序按解析结果) |\n * | **图表区**的数值 | `132.0` | `132`(图表把表解析成 payload,数值走 number 再写回) |\n * | 纯空白的正文 | `\" \\n \"` | `\"\"` |\n *\n * 注意第三行**只作用于图表区**:普通 GFM 表格走 marked 的表格解析,单元格是文本,\n * `1,234.50` 这种写法会原样保留(有测试钉住)。\n *\n * 反过来,这些**不该**被改动,同样有测试钉住:标题/段落/加粗斜体/列表/引用、代码块、\n * 公式、图片、脚注 `[^n]`、图表仍是「注释 + 表」的作者形态(不会变成 ```tmr-chart 围栏)。\n *\n * 注意它**不使用** `enrichMarkdownCitations`:脚注的 `[^n]` 由 CitationRef 自带的\n * tokenizer 解析,url/title 只存在于节点属性里、不进 markdown,所以规范化不需要 sources。\n */\nexport function normalizeMarkdown(markdown: string): string {\n if (!markdown) return markdown;\n const manager = getManager();\n return manager.serialize(manager.parse(prepareChartMarkdown(markdown)));\n}\n\n// [perf] 与 renderReportHtml 同样的理由:每次 new MarkdownManager 都会让底层 marked 全局\n// 累积 tokenizer,parse 逐次变慢。这里只做「解析 → 序列化」,无状态,模块级复用即可。\nlet sharedManager: MarkdownManager | null = null;\n\nfunction getManager(): MarkdownManager {\n sharedManager ??= new MarkdownManager({\n extensions: [...baseExtensions, pureCodeBlock, pureImage, pureChart, CitationRef],\n });\n return sharedManager;\n}\n","import '@tiptap/markdown';\nimport type { Editor } from '@tiptap/react';\nimport { prepareChartMarkdown } from './chart/prepareChartMarkdown';\nimport { enrichMarkdownCitations, type SourceRef } from './citationUtils';\nimport { splitTopLevelBlocks, stripToPlainText } from './insertMarkdown';\n\nexport interface ReplaceRangeOptions {\n /** 脚注来源:把 `[^n]` enrich 成带 url/title 的 citation-ref(与 `insertMarkdown` 同一套)。 */\n sources?: SourceRef[];\n}\n\n/**\n * 用一段 markdown **替换**一个区间。给「后台回填」用——AI 在别处改完,把结果写回编辑器。\n *\n * 与 `insertMarkdown` 的三处刻意差异(都在注释里点明,改的时候别抹平):\n *\n * 1. **不抢焦点**。`insertMarkdown` 是「用户主动插入」,链式带了 `.focus()`;后台回填时\n * 用户可能正在别处打字,抢焦点等于打断他。所以这里直接调 `insertContentAt`。\n * 2. **按区间替换**,不是往光标处插(`insertContentAt` 收 `{from,to}` 时后者即替换)。\n * 3. **进 undo 历史**。`insertContentAt` 本身是一个 transaction,撤销一步就回去;\n * 与评论锚定的 `addToHistory: false` 相反——正文改动必须能撤。\n *\n * 解析失败(AI 的脏输出过不了 schema:嵌套 bold、块级图进 listItem …)时按块降级,\n * 与 `insertMarkdown` 同一套策略。注意降级路径是**逐块多次 transaction**,撤销要多按几下;\n * 只在整段失败时才走。\n *\n * @returns 是否改动了文档。空 markdown 返回 `false`(不静默删掉用户的内容)。\n */\nexport function replaceRangeWithMarkdown(\n editor: Editor,\n range: { from: number; to: number },\n markdown: string,\n options: ReplaceRangeOptions = {},\n): boolean {\n const prepared = prepareChartMarkdown(\n enrichMarkdownCitations(markdown ?? '', options.sources ?? []),\n );\n if (!prepared.trim()) return false;\n\n let from = Math.max(0, Math.min(range.from, range.to));\n let to = Math.max(range.from, range.to);\n if (from === to) return false;\n\n // ① 整段替换:一个 transaction,撤销一步到位\n if (tryInsertMarkdownAt(editor, from, to, prepared)) return true;\n\n // ② 降级:逐块替换。块插完要往后挪插入点,否则每块都往同一个区间插、只剩最后一块\n for (const block of splitTopLevelBlocks(prepared)) {\n const before = editor.state.doc.content.size;\n const changed =\n tryInsertMarkdownAt(editor, from, to, block) ||\n insertPlainParagraphAt(editor, from, to, stripToPlainText(block));\n if (!changed) continue;\n // 插入内容的 doc 尺寸 = 文档增量 + 被替换掉的宽度\n const inserted = editor.state.doc.content.size - before + (to - from);\n from += inserted;\n to = from;\n }\n return true;\n}\n\n/** 注意:不带 `.focus()`——后台回填不能抢用户的焦点。 */\nfunction tryInsertMarkdownAt(\n editor: Editor,\n from: number,\n to: number,\n markdown: string,\n): boolean {\n try {\n return editor\n .chain()\n .insertContentAt({ from, to }, markdown, { contentType: 'markdown' })\n .run();\n } catch {\n return false;\n }\n}\n\nfunction insertPlainParagraphAt(\n editor: Editor,\n from: number,\n to: number,\n text: string,\n): boolean {\n try {\n return editor\n .chain()\n .insertContentAt(\n { from, to },\n { type: 'paragraph', content: text ? [{ type: 'text', text }] : [] },\n )\n .run();\n } catch {\n return false;\n }\n}\n","import type { Editor, Range } from '@tiptap/core';\nimport { prepareChartMarkdown } from '../chart/prepareChartMarkdown';\nimport { insertMarkdown } from '../insertMarkdown';\n\n/**\n * 斜杠命令项插入 markdown 的共享原语:同一条 chain 内先删触发词再插内容,\n * 撤销是一步。整段过不了 schema 时降级为「按块重试 + 剥标记当段落」\n * (复用 insertMarkdown 的降级路径)——那条路径里删除已单独成事务,\n * 撤销会是多步,但注册表项是受控 markdown,正常不会走到。\n */\nexport function insertSlashMarkdown(\n editor: Editor,\n range: Range,\n markdown: string,\n): void {\n const prepared = prepareChartMarkdown(markdown);\n if (!prepared) return;\n try {\n const ok = editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent(prepared, { contentType: 'markdown' })\n .run();\n if (ok) return;\n } catch {\n // 整段过不了 schema:走降级\n }\n editor.chain().focus().deleteRange(range).run();\n insertMarkdown(editor, prepared);\n}\n"],"names":["m","offset","r","e","b","c","counter","toggle","styles","Node","dragActive","dragOver","progress","s","clearAll","input","item","isComposing","menu","cell","EmojiSuggestionPopup","_a","text","SlashMenuPopup","header","Fragment","group","MD_FILE_RE","f","setActive","readonly","folded","pre","a","MarkdownWysiwygEditor","Selection","gutter","useIsoLayoutEffect","sameTocItems","HOVER_KEEP_MS","apply","remove","row","node","pos","field","ToolbarButton","bar","markdown","at"],"mappings":"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","x_google_ignoreList":[28,29,30]}