@sofereditor/react 0.9.0 → 0.10.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Editor.tsx","../src/dom-bridge.ts","../src/pastePlan.ts","../src/htmlToSlice.ts","../src/imageConstraints.ts","../src/resolvePastedImages.ts","../src/BehindImageSelectAffordance.tsx","../src/LinkHoverTooltip.tsx","../src/platform.ts","../src/EditorContext.tsx","../src/LinkDialog.tsx","../src/dialogCenterStyle.ts","../src/PageConfigDialog.tsx","../src/ImageCaptionDialog.tsx","../src/FormulaDialog.tsx","../src/formulaSnippet.ts","../src/formulaGuarda.ts","../src/ImageResizeOverlay.tsx","../src/RemoteCursorsOverlay.tsx","../src/NodeView.tsx","../src/renderInline.tsx","../src/sliceDelta.ts","../src/TableResizeOverlay.tsx","../src/usePagination.ts","../src/useEditor.ts","../src/usePageSettings.ts","../src/Toolbar.tsx","../src/TableBorderPanel.tsx"],"sourcesContent":["export { Editor } from \"./Editor\";\nexport type { EditorProps, PageRenderContext, PageRenderProp } from \"./Editor\";\nexport { RemoteCursorsOverlay } from \"./RemoteCursorsOverlay\";\nexport { useEditor } from \"./useEditor\";\nexport type {\n LinkRequest,\n PendingMarks,\n UseEditorOptions,\n UseEditorResult,\n} from \"./useEditor\";\nexport { LinkDialog } from \"./LinkDialog\";\nexport { PageConfigDialog } from \"./PageConfigDialog\";\nexport { ImageCaptionDialog } from \"./ImageCaptionDialog\";\nexport { FormulaDialog } from \"./FormulaDialog\";\nexport { usePageSettings } from \"./usePageSettings\";\nexport type {\n CaptionAlign,\n ImageCaptionRequest,\n ImageCaptionResult,\n PageConfigRequest,\n} from \"./useEditor\";\nexport { NodeView } from \"./NodeView\";\nexport { renderInline } from \"./renderInline\";\nexport { EditorProvider, useEditorContext } from \"./EditorContext\";\nexport type { EditorProviderProps } from \"./EditorContext\";\nexport { Toolbar } from \"./Toolbar\";\nexport type { ToolbarProps } from \"./Toolbar\";\nexport {\n A4_PAGE,\n contentHeight,\n contentWidth,\n defaultPageLayout,\n usePageLayout,\n} from \"./usePagination\";\nexport type {\n BlockFragment,\n PageEntry,\n PageGeometry,\n PageLayout,\n PageLayoutResult,\n PageSlot,\n PaginationPhase,\n TableRowFragment,\n} from \"./usePagination\";\nexport { sliceDelta, deltaLength } from \"./sliceDelta\";\nexport type { NodeViewFragment } from \"./NodeView\";\nexport * from \"./dom-bridge\";\nexport { htmlToSlice } from \"./htmlToSlice\";\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n type KeyboardEvent,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport {\n collapsedSelection,\n deleteBackward,\n deleteForward,\n deleteSelection,\n encodeSelection,\n insertParagraph,\n insertSlice,\n insertText,\n isEmbedAdjacentToCaret,\n isFormulaEmbed,\n serializeSelection,\n sliceToText,\n SOFER_MIME,\n type CommandContext,\n type ListKind,\n type MarkName,\n type SerializedBlock,\n} from \"@sofereditor/core\";\nimport { applyDomSelection, isTableRectSelection, readDomSelection, selectionsEqual } from \"./dom-bridge\";\nimport { planPaste } from \"./pastePlan\";\nimport { resolvePastedImageUploads, sliceHasDataImageEmbeds } from \"./resolvePastedImages\";\nimport { BehindImageSelectAffordance } from \"./BehindImageSelectAffordance\";\nimport { LinkHoverTooltip } from \"./LinkHoverTooltip\";\nimport { EditorProvider } from \"./EditorContext\";\nimport { ImageResizeOverlay } from \"./ImageResizeOverlay\";\nimport {\n RemoteCursorsOverlay,\n type AwarenessLike,\n} from \"./RemoteCursorsOverlay\";\nimport { NodeView, type NodeViewFragment } from \"./NodeView\";\nimport {\n A4_PAGE,\n defaultPageLayout,\n usePageLayout,\n type PageGeometry,\n type PageLayout,\n type PageSlot,\n} from \"./usePagination\";\nimport { useEditor, type UseEditorResult } from \"./useEditor\";\nimport { usePageSettings } from \"./usePageSettings\";\n\nexport interface PageRenderContext {\n pageNumber: number;\n pageCount: number;\n}\n\nexport type PageRenderProp = (ctx: PageRenderContext) => ReactNode;\n\nexport interface EditorProps {\n editor?: UseEditorResult;\n className?: string;\n /**\n * Page geometry override. When **omitted**, the editor reads page settings\n * from `editor.doc` (the `docSettings` Y.Map populated via `setPageSettings`\n * or `import-docx`). When **set**, this value wins for rendering — but is\n * NOT written back to the Y.Doc (view-only override; useful when the host\n * app must pin a size regardless of doc state, e.g. a prova generator).\n * Pass **null** to disable pagination entirely (flat flow).\n */\n pageGeometry?: PageGeometry | null;\n /**\n * Custom page header. Receives `{ pageNumber, pageCount }` and renders inside\n * the top margin of every page (above the content area). Pass `null` to omit.\n */\n renderPageHeader?: PageRenderProp | null;\n /**\n * Custom page footer. Receives `{ pageNumber, pageCount }`. Defaults to\n * `\"<n> / <total>\"` centered. Pass `null` to omit.\n */\n renderPageFooter?: PageRenderProp | null;\n /**\n * Awareness do Yjs (de `useCollab().binding.awareness`). Quando presente,\n * o editor publica a seleção local e desenha os cursores/seleções dos outros\n * usuários (presença colaborativa). Omitir = sem cursores remotos.\n */\n awareness?: AwarenessLike;\n}\n\nconst defaultFooter: PageRenderProp = ({ pageNumber, pageCount }) =>\n `${pageNumber} / ${pageCount}`;\n\nexport function Editor({\n editor: providedEditor,\n className,\n pageGeometry,\n renderPageHeader,\n renderPageFooter = defaultFooter,\n awareness,\n}: EditorProps): JSX.Element {\n const internal = useEditor();\n const editor = providedEditor ?? internal;\n const { doc, history, snapshot, getSelection, setSelection } = editor;\n\n const rootRef = useRef<HTMLDivElement>(null);\n const isComposingRef = useRef(false);\n // B4: cobre a colagem de HTML com imagem `data:` pendente de upload (ver\n // `onPaste`, ramo \"html\"). `true` do instante em que o upload começa até a\n // colagem terminar de ser inserida. Sem isso, uma segunda colagem disparada\n // DURANTE a janela do upload (comum: rede escolar lenta + \"não aconteceu\n // nada visualmente\" faz o professor colar de novo) capturaria a MESMA\n // posição que a primeira, e ao resolver empurraria a primeira colagem —\n // sem nenhum indicador de carregamento pra explicar por que. Descarta a\n // segunda em vez de enfileirar: mais simples, e uma colagem perdida com\n // aviso no console é melhor que duas colagens se atropelando na mesma\n // posição do documento.\n const pasteInFlightRef = useRef(false);\n // The `behind` image the pointer is currently hovering (by geometry), so we\n // can show a click affordance to select it through the text on top of it.\n const [hoveredBehind, setHoveredBehind] = useState<{\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n } | null>(null);\n const hoveredBehindRef = useRef<typeof hoveredBehind>(null);\n // The link the pointer is hovering — drives the URL tooltip (rect in viewport\n // coords). Tracked by element identity to avoid re-render churn on every move.\n const [hoveredLink, setHoveredLink] = useState<{\n href: string;\n rect: { top: number; left: number; bottom: number; width: number };\n } | null>(null);\n const hoveredLinkElRef = useRef<HTMLAnchorElement | null>(null);\n const suppressSelectionSyncRef = useRef(false);\n\n // Presença colaborativa: publica a seleção local no awareness (throttle por\n // rAF). Outros editores desenham nosso caret via RemoteCursorsOverlay.\n useEffect(() => {\n if (!awareness) return;\n const root = rootRef.current;\n if (!root) return;\n let raf: number | null = null;\n const publish = () => {\n raf = null;\n const sel = readDomSelection(root);\n awareness.setLocalStateField(\n \"cursor\",\n sel ? encodeSelection(doc, sel) ?? null : null,\n );\n };\n const onSelChange = () => {\n const s = document.getSelection();\n if (!s || s.rangeCount === 0) return;\n // Só publica quando a seleção está dentro do nosso editor.\n if (s.anchorNode && !root.contains(s.anchorNode)) return;\n if (raf != null) return;\n raf = requestAnimationFrame(publish);\n };\n const onBlur = () => awareness.setLocalStateField(\"cursor\", null);\n document.addEventListener(\"selectionchange\", onSelChange);\n root.addEventListener(\"blur\", onBlur, true);\n return () => {\n document.removeEventListener(\"selectionchange\", onSelChange);\n root.removeEventListener(\"blur\", onBlur, true);\n if (raf != null) cancelAnimationFrame(raf);\n awareness.setLocalStateField(\"cursor\", null);\n };\n }, [awareness, doc]);\n\n const ctxRef = useRef<CommandContext>({ doc, getSelection, setSelection });\n ctxRef.current = { doc, getSelection, setSelection };\n const historyRef = useRef(history);\n historyRef.current = history;\n const editorRef = useRef(editor);\n editorRef.current = editor;\n\n // Run after every render — not just when selection or snapshot change — so\n // we re-apply the caret after pagination's two-phase render cycle (which\n // mutates `layout` state without touching `editor.selection` or `snapshot`,\n // and tears down/rebuilds the text nodes the prior DOM selection was attached\n // to). The early-return when DOM already matches the model keeps this cheap.\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n if (isComposingRef.current) return;\n if (document.activeElement !== root) return;\n const current = readDomSelection(root);\n if (current && selectionsEqual(current, editor.selection)) return;\n if (isTableRectSelection(editor.selection)) {\n // Cross-cell (rectangular) table selection: the native DOM selection is\n // intentionally empty — TableView paints the highlight via CSS classes.\n // Keep it empty but do NOT arm the suppress guard. The guard exists to\n // ignore render-collapsed carets while TYPING; for a rect selection the\n // empty native selection is the steady state, so arming it would make the\n // selectionchange handler ignore a genuine click-away — the rect would\n // stick in the model and the next keystroke would overwrite the selected\n // cells instead of writing at the clicked caret.\n applyDomSelection(root, editor.selection);\n suppressSelectionSyncRef.current = false;\n return;\n }\n // DOM caret diverged from the model — typically a re-render replaced the\n // text node and collapsed the native caret to offset 0. Suppress the\n // selectionchange handler until the DOM settles back to the model; otherwise\n // a render-induced selectionchange writes the collapsed caret over a newer\n // keystroke's selection and the caret jumps backward when typing fast.\n // Cleared deterministically in the selectionchange handler once the DOM\n // settles to the live model — NOT on a timer, because timers race the event.\n suppressSelectionSyncRef.current = true;\n applyDomSelection(root, editor.selection);\n });\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const handler = () => {\n if (isComposingRef.current) return;\n if (document.activeElement !== root) {\n // Focus left the editor — nothing to capture here. Release any pending\n // suppression so a stuck flag can't swallow the next genuine change.\n suppressSelectionSyncRef.current = false;\n return;\n }\n const modelSel = readDomSelection(root);\n if (suppressSelectionSyncRef.current) {\n // Mid-reconciliation: a render moved the caret. Resume capturing user\n // selection only once the DOM caret has settled to match the LIVE model\n // (our applyDomSelection landed). Transient render-collapsed carets\n // (DOM != model) are ignored — those are the spurious events that used\n // to clobber the model selection and make the caret jump back.\n if (modelSel && selectionsEqual(modelSel, editorRef.current.getSelection())) {\n suppressSelectionSyncRef.current = false;\n }\n return;\n }\n if (modelSel) setSelection(modelSel);\n };\n document.addEventListener(\"selectionchange\", handler);\n return () => document.removeEventListener(\"selectionchange\", handler);\n }, [setSelection]);\n\n // ---------- Sub-phase 4.5: rectangular table-cell drag ----------\n //\n // mousedown captures the anchor cell. mousemove flips into \"rect mode\" the\n // moment the pointer enters a DIFFERENT cell of the same table, at which\n // point we suppress the native cross-cell selection and write a rectangular\n // selection to the model. The visual highlight is drawn by TableView via\n // CSS classes (see isTableRectSelection in dom-bridge).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n interface DragState {\n blockIndex: number;\n anchorCellIndex: number;\n anchorTableEl: HTMLTableElement;\n rectActive: boolean;\n }\n let drag: DragState | null = null;\n\n function cellFromTarget(target: EventTarget | null): {\n blockIndex: number;\n cellIndex: number;\n tableEl: HTMLTableElement;\n } | null {\n if (!(target instanceof Node)) return null;\n let n: Node | null = target;\n let cellEl: HTMLElement | null = null;\n let tableEl: HTMLTableElement | null = null;\n while (n && n !== root) {\n if (n.nodeType === Node.ELEMENT_NODE) {\n const e = n as HTMLElement;\n if (!cellEl && e.dataset.cellIndex != null) cellEl = e;\n if (e.tagName === \"TABLE\" && e.dataset.blockIndex != null) {\n tableEl = e as HTMLTableElement;\n break;\n }\n }\n n = n.parentNode;\n }\n if (!cellEl || !tableEl) return null;\n const cellIndex = Number.parseInt(cellEl.dataset.cellIndex!, 10);\n const blockIndex = Number.parseInt(tableEl.dataset.blockIndex!, 10);\n return { blockIndex, cellIndex, tableEl };\n }\n\n const onPointerDown = (e: PointerEvent) => {\n if (e.button !== 0) return;\n const hit = cellFromTarget(e.target);\n if (!hit) {\n drag = null;\n return;\n }\n drag = {\n blockIndex: hit.blockIndex,\n anchorCellIndex: hit.cellIndex,\n anchorTableEl: hit.tableEl,\n rectActive: false,\n };\n };\n\n const onPointerMove = (e: PointerEvent) => {\n if (!drag) return;\n // Only treat as drag when the primary button is still pressed.\n if ((e.buttons & 1) === 0) {\n drag = null;\n return;\n }\n const hit = cellFromTarget(e.target);\n if (!hit) return;\n if (hit.blockIndex !== drag.blockIndex) return; // never cross tables\n if (hit.cellIndex === drag.anchorCellIndex && !drag.rectActive) return;\n\n if (!drag.rectActive) {\n drag.rectActive = true;\n // Kill any in-progress native cross-cell selection.\n window.getSelection()?.removeAllRanges();\n }\n e.preventDefault();\n ctxRef.current.setSelection({\n anchor: { blockIndex: drag.blockIndex, cellIndex: drag.anchorCellIndex, offset: 0 },\n focus: { blockIndex: drag.blockIndex, cellIndex: hit.cellIndex, offset: 0 },\n });\n };\n\n const onPointerUp = () => {\n drag = null;\n };\n\n root.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"pointermove\", onPointerMove);\n document.addEventListener(\"pointerup\", onPointerUp);\n return () => {\n root.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"pointermove\", onPointerMove);\n document.removeEventListener(\"pointerup\", onPointerUp);\n };\n }, []);\n\n // ---------- Fase 5: drag-drop de imagens ----------\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onDragOver = (e: DragEvent) => {\n if (!e.dataTransfer) return;\n const hasFiles = Array.from(e.dataTransfer.items ?? []).some(\n (i) => i.kind === \"file\",\n );\n if (!hasFiles) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"copy\";\n };\n const onDrop = (e: DragEvent) => {\n if (!e.dataTransfer) return;\n const images = Array.from(e.dataTransfer.files).filter((f) =>\n f.type.startsWith(\"image/\"),\n );\n if (images.length === 0) return;\n e.preventDefault();\n // Position the caret at the drop point so the insertion lands there.\n const range = caretRangeFromPoint(e.clientX, e.clientY);\n if (range) {\n const dom = window.getSelection();\n dom?.removeAllRanges();\n dom?.addRange(range);\n const sel = readDomSelection(root);\n if (sel) ctxRef.current.setSelection(sel);\n }\n void (async () => {\n for (const f of images) await editorRef.current.insertImageFromFile(f);\n })();\n };\n root.addEventListener(\"dragover\", onDragOver);\n root.addEventListener(\"drop\", onDrop);\n return () => {\n root.removeEventListener(\"dragover\", onDragOver);\n root.removeEventListener(\"drop\", onDrop);\n };\n }, []);\n\n // ---------- Fase 5: clique numa imagem seleciona o embed ----------\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onPointerDown = (e: PointerEvent) => {\n if (e.button !== 0) return;\n const target = e.target;\n if (!(target instanceof Element)) return;\n const img = target.closest('img[data-embed=\"image\"]') as HTMLElement | null;\n if (!img) return;\n const blockEl = img.closest(\"[data-block-index]\") as HTMLElement | null;\n if (!blockEl) return;\n const blockIndex = Number.parseInt(blockEl.dataset.blockIndex!, 10);\n const offsetAttr = img.dataset.embedOffset;\n if (offsetAttr == null) return;\n const localOffset = Number.parseInt(offsetAttr, 10);\n const fragmentStart = Number.parseInt(blockEl.dataset.fragmentStart ?? \"0\", 10) || 0;\n const cellEl = img.closest(\"[data-cell-index]\") as HTMLElement | null;\n const cellIndex = cellEl\n ? Number.parseInt(cellEl.dataset.cellIndex!, 10)\n : undefined;\n const offset = fragmentStart + localOffset;\n e.preventDefault();\n ctxRef.current.setSelection({\n anchor: { blockIndex, cellIndex, offset },\n focus: { blockIndex, cellIndex, offset: offset + 1 },\n });\n };\n root.addEventListener(\"pointerdown\", onPointerDown);\n return () => root.removeEventListener(\"pointerdown\", onPointerDown);\n }, []);\n\n // Duplo clique numa fórmula reabre o modal com o LaTeX guardado. Em imagem\n // comum não faz nada — `isFormulaEmbed` é o que separa os dois.\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onDoubleClick = (e: MouseEvent) => {\n const target = e.target;\n if (!(target instanceof Element)) return;\n if (!target.closest('img[data-embed=\"image\"]')) return;\n const ed = editorRef.current;\n const sel = ed.getSelectedEmbed();\n if (!sel || !isFormulaEmbed(sel.embed)) return;\n e.preventDefault();\n const { latex, display } = sel.embed.formula;\n // A restauração da seleção do embed através do round-trip do modal\n // (bug #6) mora dentro de `insertFormula` (`useEditor.ts`) — ela é\n // quem tem o `await`, então é a única que consegue segurar a seleção\n // através do gap assíncrono. Fazer isso aqui de novo seria duplicação\n // sem efeito (e enganosa: pareceria proteger o gap que só o hook vê).\n void ed.requestFormula(latex, display).then((r) => {\n if (!r) return;\n void ed.insertFormula(r.latex, r.display);\n });\n };\n root.addEventListener(\"dblclick\", onDoubleClick);\n return () => root.removeEventListener(\"dblclick\", onDoubleClick);\n }, []);\n\n // Hover detection for `behind` images: they sit at z-index:-1, so the text on\n // top intercepts clicks and the image can't be selected by clicking there. On\n // pointer move we find — BY GEOMETRY — the behind image under the pointer and\n // expose a select affordance (rendered below). Throttled to one check/frame.\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const same = (\n a: typeof hoveredBehind,\n b: typeof hoveredBehind,\n ): boolean =>\n a === b ||\n (!!a &&\n !!b &&\n a.blockIndex === b.blockIndex &&\n a.offset === b.offset &&\n (a.cellIndex ?? -1) === (b.cellIndex ?? -1));\n const set = (next: typeof hoveredBehind) => {\n if (same(next, hoveredBehindRef.current)) return;\n hoveredBehindRef.current = next;\n setHoveredBehind(next);\n };\n let raf = 0;\n const onMove = (e: PointerEvent) => {\n if (raf) return;\n const x = e.clientX;\n const y = e.clientY;\n raf = requestAnimationFrame(() => {\n raf = 0;\n const imgs = root.querySelectorAll<HTMLImageElement>(\n 'img[data-embed=\"image\"][data-embed-layout=\"behind\"]',\n );\n let hit: HTMLImageElement | null = null;\n for (const img of imgs) {\n const r = img.getBoundingClientRect();\n if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) {\n hit = img;\n break;\n }\n }\n if (!hit) {\n set(null);\n return;\n }\n const blockEl = hit.closest<HTMLElement>(\"[data-block-index]\");\n const offsetAttr = hit.dataset.embedOffset;\n if (!blockEl || offsetAttr == null) {\n set(null);\n return;\n }\n const blockIndex = Number.parseInt(blockEl.dataset.blockIndex!, 10);\n const fragmentStart = Number.parseInt(blockEl.dataset.fragmentStart ?? \"0\", 10) || 0;\n const cellEl = hit.closest<HTMLElement>(\"[data-cell-index]\");\n const cellIndex = cellEl\n ? Number.parseInt(cellEl.dataset.cellIndex!, 10)\n : undefined;\n set({ blockIndex, offset: fragmentStart + Number.parseInt(offsetAttr, 10), cellIndex });\n });\n };\n const onLeave = () => set(null);\n root.addEventListener(\"pointermove\", onMove);\n root.addEventListener(\"pointerleave\", onLeave);\n return () => {\n root.removeEventListener(\"pointermove\", onMove);\n root.removeEventListener(\"pointerleave\", onLeave);\n if (raf) cancelAnimationFrame(raf);\n };\n }, []);\n\n // Link hover tooltip: show the URL while the pointer is over a link. Uses\n // `mouseover` delegation (fires on element enter, far cheaper than mousemove)\n // and tracks the hovered <a> by identity to avoid re-render churn. Cleared on\n // leave/scroll (the cached viewport rect would otherwise go stale).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const clear = () => {\n if (hoveredLinkElRef.current) {\n hoveredLinkElRef.current = null;\n setHoveredLink(null);\n }\n };\n const onOver = (e: MouseEvent) => {\n const target = e.target as Element | null;\n const a = target?.closest?.(\"a[href]\") as HTMLAnchorElement | null;\n if (a && root.contains(a)) {\n if (hoveredLinkElRef.current === a) return;\n hoveredLinkElRef.current = a;\n const r = a.getBoundingClientRect();\n setHoveredLink({\n href: a.getAttribute(\"href\") ?? \"\",\n rect: { top: r.top, left: r.left, bottom: r.bottom, width: r.width },\n });\n } else {\n clear();\n }\n };\n root.addEventListener(\"mouseover\", onOver);\n root.addEventListener(\"mouseleave\", clear);\n root.addEventListener(\"scroll\", clear, true);\n window.addEventListener(\"scroll\", clear, true);\n return () => {\n root.removeEventListener(\"mouseover\", onOver);\n root.removeEventListener(\"mouseleave\", clear);\n root.removeEventListener(\"scroll\", clear, true);\n window.removeEventListener(\"scroll\", clear, true);\n };\n }, []);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n const handler = (e: InputEvent) => {\n const ctx = ctxRef.current;\n const hist = historyRef.current;\n const ed = editorRef.current;\n const type = e.inputType;\n\n if (isComposingRef.current && type === \"insertCompositionText\") return;\n\n switch (type) {\n case \"insertText\": {\n const data = e.data ?? \"\";\n if (data.length > 0) {\n e.preventDefault();\n insertText(ctx, data, ed.consumePendingMarks());\n }\n return;\n }\n case \"insertLineBreak\": {\n // Shift+Enter inside a listItem inserts a soft line break (`\\n`)\n // INSIDE the item instead of splitting it into a new numbered entry\n // — useful for spacing between question lines without bumping the\n // numbering. Other blocks fall through to the regular paragraph\n // split (= same as Enter), because TipTap-style \"soft break\" inside\n // a paragraph isn't part of this model.\n e.preventDefault();\n if (ed.getBlockType() === \"listItem\") {\n insertText(ctx, \"\\n\", ed.consumePendingMarks());\n return;\n }\n // Fall through to the same path as insertParagraph.\n const selEmbed = ed.getSelectedEmbed();\n if (selEmbed) {\n const off = selEmbed.offset + 1;\n ctx.setSelection({\n anchor: {\n blockIndex: selEmbed.blockIndex,\n cellIndex: selEmbed.cellIndex,\n offset: off,\n },\n focus: {\n blockIndex: selEmbed.blockIndex,\n cellIndex: selEmbed.cellIndex,\n offset: off,\n },\n });\n }\n if (ed.splitListItem()) return;\n insertParagraph(ctx);\n return;\n }\n case \"insertParagraph\": {\n e.preventDefault();\n // If an embed is the entire selection, drop the caret RIGHT AFTER it\n // before splitting — otherwise the default \"delete range then insert\n // paragraph\" path would remove the image.\n const selectedEmbed = ed.getSelectedEmbed();\n if (selectedEmbed) {\n const off = selectedEmbed.offset + 1;\n ctx.setSelection({\n anchor: {\n blockIndex: selectedEmbed.blockIndex,\n cellIndex: selectedEmbed.cellIndex,\n offset: off,\n },\n focus: {\n blockIndex: selectedEmbed.blockIndex,\n cellIndex: selectedEmbed.cellIndex,\n offset: off,\n },\n });\n }\n if (ed.splitListItem()) return;\n insertParagraph(ctx);\n return;\n }\n case \"insertReplacementText\": {\n const text = e.data ?? e.dataTransfer?.getData(\"text/plain\") ?? \"\";\n if (text.length > 0) {\n e.preventDefault();\n insertText(ctx, text, ed.consumePendingMarks());\n }\n return;\n }\n case \"insertFromPaste\": {\n // Paste é tratado pelo handler `paste` (useEffect de clipboard). Quando\n // ele NÃO faz preventDefault (ex.: clipboard só com text/html, ou sem\n // clipboardData), o browser cai aqui — este no-op bloqueia a inserção\n // crua no DOM em vez de corromper o documento virtual.\n e.preventDefault();\n return;\n }\n case \"deleteContentBackward\":\n case \"deleteWordBackward\":\n case \"deleteSoftLineBackward\":\n case \"deleteHardLineBackward\": {\n e.preventDefault();\n deleteBackward(ctx);\n return;\n }\n case \"deleteContentForward\":\n case \"deleteWordForward\":\n case \"deleteSoftLineForward\":\n case \"deleteHardLineForward\": {\n e.preventDefault();\n deleteForward(ctx);\n return;\n }\n case \"historyUndo\": {\n e.preventDefault();\n hist.undo();\n return;\n }\n case \"historyRedo\": {\n e.preventDefault();\n hist.redo();\n return;\n }\n default: {\n e.preventDefault();\n return;\n }\n }\n };\n\n root.addEventListener(\"beforeinput\", handler);\n return () => root.removeEventListener(\"beforeinput\", handler);\n }, []);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n const writeSlice = (e: ClipboardEvent): boolean => {\n const ctx = ctxRef.current;\n const slice = serializeSelection(ctx.doc, ctx.getSelection());\n if (!slice || !e.clipboardData) return false;\n e.clipboardData.setData(SOFER_MIME, JSON.stringify(slice));\n e.clipboardData.setData(\"text/plain\", sliceToText(slice));\n return true;\n };\n\n const onCopy = (e: ClipboardEvent) => {\n if (writeSlice(e)) e.preventDefault();\n };\n\n const onCut = (e: ClipboardEvent) => {\n // Always block the native cut: it mutates the contenteditable DOM directly\n // and would write its own clipboard serialization. When we have a slice we\n // delete via the model; an unsupported selection (writeSlice false) becomes\n // a clean no-op instead of a \"cut that doesn't delete\".\n e.preventDefault();\n if (writeSlice(e)) deleteSelection(ctxRef.current);\n };\n\n const onPaste = (e: ClipboardEvent) => {\n const ctx = ctxRef.current;\n const cd = e.clipboardData;\n if (!cd) return;\n // A escolha do ramo (e sobretudo a ORDEM deles) vive em `planPaste`, para\n // ser testável sem montar o editor. Ver o comentário lá sobre por que o\n // HTML precisa vir antes dos arquivos de imagem.\n const plan = planPaste(cd);\n if (plan.kind === \"none\") return;\n e.preventDefault();\n switch (plan.kind) {\n case \"sofer\":\n insertSlice(ctx, plan.slice);\n return;\n case \"html\": {\n if (!sliceHasDataImageEmbeds(plan.slice)) {\n insertSlice(ctx, plan.slice);\n return;\n }\n // B4: upload de imagem já em andamento. Sem NENHUM indicador de\n // carregamento na UI, \"não aconteceu nada visualmente\" (rede\n // escolar lenta) é exatamente o gatilho que leva o professor a\n // colar de novo — e a segunda colagem capturaria a MESMA posição\n // abaixo, empurrando a primeira ao resolver. Descarta com aviso em\n // vez de enfileirar: enfileirar só adiaria a pergunta de cima de\n // QUAL posição (já potencialmente obsoleta) encadear a próxima.\n if (pasteInFlightRef.current) {\n console.warn(\n \"[sofereditor] colagem com imagem ainda subindo — descartando esta colagem concorrente para não corromper a posição de inserção\",\n );\n return;\n }\n pasteInFlightRef.current = true;\n // Slice tem embed(s) `data:` (Google Docs) — sobe pro storage\n // configurado (`uploadImage`) ANTES de inserir, pra não gravar\n // ~300KB de base64 direto no Y.Doc. `htmlToSlice` continua pura: só\n // emite o `data:`, a subida é feita aqui (ver `resolvePastedImages.ts`).\n const capturedSelection = ctx.getSelection();\n void (async () => {\n try {\n const resolved = await resolvePastedImageUploads(plan.slice, editorRef.current.uploadImage);\n // A subida é assíncrona — o mundo pode ter mudado nesse meio\n // tempo. O editor é colaborativo: quem deslocou tudo pode ter\n // sido o próprio professor OU um par remoto via Hocuspocus\n // editando acima, não só digitação local. Dois riscos (B5),\n // tratados separadamente:\n //\n // (a) o bloco capturado pode ter sumido (Ctrl+A+Backspace local,\n // ou uma edição remota que apagou/moveu blocos). `insertSlice`\n // faz um `return` silencioso quando o bloco alvo não existe mais\n // — a colagem desapareceria sem erro, sem log, e o `data:`\n // original já foi descartado junto. Em vez de deixar sumir\n // calado: loga e insere no fim do documento como último\n // recurso.\n //\n // (b) restaurar `capturedSelection` INCONDICIONALMENTE depois de\n // inserir é o dano em si: se o professor se moveu enquanto\n // esperava (clicou em outro lugar, ou está digitando — local ou\n // remotamente), arrancar o caret de volta pro ponto da colagem\n // no meio da digitação dele é pior que o problema original. Por\n // isso a seleção viva é lida ANTES de mexer em qualquer coisa, e\n // só é restaurada quando de fato mudou da capturada; do\n // contrário `insertSlice` já deixa o caret logo depois do\n // conteúdo colado — o comportamento esperado do caso comum (o\n // professor esperou o upload terminar sem se mover).\n //\n // Limitação conhecida (mesma classe da anterior): a seleção\n // restaurada é uma posição ABSOLUTA, não uma `Y.RelativePosition`\n // — se a inserção deslocou blocos ANTES da posição do usuário, o\n // índice restaurado pode ficar levemente errado. Migrar pra\n // RelativePosition fecharia isso; fora de escopo aqui.\n const liveSelection = ctxRef.current.getSelection();\n const userMovedAway = !selectionsEqual(liveSelection, capturedSelection);\n\n const { blockIndex, cellIndex } = capturedSelection.focus;\n const targetText = ctxRef.current.doc.textAt(blockIndex, cellIndex);\n let insertAt = capturedSelection;\n if (!targetText) {\n console.error(\n \"[sofereditor] bloco de destino da colagem sumiu durante o upload da imagem — inserindo no fim do documento em vez de descartar a colagem\",\n );\n const lastIndex = Math.max(0, ctxRef.current.doc.blockCount() - 1);\n const lastText = ctxRef.current.doc.getBlockText(lastIndex);\n insertAt = collapsedSelection({ blockIndex: lastIndex, offset: lastText ? lastText.length : 0 });\n }\n\n ctxRef.current.setSelection(insertAt);\n insertSlice(ctxRef.current, resolved);\n if (userMovedAway) {\n ctxRef.current.setSelection(liveSelection);\n }\n } finally {\n pasteInFlightRef.current = false;\n }\n })();\n return;\n }\n case \"images\":\n void (async () => {\n for (const f of plan.files) await editorRef.current.insertImageFromFile(f);\n })();\n return;\n case \"text\": {\n const lines = plan.text.split(/\\r\\n|\\r|\\n/);\n lines.forEach((line, i) => {\n if (i > 0) insertParagraph(ctx);\n if (line.length > 0) insertText(ctx, line);\n });\n return;\n }\n }\n };\n\n root.addEventListener(\"copy\", onCopy);\n root.addEventListener(\"cut\", onCut);\n root.addEventListener(\"paste\", onPaste);\n return () => {\n root.removeEventListener(\"copy\", onCopy);\n root.removeEventListener(\"cut\", onCut);\n root.removeEventListener(\"paste\", onPaste);\n };\n }, []);\n\n const onKeyDown = useCallback((e: KeyboardEvent<HTMLDivElement>) => {\n const ed = editorRef.current;\n const mod = e.metaKey || e.ctrlKey;\n\n if (e.key === \"Tab\") {\n e.preventDefault();\n // Inside a table, Tab/Shift+Tab navigates between cells; at the last\n // cell Tab appends a new row.\n if (ed.isInTable()) {\n if (e.shiftKey) ed.moveToPrevCell();\n else ed.moveToNextCell();\n return;\n }\n if (e.shiftKey) ed.dedentList();\n else ed.indentList();\n return;\n }\n\n // Home/End: move the caret to the visual start/end of the current line.\n // The default contenteditable behavior is inconsistent with paginated\n // fragments — Chrome occasionally jumps to the start/end of the block\n // element instead of the visual line. We explicitly use the native\n // `Selection.modify(\"lineboundary\")` API (Chrome/Edge/Safari) which\n // respects visual line wrapping inside any contenteditable.\n //\n // `Cmd/Ctrl+Home/End` keep the default browser behavior (document scroll).\n if (!mod && (e.key === \"Home\" || e.key === \"End\")) {\n const root = rootRef.current;\n const sel = window.getSelection();\n const sm = (sel as unknown as { modify?: (alter: string, direction: string, granularity: string) => void })?.modify;\n if (root && sel && sel.rangeCount > 0 && typeof sm === \"function\") {\n e.preventDefault();\n const alter = e.shiftKey ? \"extend\" : \"move\";\n const direction = e.key === \"Home\" ? \"left\" : \"right\";\n sm.call(sel, alter, direction, \"lineboundary\");\n const modelSel = readDomSelection(root);\n if (modelSel) ctxRef.current.setSelection(modelSel);\n }\n return;\n }\n\n // Backspace/Delete quando um embed (imagem) está selecionado ou\n // adjacente ao caret colapsado. O `<figure>` do embed é\n // `contenteditable=false`; quando a seleção do DOM pousa nele — o que\n // acontece depois de inserir ou clicar numa imagem — o navegador NUNCA\n // dispara `beforeinput`, então o caminho normal de delete (tratado em\n // `beforeinput`, no outro useEffect) fica mudo pra esse caso. Decidido\n // pelo MODELO (`getSelectedEmbed`/`isEmbedAdjacentToCaret`), não pela\n // seleção do DOM — determinístico e não depende de onde cada navegador\n // decide pousar o caret ao redor de um elemento não-editável.\n //\n // Não intercepta o caso normal (cursor em texto, sem embed adjacente):\n // aí as duas funções devolvem false/null e o handler cai no `return`\n // sem `preventDefault`, deixando `beforeinput` cuidar disso como sempre\n // (ver o teste \"não intercepta\" em `Editor.keydown.test.ts`).\n if (!mod && (e.key === \"Backspace\" || e.key === \"Delete\")) {\n // Composição ativa (ex.: acento morto `´` no layout ABC-Extended dispara\n // `compositionstart`) — o modelo ainda não recebeu o texto em\n // composição, só o DOM sabe. Decidir por `getSelectedEmbed`/\n // `isEmbedAdjacentToCaret` aqui enxergaria o embed adjacente do MODELO\n // (que não avançou) e apagaria a imagem em vez de deixar o Backspace\n // corrigir a composição — e ainda travaria a composição no meio, porque\n // `preventDefault` interrompe o IME. Mesma guarda que as linhas 173,\n // 205 e 520 já usam pros outros caminhos de input.\n if (isComposingRef.current) return;\n const direction = e.key === \"Backspace\" ? \"backward\" : \"forward\";\n const shouldIntercept =\n ed.getSelectedEmbed() != null || isEmbedAdjacentToCaret(ctxRef.current, direction);\n if (shouldIntercept) {\n e.preventDefault();\n if (direction === \"backward\") deleteBackward(ctxRef.current);\n else deleteForward(ctxRef.current);\n }\n return;\n }\n\n if (!mod) return;\n const key = e.key.toLowerCase();\n\n if (!e.shiftKey && !e.altKey && key === \"z\") {\n e.preventDefault();\n historyRef.current.undo();\n return;\n }\n if (!e.altKey && (key === \"y\" || (e.shiftKey && key === \"z\"))) {\n e.preventDefault();\n historyRef.current.redo();\n return;\n }\n\n if (e.altKey && !e.shiftKey && /^[0-6]$/.test(key)) {\n e.preventDefault();\n const n = Number(key);\n if (n === 0) ed.setBlockType(\"paragraph\");\n else ed.setBlockType(\"heading\", { level: n as 1 | 2 | 3 | 4 | 5 | 6 });\n return;\n }\n\n if (e.shiftKey && !e.altKey && (key === \"7\" || key === \"8\")) {\n e.preventDefault();\n ed.toggleList(key === \"7\" ? \"ordered\" : \"bullet\");\n return;\n }\n\n const markToggle = !e.shiftKey && !e.altKey\n ? (BOOLEAN_SHORTCUTS[key] as MarkName | undefined)\n : undefined;\n if (markToggle) {\n e.preventDefault();\n ed.toggleMark(markToggle);\n return;\n }\n if (e.shiftKey && !e.altKey && key === \"x\") {\n e.preventDefault();\n ed.toggleMark(\"strike\");\n return;\n }\n\n if (!e.altKey && !e.shiftKey && key === \"k\") {\n e.preventDefault();\n const active = ed.getActiveMarks();\n const currentHref =\n active.link && typeof active.link === \"object\" && \"href\" in active.link\n ? (active.link.href as string)\n : \"\";\n void ed.requestLink(currentHref).then((href) => {\n if (href === null) return;\n if (href === \"\") ed.removeMark(\"link\");\n else ed.setMark(\"link\", { href });\n });\n }\n }, []);\n\n const onCompositionStart = useCallback(() => {\n isComposingRef.current = true;\n }, []);\n\n const onCompositionEnd = useCallback((e: React.CompositionEvent<HTMLDivElement>) => {\n isComposingRef.current = false;\n if (e.data && e.data.length > 0) {\n insertText(ctxRef.current, e.data);\n }\n }, []);\n\n const topLevels = useMemo(() => groupTopLevels(snapshot.blocks), [snapshot]);\n // Chave estrutural dos topLevels: muda só quando a SHAPE muda (tipos,\n // contagem de itens em lists, list kind/style/start), não quando o texto\n // dentro de um block é editado. Usada como dep do `usePageLayout` em\n // lugar do array `topLevels` (cuja referência muda a cada keystroke), o\n // que cortava repaginações desnecessárias — antes 1.3M DOM mutations\n // por ~10 teclas viraram zero. O array `topLevels` em si continua fresh\n // pra `renderSlot` ler o texto atualizado de cada block.\n const topLevelsStructuralKey = useMemo(\n () =>\n topLevels\n .map((t) =>\n t.kind === \"list\"\n ? `L:${t.listKind}:${t.items.length}:${t.ordinalStart}`\n : `B:${t.block.type}:${t.block.attrs.level ?? \"\"}`,\n )\n .join(\"|\"),\n [topLevels],\n );\n // Page geometry sources, in precedence order:\n // 1. `pageGeometry` prop when explicitly set (view-only override — does NOT\n // write back to the Y.Doc; useful for apps that force a fixed size, e.g.\n // prova generator pinned to A4).\n // 2. `editor.doc` page settings (Y.Doc-backed, sync-aware).\n // 3. `A4_PAGE` fallback when both unavailable.\n // `pageGeometry={null}` continues to disable pagination entirely.\n const docSettings = usePageSettings(editor.doc);\n const propIsSet = pageGeometry !== undefined;\n const geom: PageGeometry = propIsSet ? (pageGeometry ?? A4_PAGE) : docSettings;\n const paginated = pageGeometry !== null;\n // Reads from snapshot + selection; recomputed on every render.\n const selectedEmbed = editor.getSelectedEmbed();\n\n const { layout, phase: paginationPhase } = usePageLayout(\n rootRef as RefObject<HTMLElement>,\n geom,\n topLevels.length,\n // Usar a chave estrutural (string) em vez do array `topLevels` direto:\n // o array reference muda a cada keystroke, mas a chave só muda quando\n // a SHAPE dos top-levels muda. `usePageLayout` compara deps via\n // `shallowArrayEqual` — string equality cobre isso.\n [topLevelsStructuralKey, pageGeometry],\n );\n\n const effectiveLayout: PageLayout = paginated ? layout : defaultPageLayout(topLevels.length);\n\n // pageStyle memoizado: CSS custom properties são HERDADAS, então qualquer\n // toggle de `style` no `.ed-root` invalidava estilos em todos os\n // descendentes (~165k elementos no caso do bug histórico) e alimentava\n // style recalc pesado a cada keystroke. Memo aqui mantém a mesma\n // referência enquanto width/height/margens não mudam.\n const pageStyle = useMemo(\n () =>\n paginated\n ? ({\n \"--ed-page-width\": `${geom.width}px`,\n \"--ed-page-height\": `${geom.height}px`,\n \"--ed-page-margin-top\": `${geom.marginTop}px`,\n \"--ed-page-margin-bottom\": `${geom.marginBottom}px`,\n \"--ed-page-margin-left\": `${geom.marginLeft}px`,\n \"--ed-page-margin-right\": `${geom.marginRight}px`,\n } as React.CSSProperties)\n : undefined,\n [\n paginated,\n geom.width,\n geom.height,\n geom.marginTop,\n geom.marginBottom,\n geom.marginLeft,\n geom.marginRight,\n ],\n );\n\n const body = (\n <div\n ref={rootRef}\n contentEditable\n suppressContentEditableWarning\n spellCheck\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n role=\"textbox\"\n aria-multiline=\"true\"\n className={\n (className ?? \"ed-root\") + (paginated ? \" ed-root--paged\" : \" ed-root--flat\")\n }\n data-pagination-phase={paginated ? paginationPhase : undefined}\n style={pageStyle}\n onKeyDown={onKeyDown}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n >\n {paginated\n ? effectiveLayout.pages.map((page, pi) => {\n const ctx: PageRenderContext = {\n pageNumber: pi + 1,\n pageCount: effectiveLayout.pages.length,\n };\n // Apenas a pág 1 recebe o `firstPageExtraTop` adicional. Inline\n // style sobrescreve a CSS var herdada do `.ed-root` (que vale para\n // as páginas seguintes).\n const pageInlineStyle =\n pi === 0 && geom.firstPageExtraTop\n ? ({\n \"--ed-page-margin-top\": `${geom.marginTop + geom.firstPageExtraTop}px`,\n } as React.CSSProperties)\n : undefined;\n return (\n <div\n key={`page-${pi}`}\n className=\"ed-page\"\n data-page-number={ctx.pageNumber}\n data-page-total={ctx.pageCount}\n style={pageInlineStyle}\n >\n {renderPageHeader && (\n <div className=\"ed-page-header\" contentEditable={false}>\n {renderPageHeader(ctx)}\n </div>\n )}\n <div className=\"ed-page-content\">\n {page.slots.map((slot) => renderSlot(slot, topLevels))}\n </div>\n {renderPageFooter && (\n <div className=\"ed-page-footer\" contentEditable={false}>\n {renderPageFooter(ctx)}\n </div>\n )}\n </div>\n );\n })\n : topLevels.map((t) => renderTopLevel(t))}\n {selectedEmbed && (\n <ImageResizeOverlay\n rootRef={rootRef}\n blockIndex={selectedEmbed.blockIndex}\n offset={selectedEmbed.offset}\n cellIndex={selectedEmbed.cellIndex}\n embed={selectedEmbed.embed}\n onCommit={(w, h) =>\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n { width: w, height: h },\n selectedEmbed.cellIndex,\n )\n }\n onCommitMove={(ox, oy) =>\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n { offsetX: ox, offsetY: oy },\n selectedEmbed.cellIndex,\n )\n }\n onReanchor={(to, ox, oy) =>\n editor.moveEmbedAnchor(\n {\n blockIndex: selectedEmbed.blockIndex,\n offset: selectedEmbed.offset,\n cellIndex: selectedEmbed.cellIndex,\n },\n to,\n ox,\n oy,\n )\n }\n />\n )}\n {hoveredBehind &&\n !(\n selectedEmbed &&\n selectedEmbed.blockIndex === hoveredBehind.blockIndex &&\n selectedEmbed.offset === hoveredBehind.offset &&\n (selectedEmbed.cellIndex ?? -1) === (hoveredBehind.cellIndex ?? -1)\n ) && (\n <BehindImageSelectAffordance\n rootRef={rootRef}\n blockIndex={hoveredBehind.blockIndex}\n offset={hoveredBehind.offset}\n cellIndex={hoveredBehind.cellIndex}\n onSelect={() =>\n setSelection({\n anchor: {\n blockIndex: hoveredBehind.blockIndex,\n cellIndex: hoveredBehind.cellIndex,\n offset: hoveredBehind.offset,\n },\n focus: {\n blockIndex: hoveredBehind.blockIndex,\n cellIndex: hoveredBehind.cellIndex,\n offset: hoveredBehind.offset + 1,\n },\n })\n }\n />\n )}\n {hoveredLink && hoveredLink.href && (\n <LinkHoverTooltip href={hoveredLink.href} rect={hoveredLink.rect} />\n )}\n {awareness && (\n <RemoteCursorsOverlay\n rootRef={rootRef}\n doc={doc}\n awareness={awareness}\n revision={snapshot}\n />\n )}\n </div>\n );\n\n return <EditorProvider editor={editor}>{body}</EditorProvider>;\n}\n\n// ---------- Top-level grouping (data, not JSX) ----------\n\ninterface ListEntry {\n block: SerializedBlock;\n index: number;\n}\n\ntype TopLevel =\n | { kind: \"block\"; block: SerializedBlock; blockIndex: number; key: number }\n | {\n kind: \"list\";\n items: ListEntry[];\n listKind: ListKind;\n /**\n * Number to seed the `<ol>` `start` attribute. Pulled from the first\n * item's `listStart` attr, defaulting to 1. Only meaningful for\n * `listKind === \"ordered\"`.\n */\n ordinalStart: number;\n /**\n * Optional CSS `list-style-type` override for this group. Pulled from\n * the first item's `listStyle` attr. Undefined means \"use the cascade\n * cycle from sofer-editor.css\".\n */\n listStyle?: string;\n key: number;\n };\n\nexport function groupTopLevels(snapshot: SerializedBlock[]): TopLevel[] {\n const out: TopLevel[] = [];\n let i = 0;\n while (i < snapshot.length) {\n const block = snapshot[i];\n if (block.type === \"listItem\") {\n const groupStart = i;\n const items: ListEntry[] = [];\n const kind: ListKind = block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const leaderLevel = clampListLevel(block.attrs.listLevel);\n const groupStyle = block.attrs.listStyle as string | undefined;\n // A `listStart` on the FIRST item of the group seeds the `<ol start>`.\n // A `listStart` or `listStyle` on a LATER item breaks the group at that\n // item — its first item becomes the seed of the next group.\n while (i < snapshot.length) {\n const b = snapshot[i];\n if (b.type !== \"listItem\") break;\n // A type change breaks the group whenever the item sits AT OR ABOVE\n // the group leader's level — that's a genuine sibling list of a\n // different type (or a return to a shallower level, which\n // `buildListTree`'s stack treats as a new root, same as the leader).\n // Only a type change STRICTLY DEEPER than the leader stays in the\n // group: that's a sublist (e.g. a bullet sub-list under a numbered\n // item pasted from Word), and `buildListTree` nests it under its\n // parent instead of making it a root.\n const bLevel = clampListLevel(b.attrs.listLevel);\n if (bLevel <= leaderLevel && (b.attrs.listKind ?? \"bullet\") !== kind) {\n break;\n }\n if (i > groupStart) {\n // Continuation: break the group ONLY when this item EXPLICITLY\n // declares a renumbering. An undefined `listStart` / `listStyle`\n // means \"inherit from the current group leader\" — that's the\n // common case (user only tags the first item of the new sequence).\n if (typeof b.attrs.listStart === \"number\") break;\n const itemStyle = b.attrs.listStyle as string | undefined;\n if (itemStyle !== undefined && itemStyle !== groupStyle) break;\n }\n items.push({ block: b, index: i });\n i++;\n }\n const firstAttrs = items[0].block.attrs;\n const ordinalStart =\n typeof firstAttrs.listStart === \"number\" && firstAttrs.listStart > 0\n ? firstAttrs.listStart\n : 1;\n out.push({\n kind: \"list\",\n items,\n listKind: kind,\n ordinalStart,\n listStyle: groupStyle,\n key: groupStart,\n });\n continue;\n }\n out.push({ kind: \"block\", block, blockIndex: i, key: i });\n i++;\n }\n return out;\n}\n\nfunction renderSlot(slot: PageSlot, topLevels: TopLevel[]): JSX.Element | null {\n const top = topLevels[slot.topLevelIndex];\n if (!top) return null;\n if (slot.fragment && top.kind === \"block\") {\n const f: NodeViewFragment = {\n index: slot.fragment.index,\n start: slot.fragment.start,\n end: slot.fragment.end,\n };\n return (\n <NodeView\n key={`${top.key}-f${f.index}`}\n block={top.block}\n index={top.blockIndex}\n fragment={f}\n />\n );\n }\n if (slot.tableFragment && top.kind === \"block\" && top.block.type === \"table\") {\n return (\n <NodeView\n key={`${top.key}-tf${slot.tableFragment.index}`}\n block={top.block}\n index={top.blockIndex}\n tableFragment={slot.tableFragment}\n />\n );\n }\n if (slot.listFragment && top.kind === \"list\") {\n return renderListFragment(top, slot.listFragment);\n }\n return renderTopLevel(top);\n}\n\nexport function renderTopLevel(top: TopLevel): JSX.Element {\n if (top.kind === \"list\") {\n const tree = buildListTree(top.items);\n return renderListTree(tree, top, top.ordinalStart, `list-${top.key}`);\n }\n return <NodeView key={top.key} block={top.block} index={top.blockIndex} />;\n}\n\n/**\n * Render a sub-range of a list group's top-level items, as produced by\n * `placeFragmentedList`. The continuation `<ol>` carries an adjusted `start`\n * so numbering remains contiguous across the page break.\n */\nexport function renderListFragment(top: TopLevel & { kind: \"list\" }, frag: { index: number; itemStart: number; itemEnd: number }): JSX.Element {\n const ranges = topLevelItemRanges(top.items);\n const startRange = ranges[frag.itemStart];\n const endRange = ranges[frag.itemEnd - 1];\n if (!startRange || !endRange) return renderTopLevel(top);\n const sliced = top.items.slice(startRange[0], endRange[1]);\n // Continuation start = group's seed + count of top-level items already on prior pages.\n const ordinalStart = top.ordinalStart + frag.itemStart;\n const key = `list-${top.key}-f${frag.index}`;\n const slicedTop: TopLevel & { kind: \"list\" } = { ...top, items: sliced };\n const tree = buildListTree(sliced);\n return renderListTree(tree, slicedTop, ordinalStart, key);\n}\n\n/**\n * Index ranges within a flat `ListEntry[]` corresponding to each top-level\n * (level-0) item plus its nested descendants. Used by `renderListFragment` to\n * slice the flat list on a top-level boundary that matches what the paginator\n * measured against direct `<li>` children.\n */\nexport function topLevelItemRanges(items: ListEntry[]): Array<[number, number]> {\n const out: Array<[number, number]> = [];\n let currentStart = -1;\n items.forEach((it, idx) => {\n const level = clampListLevel(it.block.attrs.listLevel);\n if (level === 0) {\n if (currentStart >= 0) out.push([currentStart, idx]);\n currentStart = idx;\n }\n });\n if (currentStart >= 0) out.push([currentStart, items.length]);\n return out;\n}\n\ninterface ListNode {\n entry: ListEntry;\n children: ListNode[];\n}\n\nfunction buildListTree(items: ListEntry[]): ListNode[] {\n const roots: ListNode[] = [];\n const stack: { children: ListNode[]; level: number }[] = [{ children: roots, level: -1 }];\n for (const it of items) {\n const level = clampListLevel(it.block.attrs.listLevel);\n while (stack.length > 1 && stack[stack.length - 1].level >= level) stack.pop();\n const node: ListNode = { entry: it, children: [] };\n stack[stack.length - 1].children.push(node);\n stack.push({ children: node.children, level });\n }\n return roots;\n}\n\nfunction renderListTree(\n tree: ListNode[],\n top: TopLevel & { kind: \"list\" },\n start: number,\n key: string,\n): JSX.Element {\n const className = `ed-list ed-list-${top.listKind}`;\n const style: React.CSSProperties | undefined = top.listStyle\n ? { listStyleType: top.listStyle }\n : undefined;\n const items = tree.map((node) => (\n <NodeView key={node.entry.index} block={node.entry.block} index={node.entry.index}>\n {node.children.length > 0\n ? renderNestedListTree(node.children, `nested-${node.entry.index}`)\n : null}\n </NodeView>\n ));\n if (top.listKind === \"ordered\") {\n return (\n <ol\n key={key}\n className={className}\n data-list-kind=\"ordered\"\n start={start > 1 ? start : undefined}\n style={style}\n >\n {items}\n </ol>\n );\n }\n return (\n <ul key={key} className={className} data-list-kind=\"bullet\" style={style}>\n {items}\n </ul>\n );\n}\n\n/**\n * Nested sub-lists never carry an explicit `start`/`listStyle` from a parent\n * item: the cascade in `sofer-editor.css` handles the per-level style cycle\n * (decimal → lower-alpha → lower-roman). Only the TOP-level `<ol>` for a\n * group needs an explicit `start`/`listStyleType`.\n *\n * The sublist's own `listKind` comes from ITS first item, never from the\n * parent/ancestor group. A numbered item pasted from Word can have a bullet\n * sub-list underneath (or vice-versa) — `groupTopLevels` now keeps those\n * together in one group since the type change happens below the group's\n * level-0 leader, so this function must not impose the ancestor's kind on\n * every descendant level.\n */\nfunction renderNestedListTree(tree: ListNode[], key: string): JSX.Element {\n const kind: ListKind = tree[0]?.entry.block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const Tag = kind === \"ordered\" ? \"ol\" : \"ul\";\n return (\n <Tag key={key} className={`ed-list ed-list-${kind}`} data-list-kind={kind}>\n {tree.map((node) => (\n <NodeView key={node.entry.index} block={node.entry.block} index={node.entry.index}>\n {node.children.length > 0\n ? renderNestedListTree(node.children, `nested-${node.entry.index}`)\n : null}\n </NodeView>\n ))}\n </Tag>\n );\n}\n\nfunction clampListLevel(l: unknown): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\nconst BOOLEAN_SHORTCUTS: Record<string, MarkName> = {\n b: \"bold\",\n i: \"italic\",\n u: \"underline\",\n};\n\ninterface CaretRangeFromPoint {\n caretRangeFromPoint(x: number, y: number): Range | null;\n}\ninterface CaretPositionFromPoint {\n caretPositionFromPoint(\n x: number,\n y: number,\n ): { offsetNode: Node; offset: number } | null;\n}\n\nfunction caretRangeFromPoint(x: number, y: number): Range | null {\n const doc = document as unknown as Partial<CaretRangeFromPoint & CaretPositionFromPoint>;\n if (typeof doc.caretRangeFromPoint === \"function\") {\n return doc.caretRangeFromPoint(x, y);\n }\n if (typeof doc.caretPositionFromPoint === \"function\") {\n const pos = doc.caretPositionFromPoint(x, y);\n if (!pos) return null;\n const range = document.createRange();\n range.setStart(pos.offsetNode, pos.offset);\n range.collapse(true);\n return range;\n }\n return null;\n}\n","import type { Position, Selection } from \"@sofereditor/core\";\n\n/**\n * Mapping between the model and the contenteditable DOM.\n *\n * Each block is rendered as one or more elements carrying `data-block-index`.\n * Unfragmented blocks have a single such element. A paragraph fragmented across\n * pages (Sub-phase 3.2) has multiple elements with the same `data-block-index`,\n * each carrying `data-fragment-start` / `data-fragment-end` indicating the\n * character offsets within the block's text that the element covers.\n *\n * Table blocks (Sub-phase 4.1) render the `<table>` itself with `data-block-index`.\n * Each `<td>` cell carries `data-cell-index` (flat row-major). Model positions\n * inside a table always include `cellIndex`; their `offset` is local to that\n * cell's text.\n */\n\n/** Walks up from `node` to the first ancestor that carries `data-block-index`. */\nfunction findContainingBlockElement(node: Node, root: HTMLElement): HTMLElement | null {\n let cur: Node | null = node;\n while (cur && cur !== root) {\n if (cur.nodeType === Node.ELEMENT_NODE) {\n const el = cur as HTMLElement;\n if (el.dataset.blockIndex != null) return el;\n }\n cur = cur.parentNode;\n }\n return null;\n}\n\n/** Walks up to the first ancestor carrying `data-cell-index`, stopping at the block element. */\nfunction findContainingCellElement(\n node: Node,\n blockEl: HTMLElement,\n): HTMLElement | null {\n let cur: Node | null = node;\n while (cur && cur !== blockEl) {\n if (cur.nodeType === Node.ELEMENT_NODE) {\n const el = cur as HTMLElement;\n if (el.dataset.cellIndex != null) return el;\n }\n cur = cur.parentNode;\n }\n return null;\n}\n\nexport function findBlockIndex(node: Node, root: HTMLElement): number | null {\n const el = findContainingBlockElement(node, root);\n if (!el) return null;\n return Number.parseInt(el.dataset.blockIndex!, 10);\n}\n\n/**\n * Returns the first DOM element for `blockIndex`. For a fragmented block this\n * is the first fragment. Prefer `getFragmentForOffset` when you need the\n * fragment that contains a specific offset.\n */\nexport function getBlockElement(root: HTMLElement, blockIndex: number): HTMLElement | null {\n return root.querySelector<HTMLElement>(`[data-block-index=\"${blockIndex}\"]`);\n}\n\nfunction readFragmentStart(el: HTMLElement): number {\n const s = el.dataset.fragmentStart;\n if (s == null) return 0;\n const n = Number.parseInt(s, 10);\n return Number.isFinite(n) ? n : 0;\n}\n\nfunction readFragmentEnd(el: HTMLElement): number | undefined {\n const s = el.dataset.fragmentEnd;\n if (s == null) return undefined;\n const n = Number.parseInt(s, 10);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Returns the DOM element responsible for `offset` inside `blockIndex`.\n * - Unfragmented block (single element): returns it.\n * - Fragmented block: returns the fragment whose `[start, end)` covers `offset`.\n * For the end-of-block offset, returns the last fragment.\n */\nexport function getFragmentForOffset(\n root: HTMLElement,\n blockIndex: number,\n offset: number,\n): HTMLElement | null {\n const candidates = root.querySelectorAll<HTMLElement>(`[data-block-index=\"${blockIndex}\"]`);\n if (candidates.length === 0) return null;\n if (candidates.length === 1) return candidates[0];\n let last: HTMLElement | null = null;\n for (const el of candidates) {\n last = el;\n const start = readFragmentStart(el);\n const end = readFragmentEnd(el);\n if (end == null) return el;\n if (offset >= start && offset < end) return el;\n }\n return last; // end-of-block offset falls on the last fragment\n}\n\n/**\n * Returns the `<td>` element for `(blockIndex, cellIndex)`. Tables are\n * unfragmented in 4.1 so there's at most one block element to search inside.\n */\nexport function getCellElement(\n root: HTMLElement,\n blockIndex: number,\n cellIndex: number,\n): HTMLElement | null {\n const blockEl = getBlockElement(root, blockIndex);\n if (!blockEl) return null;\n return blockEl.querySelector<HTMLElement>(`[data-cell-index=\"${cellIndex}\"]`);\n}\n\n/**\n * `true` when `el` is an image embed (a length-1 atomic insert in the model).\n * Image embeds participate in offset arithmetic as 1 char each. Intentionally\n * not a type predicate — narrowing `n is HTMLElement` would collapse the\n * \"already an HTMLElement\" path to `never` and break downstream code.\n */\nfunction isImgEmbed(n: Node): boolean {\n return (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embed === \"image\"\n );\n}\n\n/**\n * `true` when `el` is the float-wrap anchor `<img>` rendered at the START of a\n * block (so CSS float can wrap text around it). The model places this embed at\n * its original offset — represented in the DOM by a zero-width phantom span —\n * so the anchor itself must be SKIPPED during offset counting.\n */\nfunction isWrapAnchorImg(n: Node): boolean {\n return (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.wrapAnchor === \"true\"\n );\n}\n\n/**\n * `true` when `el` is the zero-width sentinel left at the model offset of a\n * float-wrap image. Counts as 1 char in offset arithmetic, mirroring how the\n * embed counts in the model.\n */\nfunction isPhantomEmbed(n: Node): boolean {\n return (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embedPhantom === \"true\"\n );\n}\n\n/** Index of `n` within its parent's `childNodes`. */\nfunction indexInParent(n: Node): number {\n const parent = n.parentNode;\n if (!parent) return 0;\n let i = 0;\n for (const c of Array.from(parent.childNodes)) {\n if (c === n) return i;\n i++;\n }\n return 0;\n}\n\n/**\n * `true` when the subtree rooted at `el` should be skipped during a\n * container-scoped walk — i.e. it belongs to a different block or to a sibling\n * cell when scoping a cell container.\n */\nfunction isRejectedSubtree(el: HTMLElement, container: HTMLElement): boolean {\n if (el === container) return false;\n if (el.dataset.blockIndex != null) return true;\n if (container.dataset.cellIndex != null && el.dataset.cellIndex != null) return true;\n return false;\n}\n\n/**\n * Sum length of all text nodes plus 1 for each image embed inside `container`,\n * up to the boundary defined by `(node, offsetInNode)`. Mirrors how the model\n * counts characters: embeds are atomic length-1 inserts.\n */\nexport function textOffsetWithin(\n container: HTMLElement,\n node: Node,\n offsetInNode: number,\n): number {\n // Normalize an anchor pointing AT an image embed to (parent, indexOfImg).\n let target: Node = node;\n let targetOff = offsetInNode;\n if (isImgEmbed(target) && target.parentNode) {\n targetOff = indexInParent(target);\n target = target.parentNode as Node;\n }\n\n let offset = 0;\n let done = false;\n\n function visit(n: Node): void {\n if (done) return;\n if (n === target) {\n if (n.nodeType === Node.TEXT_NODE) {\n offset += targetOff;\n done = true;\n return;\n }\n // Element target: consume childNodes[0..targetOff-1] recursively.\n const children = n.childNodes;\n for (let i = 0; i < targetOff && i < children.length; i++) {\n visit(children[i]);\n if (done) return;\n }\n done = true;\n return;\n }\n if (n.nodeType === Node.TEXT_NODE) {\n offset += (n.textContent ?? \"\").length;\n return;\n }\n if (n.nodeType !== Node.ELEMENT_NODE) return;\n const el = n as HTMLElement;\n if (isRejectedSubtree(el, container)) return;\n // Float-wrap anchor `<img>` is rendered at the block start as a pure\n // visual; its model position lives in the phantom span downstream.\n if (isImgEmbed(el) && isWrapAnchorImg(el)) return;\n if (isImgEmbed(el) || isPhantomEmbed(el)) {\n offset += 1;\n return;\n }\n for (const child of Array.from(el.childNodes)) {\n visit(child);\n if (done) return;\n }\n }\n\n visit(container);\n return offset;\n}\n\ninterface ResolvedAnchor {\n blockIdx: number;\n cellIdx: number | undefined;\n /** The element whose text content offsets are computed against. */\n container: HTMLElement;\n /** Number to add to within-container offsets — non-zero only for fragmented blocks. */\n containerStart: number;\n}\n\nfunction resolveAnchorElement(\n node: Node,\n root: HTMLElement,\n): ResolvedAnchor | null {\n const blockEl = findContainingBlockElement(node, root);\n if (!blockEl) return null;\n const blockIdx = Number.parseInt(blockEl.dataset.blockIndex!, 10);\n const blockType = blockEl.dataset.blockType;\n if (blockType === \"table\") {\n const cellEl = findContainingCellElement(node, blockEl);\n if (!cellEl) {\n // Click landed inside the table but not in any cell — snap to first cell.\n const firstCell = blockEl.querySelector<HTMLElement>(\"[data-cell-index]\");\n if (!firstCell) return null;\n return {\n blockIdx,\n cellIdx: 0,\n container: firstCell,\n containerStart: 0,\n };\n }\n const cellIdx = Number.parseInt(cellEl.dataset.cellIndex!, 10);\n return {\n blockIdx,\n cellIdx,\n container: cellEl,\n containerStart: 0,\n };\n }\n return {\n blockIdx,\n cellIdx: undefined,\n container: blockEl,\n containerStart: readFragmentStart(blockEl),\n };\n}\n\nexport function readDomSelection(root: HTMLElement): Selection | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n if (!sel.anchorNode || !sel.focusNode) return null;\n if (!root.contains(sel.anchorNode) || !root.contains(sel.focusNode)) return null;\n\n const a = resolveAnchorElement(sel.anchorNode, root);\n const f = resolveAnchorElement(sel.focusNode, root);\n if (!a || !f) return null;\n\n const anchor: Position = {\n blockIndex: a.blockIdx,\n cellIndex: a.cellIdx,\n offset: a.containerStart + textOffsetWithin(a.container, sel.anchorNode, sel.anchorOffset),\n };\n const focus: Position = {\n blockIndex: f.blockIdx,\n cellIndex: f.cellIdx,\n offset: f.containerStart + textOffsetWithin(f.container, sel.focusNode, sel.focusOffset),\n };\n return { anchor, focus };\n}\n\ninterface DomPoint {\n node: Node;\n offset: number;\n}\n\nexport function locatePoint(root: HTMLElement, pos: Position): DomPoint | null {\n let container: HTMLElement | null;\n let containerStart = 0;\n\n if (pos.cellIndex != null) {\n container = getCellElement(root, pos.blockIndex, pos.cellIndex);\n if (!container) return null;\n } else {\n container = getFragmentForOffset(root, pos.blockIndex, pos.offset);\n if (!container) return null;\n containerStart = readFragmentStart(container);\n }\n\n let remaining = Math.max(0, pos.offset - containerStart);\n let result: DomPoint | null = null;\n let lastImg: HTMLElement | null = null;\n let lastText: Text | null = null;\n\n function visit(n: Node): void {\n if (result) return;\n if (n.nodeType === Node.TEXT_NODE) {\n const len = (n.textContent ?? \"\").length;\n if (remaining <= len) {\n result = { node: n, offset: remaining };\n return;\n }\n remaining -= len;\n lastImg = null;\n lastText = n as Text;\n return;\n }\n if (n.nodeType !== Node.ELEMENT_NODE) return;\n const el = n as HTMLElement;\n if (isRejectedSubtree(el, container!)) return;\n // Skip the float-wrap anchor — its slot in the model lives on the phantom.\n if (isImgEmbed(el) && isWrapAnchorImg(el)) return;\n if (isImgEmbed(el) || isPhantomEmbed(el)) {\n if (remaining === 0) {\n const parent = el.parentNode;\n if (parent) {\n result = { node: parent, offset: indexInParent(el) };\n return;\n }\n }\n remaining -= 1;\n lastImg = el;\n return;\n }\n for (const child of Array.from(el.childNodes)) {\n visit(child);\n if (result) return;\n }\n }\n\n visit(container);\n if (result) return result;\n\n // Walked the whole container without consuming `remaining` — caret lands\n // just AFTER the last embed if it was the last visited node.\n if (lastImg && remaining === 0) {\n const parent = (lastImg as HTMLElement).parentNode;\n if (parent) return { node: parent, offset: indexInParent(lastImg) + 1 };\n }\n // Offset overflowed the container's content (the model selection is\n // transiently AHEAD of the rendered DOM — insertText commits the new\n // selection one React commit before the new text). Clamp to the END of the\n // last text node so the caret stays at the end of the line. Collapsing to\n // the container's start instead would paint the caret at the start of the\n // line for one frame before the next commit corrects it (bug #1 flash).\n if (lastText) {\n const t = lastText as Text;\n return { node: t, offset: (t.textContent ?? \"\").length };\n }\n return { node: container, offset: 0 };\n}\n\nexport function selectionsEqual(a: Selection, b: Selection): boolean {\n return (\n a.anchor.blockIndex === b.anchor.blockIndex &&\n (a.anchor.cellIndex ?? -1) === (b.anchor.cellIndex ?? -1) &&\n a.anchor.offset === b.anchor.offset &&\n a.focus.blockIndex === b.focus.blockIndex &&\n (a.focus.cellIndex ?? -1) === (b.focus.cellIndex ?? -1) &&\n a.focus.offset === b.focus.offset\n );\n}\n\n/**\n * `true` when the selection is a rectangular table-cell range — both endpoints\n * inside cells of the same block, addressing different cells. The visual\n * highlight comes from CSS classes applied by `<TableView>`; we deliberately\n * leave the native DOM selection empty so the browser doesn't draw a confusing\n * cross-`<td>` range.\n */\nexport function isTableRectSelection(sel: Selection): boolean {\n if (sel.anchor.blockIndex !== sel.focus.blockIndex) return false;\n if (sel.anchor.cellIndex == null || sel.focus.cellIndex == null) return false;\n return sel.anchor.cellIndex !== sel.focus.cellIndex;\n}\n\nexport function applyDomSelection(root: HTMLElement, modelSel: Selection): void {\n const domSel = window.getSelection();\n if (!domSel) return;\n if (isTableRectSelection(modelSel)) {\n // Don't paint a native range — TableView highlights the rect via classes.\n domSel.removeAllRanges();\n return;\n }\n const anchorPt = locatePoint(root, modelSel.anchor);\n const focusPt = locatePoint(root, modelSel.focus);\n if (!anchorPt || !focusPt) return;\n\n // Already on the resolved points? Don't touch the live range. `removeAllRanges`\n // momentarily clears the selection (the caret blinks out), so re-applying an\n // identical selection is a visible no-op flash. This matters when the offset\n // clamped to the end of the line (model transiently ahead of the DOM): the\n // caret is already there, so we must leave it alone (bug #1 flash).\n if (\n domSel.anchorNode === anchorPt.node &&\n domSel.anchorOffset === anchorPt.offset &&\n domSel.focusNode === focusPt.node &&\n domSel.focusOffset === focusPt.offset\n ) {\n return;\n }\n\n try {\n domSel.removeAllRanges();\n domSel.setBaseAndExtent(anchorPt.node, anchorPt.offset, focusPt.node, focusPt.offset);\n } catch {\n // ignore — node may not be attached yet\n }\n}\n","import type { ClipboardSlice } from \"@sofereditor/core\";\nimport { SOFER_MIME } from \"@sofereditor/core\";\nimport { htmlToSlice } from \"./htmlToSlice\";\n\n/**\n * O que fazer com uma colagem, decidido só a partir do clipboard.\n *\n * Vive separado do handler para que a ORDEM dos ramos seja testável sem montar\n * o editor inteiro. A ordem já regrediu uma vez em produção (ver `planPaste`).\n */\nexport type PastePlan =\n /** Slice do próprio editor (cópia interna). */\n | { kind: \"sofer\"; slice: ClipboardSlice }\n /** HTML externo (Word, Google Docs, navegador) com conteúdo aproveitável. */\n | { kind: \"html\"; slice: ClipboardSlice }\n /** Arquivos de imagem do sistema operacional. */\n | { kind: \"images\"; files: File[] }\n /** Texto puro; `text` nunca é vazio. */\n | { kind: \"text\"; text: string }\n /** Nada aproveitável — o handler não deve nem chamar preventDefault. */\n | { kind: \"none\" };\n\n/**\n * Decide o ramo da colagem.\n *\n * **A ordem é o ponto deste módulo.** HTML vem ANTES de arquivos de imagem: ao\n * copiar TEXTO, o Word coloca no clipboard o HTML completo E um PNG com o\n * trecho renderizado. Medido num caso real (2026-08-19): `types` =\n * `[\"text/plain\",\"text/html\",\"text/rtf\",\"Files\"]`, HTML de 40.517 bytes e\n * `image.png` de 7.392 bytes. Com arquivos na frente, o texto do professor era\n * colado como FIGURA e a formatação nunca era lida. O Google Docs não manda\n * arquivo — por isso só o Word sofria.\n *\n * Inverter é seguro porque `htmlToSlice` ignora imagens e devolve `null` quando\n * não sobra conteúdo: colar uma imagem (do Word, de um site ou do Finder) cai\n * no ramo de arquivos exatamente como antes.\n */\nexport function planPaste(cd: DataTransfer): PastePlan {\n // 1) Slice do próprio editor — sempre vence, é a representação mais fiel.\n const raw = cd.getData(SOFER_MIME);\n if (raw) {\n try {\n const slice = JSON.parse(raw) as ClipboardSlice;\n if (slice && Array.isArray(slice.blocks)) return { kind: \"sofer\", slice };\n } catch {\n // JSON corrompido — segue para os ramos externos.\n }\n }\n\n // 2) HTML externo, ANTES dos arquivos. Ver o comentário acima.\n const html = cd.getData(\"text/html\");\n if (html) {\n const slice = htmlToSlice(html);\n if (slice) return { kind: \"html\", slice };\n }\n\n // 3) Arquivos de imagem.\n //\n // Tentativa abandonada (2026-08-20): tentamos anexar, no final do trecho\n // colado, o arquivo de imagem solto que o Word deixa em `cd.files` quando\n // o HTML não referencia nenhuma figura (nem `<img>`, nem VML, nem `data:`).\n // A ideia era usar o grupo `\\pict` do RTF como sinal de que uma figura de\n // verdade tinha sido copiada, distinguindo isso do PNG \"screenshot\" que o\n // Word também anexa em TODA colagem de texto (ver o comentário acima).\n //\n // Verificado com clipboard real do Word para Mac — o discriminador não\n // existe. Duas colagens medidas no navegador:\n // colagem 1 (SÓ TEXTO, sem figura): \\pict: 0 \\shppict: false PNG: 55.814 bytes <img>: 0\n // colagem 2 (TEXTO + FIGURA de verdade): \\pict: 0 \\shppict: false PNG: 24.902 bytes <img>: 0\n // Nenhuma das duas tem `\\pict` no RTF, nenhuma tem `<img>` no HTML, as duas\n // trazem um PNG — e o tamanho não separa (a colagem só-texto rendeu um\n // arquivo MAIOR que a com figura). O PNG que o Word anexa é sempre uma\n // renderização da seleção, nunca a figura em si — não há sinal nenhum no\n // clipboard que distinga os dois casos.\n const files = Array.from(cd.files ?? []).filter((f) => f.type.startsWith(\"image/\"));\n if (files.length > 0) return { kind: \"images\", files };\n\n // 4) Texto puro.\n const text = cd.getData(\"text/plain\");\n if (text.length > 0) return { kind: \"text\", text };\n\n return { kind: \"none\" };\n}\n","import type {\n AlignValue,\n BlockAttrs,\n BlockType,\n ClipboardSlice,\n DeltaOp,\n ListKind,\n MarkAttrs,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport { isImageEmbed, MAX_LIST_LEVEL } from \"@sofereditor/core\";\nimport { MAX_INSERT_WIDTH } from \"./imageConstraints\";\n\n/**\n * Converte o HTML do clipboard (Word / Google Docs / navegador) num\n * `ClipboardSlice` que o editor já sabe inserir via `insertSlice`.\n *\n * Sempre bloco-inteiro: `openStart`/`openEnd` são `false` e `blockLevel` é\n * `true` — o conteúdo colado de fora não é um fragmento de um dos blocos do\n * editor, é uma sequência de blocos novos. `blockLevel: true` é o que diz a\n * `insertSlice` (em `@sofereditor/core`) para adotar type/attrs do bloco\n * colado em vez de tratá-lo como fragmento de seleção — ver o comentário em\n * `ClipboardSlice.blockLevel`.\n *\n * Retorna `null` quando não sobra nada aproveitável, para o chamador (`Editor.tsx`)\n * cair no ramo de texto puro em vez de engolir a colagem.\n *\n * Duas armadilhas reais que este módulo trata explicitamente (ver testes):\n * - Google Docs embrulha o documento inteiro num `<b style=\"font-weight:normal\"\n * id=\"docs-internal-guid-...\">` — uma tag inline por fora de blocos inteiros.\n * `hasBlockDescendant` detecta isso e trata o wrapper como transparente\n * (propaga as marcas resolvidas, mas não achata os blocos internos).\n * - Word não usa `<ul>/<ol>/<li>` — usa `<p style=\"mso-list:l0 level1 ...\">`\n * com o marcador (número/símbolo) como texto literal dentro de um span\n * `mso-list:Ignore`. `parseWordListLevel` + a exclusão desse span tratam isso.\n *\n * fontFamily é deliberadamente NUNCA emitida: a escola padroniza Arial e cada\n * documento tem sua própria tipografia (mesma decisão de `import-docx`, que\n * descarta `w:rFonts`). fontSize É preservado (ver `parseFontSizePt`): o\n * modelo já suporta a mark, a barra já tem controle de tamanho, e descartar o\n * tamanho perdia informação real (título de 37.5pt colado do Google Docs saía\n * do tamanho do corpo). Unidades que dependem do contexto de herança do\n * documento de origem (em/rem/%/larger/smaller/medium) não são resolvíveis\n * aqui e por isso não emitem marca — sem tamanho é melhor que tamanho errado.\n */\nexport function htmlToSlice(html: string): ClipboardSlice | null {\n if (!html || html.trim().length === 0) return null;\n let doc: Document;\n try {\n doc = new DOMParser().parseFromString(html, \"text/html\");\n } catch {\n return null;\n }\n const body = doc.body;\n if (!body) return null;\n\n const blocks: SerializedBlock[] = [];\n try {\n walkBlockLevel(body, {}, blocks);\n } catch {\n // M7: aninhamento extremo (milhares de <span> aninhados, por exemplo)\n // estoura a pilha de chamadas em `walkInlineNode`/`walkBlockLevel`, que\n // são recursivas. O try/catch original só cobria o `DOMParser`, não o\n // walk — a exceção subia por `planPaste` e a colagem não fazia nada, nem\n // caía no fallback de texto puro. Devolvendo `null` aqui, o chamador\n // degrada para esse fallback em vez de engolir a colagem em silêncio.\n return null;\n }\n if (blocks.length === 0) return null;\n\n // I4: se o professor copia a partir de um sub-item, o primeiro listItem da\n // colagem pode não ser nível 0 (forma irmã do Docs, ou um `mso-list …\n // level2` sozinho/em sequência). O renderizador (`topLevelItemRanges`,\n // `Editor.tsx`) só abre um range de página em `level === 0` — sem esse\n // nível presente, o fatiamento de página perde um item num fragmento e\n // duplica a lista inteira noutro. Não é conserto na paginação (fora de\n // escopo aqui): fecha o caminho normalizando cada sequência contígua de\n // listItem para que o primeiro item sempre comece em 0.\n normalizeListLevels(blocks);\n\n // Nada aproveitável = TODO bloco sem texto E sem embed de imagem. O Word\n // nunca escreve `<img>` no HTML do clipboard (nem VML, nem `data:` — ver o\n // comentário em `imageEmbedFromElement`), então uma imagem do Word sempre\n // produz um parágrafo vazio aqui: `<p class=MsoNormal><img ...><o:p></o:p></p>`\n // sem `<img>` nenhum sobrevivendo ao parser do navegador com atributos\n // aproveitáveis. Devolver esse parágrafo vazio faria `planPaste` escolher o\n // ramo de HTML e a imagem seria DESCARTADA — colar imagem do Word passaria a\n // inserir uma linha em branco. Devolvendo null, a colagem segue para o ramo\n // de arquivos, que insere a imagem (ver `pastePlan.ts`).\n //\n // O Google Docs, ao contrário, ESCREVE `<img src=\"data:image/...\">` no HTML\n // — por isso um bloco pode ter embed e nenhum texto (colar só a imagem, sem\n // nenhuma palavra ao redor). Esse caso agora conta como aproveitável.\n //\n // Parágrafo vazio NO MEIO de conteúdo real é linha em branco de propósito e\n // continua preservado — a regra só descarta quando não sobrou texto NEM\n // embed nenhum.\n const temConteudo = blocks.some(\n (b) => b.text.trim().length > 0 || b.delta.some((op) => isImageEmbed(op.insert)),\n );\n if (!temConteudo) return null;\n\n // `blockLevel: true` diz a `insertSlice` para ADOTAR o type/attrs do bloco\n // colado em vez de fazer splice inline (ver o comentário em `insertSlice`).\n // Isso só vale a pena quando o resultado tem mais de um bloco (é\n // inequivocamente uma sequência de blocos externa) OU quando o único bloco\n // NÃO é um parágrafo simples (heading/listItem/blockquote — adotar o tipo é\n // o ganho real). Um ÚNICO parágrafo — a forma mais comum de colagem curta,\n // vinda de nós inline soltos (site/Chrome `<span>`, Word `<p class=\n // MsoNormal><span>`, Docs `<b style=\"font-weight:normal\">`) — NÃO é uma\n // \"colagem de bloco inteiro\" no sentido que importa aqui: marcar\n // `blockLevel` nesse caso faz `insertSlice` tratar até um trecho de duas\n // palavras como bloco externo, partindo o parágrafo do professor em três\n // (Dhead / bloco colado / tail) em vez de um splice inline, e — pior —\n // colar no offset 0 de um heading fazia `am.clear()` nos attrs do bloco\n // alvo, rebaixando o título a `paragraph`. Sem `blockLevel`, esse caso cai\n // no ramo de splice inline (comportamento de antes da branch); um bloco\n // único que É heading/listItem/blockquote continua adotando o tipo — o\n // ganho que motivou esta branch.\n const blockLevel = blocks.length > 1 || blocks[0].type !== \"paragraph\";\n return { blocks, openStart: false, openEnd: false, ...(blockLevel ? { blockLevel: true as const } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// Classificação de tags\n// ---------------------------------------------------------------------------\n\n/** Tags cujo conteúdo nunca vira texto (script/style vazam pra dentro do body\n * no HTML que o Google Docs cola; o:p/w:.../v:... são namespaces do Word). */\nconst IGNORED_TAGS = new Set([\"script\", \"style\", \"meta\", \"link\", \"head\", \"title\", \"xml\"]);\n\n/** Tags \"block-ish\": sem filhos de bloco viram UM parágrafo; com filhos de\n * bloco são tratadas como container transparente (ver `hasBlockDescendant`). */\nconst BLOCK_LEVEL_TAGS = new Set([\n \"p\",\n \"div\",\n \"section\",\n \"article\",\n \"header\",\n \"footer\",\n \"main\",\n \"figure\",\n \"figcaption\",\n \"pre\",\n \"address\",\n]);\n\n/** Tags que, se aparecerem como descendente direto ou indireto de um elemento\n * não tratado explicitamente, forçam esse elemento a ser um container\n * transparente em vez de uma corrida inline (caso do wrapper `<b>` do Docs). */\nconst BLOCK_TRIGGER_TAGS = new Set([\n \"p\",\n \"div\",\n \"ul\",\n \"ol\",\n \"li\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"blockquote\",\n \"table\",\n \"tr\",\n \"td\",\n \"th\",\n]);\n\nfunction isNamespacedTag(tag: string): boolean {\n return tag.includes(\":\");\n}\n\nfunction hasBlockDescendant(el: Element): boolean {\n const all = el.querySelectorAll(\"*\");\n for (const e of Array.from(all)) {\n if (BLOCK_TRIGGER_TAGS.has(e.tagName.toLowerCase())) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Walker de nível de bloco\n// ---------------------------------------------------------------------------\n\nfunction walkBlockLevel(container: Element, marks: MarkAttrs, blocks: SerializedBlock[]): void {\n let loose: ChildNode[] = [];\n const flushLoose = () => {\n if (loose.length === 0) return;\n const nodes = loose;\n loose = [];\n const delta = inlineDeltaFromNodes(nodes, marks);\n if (delta.length > 0) blocks.push(makeBlock(\"paragraph\", delta, {}));\n };\n\n for (const node of Array.from(container.childNodes)) {\n if (node.nodeType === Node.COMMENT_NODE) continue;\n if (node.nodeType === Node.TEXT_NODE) {\n loose.push(node);\n continue;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\n const el = node as Element;\n const tag = el.tagName.toLowerCase();\n if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) continue;\n\n switch (tag) {\n case \"ul\":\n case \"ol\": {\n flushLoose();\n handleList(el, tag === \"ol\" ? \"ordered\" : \"bullet\", 0, marks, blocks);\n break;\n }\n case \"h1\":\n case \"h2\":\n case \"h3\":\n case \"h4\":\n case \"h5\":\n case \"h6\": {\n flushLoose();\n const level = Number(tag[1]) as 1 | 2 | 3 | 4 | 5 | 6;\n const attrs: BlockAttrs = { level };\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"heading\", delta, attrs));\n break;\n }\n case \"blockquote\": {\n flushLoose();\n handleBlockquote(el, marks, blocks);\n break;\n }\n case \"table\": {\n flushLoose();\n handleTable(el, marks, blocks);\n break;\n }\n case \"li\": {\n // <li> fora de <ul>/<ol> (defensivo) — trata como bullet nível 0.\n // Mesma exclusão de listas aninhadas do <li> normal (handleList):\n // sem isso, um <li> defensivo com filho <ul> vazaria o texto do\n // filho pro pai e perderia a estrutura de listItem do filho.\n flushLoose();\n const nestedLists = topLevelListDescendants(el);\n const clone = el.cloneNode(true) as Element;\n for (const l of topLevelListDescendants(clone)) l.remove();\n const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);\n blocks.push(makeBlock(\"listItem\", delta, { listKind: \"bullet\", listLevel: 0 }));\n for (const nested of nestedLists) {\n const nestedKind: ListKind = nested.tagName.toLowerCase() === \"ol\" ? \"ordered\" : \"bullet\";\n handleList(nested, nestedKind, 1, marks, blocks);\n }\n break;\n }\n case \"img\":\n // <img> como filho direto do container (sem <p> ao redor) — trata\n // como conteúdo solto, igual a <br>, pra juntar com texto vizinho no\n // mesmo parágrafo em vez de sempre virar um bloco próprio. A emissão\n // de fato (checagem de `src`/dimensões) acontece em `walkInlineNode`,\n // que processa `loose` no flush.\n loose.push(node);\n break;\n case \"br\":\n loose.push(node);\n break;\n default: {\n const styleAttr = el.getAttribute(\"style\") ?? \"\";\n const wordListLevel = parseWordListLevel(styleAttr);\n if (wordListLevel != null && !hasBlockDescendant(el)) {\n flushLoose();\n const marker = findWordListMarker(el);\n const listKind: ListKind = marker ? detectWordListKind(marker) : \"bullet\";\n const attrs: BlockAttrs = { listKind, listLevel: wordListLevel };\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"listItem\", delta, attrs));\n break;\n }\n if (hasBlockDescendant(el)) {\n // Container transparente: tag inline (ou div) por fora de blocos\n // inteiros — caso clássico do wrapper do Google Docs. Propaga as\n // marcas resolvidas do próprio elemento para os blocos internos,\n // sem achatar a estrutura.\n flushLoose();\n let nextMarks = applyTagMarks(tag, marks);\n nextMarks = applyStyleMarks(el.getAttribute(\"style\"), nextMarks);\n walkBlockLevel(el, nextMarks, blocks);\n break;\n }\n if (BLOCK_LEVEL_TAGS.has(tag)) {\n flushLoose();\n const attrs: BlockAttrs = {};\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"paragraph\", delta, attrs));\n break;\n }\n // Tag inline \"solta\" (span/b/i/a/font/...) sem filhos de bloco — junta\n // ao parágrafo corrente.\n loose.push(node);\n }\n }\n }\n flushLoose();\n}\n\n/**\n * Listas do Google Docs: `<ul>/<ol>` reais aninhados; nível = profundidade.\n *\n * Duas formas de aninhamento coexistem na prática e as duas têm que\n * funcionar:\n * - Canônica: `<li>texto<ul>...</ul></li>` — a lista filha é descendente do\n * `<li>` (direta, ou dentro de outro container como `<div>`).\n * - Real do Google Docs: `<li>texto</li><ul>...</ul>` — a lista filha é\n * IRMÃ do `<li>` dentro do mesmo `<ul>/<ol>` pai, não descendente dele.\n * Sem tratar essa forma, o item aninhado inteiro some (não aparece nem\n * como filho de outro bloco nem como block próprio).\n */\nfunction handleList(\n listEl: Element,\n kind: ListKind,\n level: number,\n marks: MarkAttrs,\n blocks: SerializedBlock[],\n): void {\n for (const child of Array.from(listEl.children)) {\n const tag = child.tagName.toLowerCase();\n if (tag === \"ul\" || tag === \"ol\") {\n // Forma \"irmã\": lista aninhada emitida como irmã do <li> anterior, não\n // dentro dele. Semanticamente ela pertence ao item anterior — sobe um\n // nível a partir do nível da lista atual.\n const siblingKind: ListKind = tag === \"ol\" ? \"ordered\" : \"bullet\";\n handleList(child, siblingKind, level + 1, marks, blocks);\n continue;\n }\n if (tag !== \"li\") continue;\n\n // Acha as listas aninhadas descendentes do <li> — diretas (forma\n // canônica) ou dentro de outro container (`<div>` etc; `<p>` nunca\n // aninha `<ul>` porque o parser HTML fecha o `<p>` antes). Só pega as\n // \"de fora para dentro\": uma lista aninhada dentro de outra lista já\n // encontrada não conta de novo aqui — ela é tratada recursivamente\n // quando a lista externa for percorrida.\n const nestedLists = topLevelListDescendants(child);\n\n // Clona o <li> e remove as listas aninhadas correspondentes do clone\n // antes de extrair o delta inline — sem isso, o texto do item filho\n // vaza para dentro do item pai (ex.: \"B1\" + \"B2\" viram \"B1B2\" quando a\n // lista aninhada está dentro de um <div> em vez de direta no <li>).\n const clone = child.cloneNode(true) as Element;\n for (const l of topLevelListDescendants(clone)) l.remove();\n const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);\n\n const attrs: BlockAttrs = { listKind: kind, listLevel: clampLevel(level) };\n const align = readAlign(child);\n if (align) attrs.align = align;\n blocks.push(makeBlock(\"listItem\", delta, attrs));\n for (const nested of nestedLists) {\n const nestedKind: ListKind = nested.tagName.toLowerCase() === \"ol\" ? \"ordered\" : \"bullet\";\n handleList(nested, nestedKind, level + 1, marks, blocks);\n }\n }\n}\n\n/** `<ul>/<ol>` descendentes de `root` que não estão, por sua vez, dentro de\n * outro `<ul>/<ol>` descendente de `root` — ou seja, o nível mais externo\n * de listas aninhadas dentro de `root`, não importa quantos containers\n * (`<div>`, `<p>`...) estejam no meio. Não atravessa fronteira de tabela:\n * uma lista dentro de uma `<td>`/`<th>` de uma `<table>` que por sua vez\n * esteja dentro de `root` pertence à célula, não ao `<li>` que contém a\n * tabela — não é \"promovida\" a listItem irmão (edge case raro, tabela\n * dentro de item de lista; ver `handleTable`, que não é chamado a partir\n * daqui, então o conteúdo da tabela continua achatado como antes). */\nfunction topLevelListDescendants(root: Element): Element[] {\n const all = Array.from(root.querySelectorAll(\"ul, ol\"));\n return all.filter((el) => {\n let p = el.parentElement;\n while (p && p !== root) {\n const t = p.tagName.toLowerCase();\n if (t === \"ul\" || t === \"ol\" || t === \"table\" || t === \"td\" || t === \"th\") return false;\n p = p.parentElement;\n }\n return true;\n });\n}\n\n/** `<blockquote>` — se tiver filhos de bloco (ex.: `<p>` internos), cada um\n * vira seu próprio bloco `blockquote`; senão, um único bloco a partir do\n * conteúdo inline. */\nfunction handleBlockquote(el: Element, marks: MarkAttrs, blocks: SerializedBlock[]): void {\n if (!hasBlockDescendant(el)) {\n const attrs: BlockAttrs = {};\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"blockquote\", delta, attrs));\n return;\n }\n const nested: SerializedBlock[] = [];\n walkBlockLevel(el, marks, nested);\n for (const b of nested) {\n blocks.push(b.type === \"paragraph\" ? { ...b, type: \"blockquote\" } : b);\n }\n}\n\n/** Tabelas ficam de fora do modelo: o texto de cada célula sobrevive como\n * parágrafo(s), em ordem de leitura (linha a linha, célula a célula). Nunca\n * descarta texto — só a estrutura de grade é perdida, de propósito. */\nfunction handleTable(table: Element, marks: MarkAttrs, blocks: SerializedBlock[]): void {\n for (const row of directRows(table)) {\n const cells = Array.from(row.children).filter((c) =>\n [\"td\", \"th\"].includes(c.tagName.toLowerCase()),\n );\n for (const cell of cells) {\n const cellBlocks: SerializedBlock[] = [];\n walkBlockLevel(cell, marks, cellBlocks);\n for (const b of cellBlocks) {\n const attrs: BlockAttrs = {};\n if (b.attrs.align) attrs.align = b.attrs.align;\n blocks.push(makeBlock(\"paragraph\", b.delta, attrs));\n }\n }\n }\n}\n\nfunction directRows(table: Element): Element[] {\n const rows: Element[] = [];\n for (const child of Array.from(table.children)) {\n const tag = child.tagName.toLowerCase();\n if (tag === \"tr\") {\n rows.push(child);\n } else if (tag === \"tbody\" || tag === \"thead\" || tag === \"tfoot\") {\n for (const r of Array.from(child.children)) {\n if (r.tagName.toLowerCase() === \"tr\") rows.push(r);\n }\n }\n }\n return rows;\n}\n\n// ---------------------------------------------------------------------------\n// Word: `mso-list` (sem <ul>/<ol>/<li> reais)\n// ---------------------------------------------------------------------------\n\n/**\n * `mso-list:l0 level2 lfo1` → nível 1 (level é 1-based no Word).\n *\n * Isso sozinho não basta: o Word pode usar duas listas mso-list *separadas*\n * para pai e filho (`l0`/`lfo1` vs `l1`/`lfo2`) e marcar as duas como\n * `level1` — a profundidade real então só aparece no `margin-left` do\n * parágrafo, não no `levelN`. Por isso o nível final é o MAIOR entre o que\n * vem do `levelN` e o que vem da indentação (`nivelPelaMargem`): nenhum dos\n * dois caminhos é descartado, porque cada um cobre um caso que o outro não\n * cobre (levelN real sem margin-left; margin-left real com level1 repetido).\n */\nfunction parseWordListLevel(styleAttr: string): number | null {\n const m = /mso-list\\s*:\\s*l\\d+\\s+level(\\d+)/i.exec(styleAttr);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n const fromLevelN = Number.isFinite(n) ? Math.max(0, n - 1) : 0;\n const fromMargin = wordListLevelFromMarginLeft(marginLeftPt(styleAttr));\n return clampLevel(Math.max(fromLevelN, fromMargin));\n}\n\n/** `margin-left` do parágrafo, convertido para pt. Aceita `pt`/`cm`/`in`/`px`;\n * ausente, `auto`, negativo ou não-parseável conta como 0 (nunca reduz o\n * nível abaixo do que `levelN` já indicava). Ignora `text-indent` de\n * propósito — é o recuo pendente do marcador (`-18pt` típico), não a\n * profundidade do item. */\nfunction marginLeftPt(styleAttr: string): number {\n const raw = parseStyle(styleAttr)[\"margin-left\"];\n if (!raw) return 0;\n const m = /^(-?[\\d.]+)\\s*(pt|cm|in|px)$/i.exec(raw.trim());\n if (!m) return 0;\n const val = Number.parseFloat(m[1]);\n if (!Number.isFinite(val) || val < 0) return 0;\n switch (m[2].toLowerCase()) {\n case \"pt\":\n return val;\n case \"cm\":\n return val * 28.3465;\n case \"in\":\n return val * 72;\n case \"px\":\n return val * 0.75;\n default:\n return 0;\n }\n}\n\n/** 36pt (0,5\") é o passo padrão de indentação de lista do Word: primeiro\n * nível de recuo (36pt) é nível 0, cada 36pt adicional soma 1 nível. */\nfunction wordListLevelFromMarginLeft(pt: number): number {\n if (pt <= 0) return 0;\n return Math.max(0, Math.round(pt / 36) - 1);\n}\n\n/** Acha o span `mso-list:Ignore` que embrulha o marcador (número/símbolo +\n * o espaço de tabulação até o texto real). */\nfunction findWordListMarker(el: Element): Element | null {\n const all = el.querySelectorAll(\"*\");\n for (const e of Array.from(all)) {\n const style = (e.getAttribute(\"style\") ?? \"\").toLowerCase().replace(/\\s+/g, \"\");\n if (style.includes(\"mso-list:ignore\")) return e;\n }\n return null;\n}\n\n/** Marcador com dígito/letra seguido de `.`/`)` → ordered; símbolo (Symbol/\n * Wingdings ou glifo não alfanumérico) → bullet. */\nfunction detectWordListKind(markerEl: Element): ListKind {\n const all = [markerEl, ...Array.from(markerEl.querySelectorAll(\"*\"))];\n for (const e of all) {\n const style = (e.getAttribute(\"style\") ?? \"\").toLowerCase();\n if (style.includes(\"symbol\") || style.includes(\"wingdings\")) return \"bullet\";\n }\n const text = (markerEl.textContent ?? \"\").replace(/ /g, \" \").trim();\n if (/^[0-9]+[.)]/.test(text)) return \"ordered\";\n if (/^[a-zA-Z][.)]/.test(text)) return \"ordered\";\n return \"bullet\";\n}\n\n// ---------------------------------------------------------------------------\n// Walker inline (marcas + texto)\n// ---------------------------------------------------------------------------\n\nfunction inlineDeltaFromNodes(nodes: ChildNode[], marks: MarkAttrs): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const n of nodes) walkInlineNode(n, marks, ops);\n return trimEdgeWhitespace(collapseRepeatedSpaces(mergeAdjacentOps(ops)));\n}\n\n/** Runs de espaço adjacentes em nós de texto distintos (ex.: \"antes \" + <img>\n * removida + \" depois\") colapsam pra um único espaço quando as marcas batem\n * e por isso já viraram um único op em `mergeAdjacentOps`. */\nfunction collapseRepeatedSpaces(ops: DeltaOp[]): DeltaOp[] {\n return ops.map((op) =>\n typeof op.insert === \"string\" ? { ...op, insert: op.insert.replace(/ {2,}/g, \" \") } : op,\n );\n}\n\nfunction walkInlineNode(node: ChildNode, marks: MarkAttrs, ops: DeltaOp[]): void {\n if (node.nodeType === Node.COMMENT_NODE) return;\n if (node.nodeType === Node.TEXT_NODE) {\n const text = normalizeWhitespace(node.textContent ?? \"\");\n if (text.length > 0) pushOp(ops, text, marks);\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n const el = node as Element;\n const tag = el.tagName.toLowerCase();\n\n if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) return;\n if (tag === \"img\") {\n const op = imageEmbedFromElement(el);\n if (op) ops.push(op);\n return;\n }\n if (tag === \"br\") {\n // \"\\n\" literal só é legítimo dentro de célula de tabela (ver\n // `core/src/commands.ts`, com `white-space: pre-wrap` no CSS da célula).\n // `.ed-block` não tem `pre-wrap`, então um \"\\n\" aqui virava espaço visual\n // mas continuava sendo um caractere invisível que contava offset e\n // travava o caret ao passar por ele. Correção mínima pra esta release:\n // emitir \" \" em vez de quebrar em blocos de verdade — quebrar em blocos\n // mudaria o contrato de `inlineDeltaFromNodes` (que hoje devolve um delta\n // pra um bloco só). Fica como trabalho futuro.\n ops.push({ insert: \" \" });\n return;\n }\n\n const styleAttr = (el.getAttribute(\"style\") ?? \"\").toLowerCase().replace(/\\s+/g, \"\");\n if (styleAttr.includes(\"mso-list:ignore\")) return; // marcador de lista do Word.\n\n let nextMarks = applyTagMarks(tag, marks);\n nextMarks = applyStyleMarks(el.getAttribute(\"style\"), nextMarks);\n if (tag === \"a\") {\n const href = el.getAttribute(\"href\");\n if (href) nextMarks = { ...nextMarks, link: { href } };\n }\n\n for (const child of Array.from(el.childNodes)) walkInlineNode(child, nextMarks, ops);\n}\n\n/**\n * `<img>` → `DeltaOp` de embed de imagem, ou `null` quando não dá pra\n * aproveitar. Duas condições, as duas obrigatórias:\n *\n * - `src` começando com `data:image/`. `http(s):` exigiria download\n * cross-origin (CORS, latência, falha silenciosa) e `file:` (Word no\n * Windows referencia a imagem por caminho local do disco do autor) é\n * inalcançável daqui — nos dois casos a imagem continua sendo ignorada,\n * como sempre foi.\n * - Dimensões resolvíveis: atributo `width`/`height` (número em px, é o que\n * o Google Docs manda), senão `style=\"width:...;height:...\"`. Sem\n * dimensão o layout quebra (o embed não tem tamanho intrínseco confiável\n * pra render), então sem NENHUMA das duas fontes a imagem é ignorada —\n * mesmo comportamento de antes desta mudança.\n *\n * O Word nunca chega aqui: ele não escreve `<img>` no HTML do clipboard de\n * jeito nenhum (nem VML, nem condicional `<![if !vml]>`, nem `data:` — ver o\n * comentário em `htmlToSlice`), então esta função só tem efeito prático para\n * fontes que de fato emitem `<img>` com `data:` (Google Docs é o caso medido).\n */\n/** Piso de bytes decodificados abaixo do qual um embed `data:image/` é\n * descartado — ver o comentário em `imageEmbedFromElement` (bug B2). */\nconst MIN_EMBED_BYTES = 1024;\n\n/** Tamanho aproximado, em bytes, do payload decodificado de uma `data:` URL —\n * sem decodificar de fato (nem `atob`, nem `decodeURIComponent` do payload\n * inteiro seria necessário para só medir tamanho), só o cálculo de tamanho a\n * partir do comprimento do texto. Cobre as duas formas de `data:` URL:\n * base64 (`;base64,`, o caso comum de imagem grande) e URL-encoded/percent-\n * encoded (`data:image/svg+xml,%3Csvg...`, comum em ícone inline). Payload\n * ausente ou URL sem vírgula → 0. */\nfunction decodedDataUrlByteLength(dataUrl: string): number {\n const commaIndex = dataUrl.indexOf(\",\");\n if (commaIndex === -1) return 0;\n const meta = dataUrl.slice(0, commaIndex);\n const payload = dataUrl.slice(commaIndex + 1);\n if (/;base64$/i.test(meta)) {\n const padding = payload.endsWith(\"==\") ? 2 : payload.endsWith(\"=\") ? 1 : 0;\n return Math.max(0, Math.floor((payload.length * 3) / 4) - padding);\n }\n // Forma URL-encoded: cada byte real ou é um caractere literal (1 byte) ou\n // um \"%XX\" (3 caracteres pra 1 byte) — decodeURIComponent dá o comprimento\n // certo em unidades UTF-16, aproximação suficiente pra um piso de tamanho.\n try {\n return decodeURIComponent(payload).length;\n } catch {\n return payload.length;\n }\n}\n\nfunction imageEmbedFromElement(el: Element): DeltaOp | null {\n const src = el.getAttribute(\"src\");\n if (!src || !src.startsWith(\"data:image/\")) return null;\n // Piso de bytes: o padrão de lazy-load mais comum da web é\n // `<img src=\"data:image/gif;base64,R0lGOD…\" data-src=\"real.jpg\" width=\"800\"\n // height=\"400\">` — um GIF 1×1 transparente usado só como placeholder\n // enquanto o `src` de verdade (`http`, ignorado por esta função) carrega.\n // Sem este piso, o 1×1 passa pelas duas checagens (é `data:image/`, tem\n // dimensões resolvíveis via atributo) e vira uma caixa em branco esticada\n // pro tamanho do placeholder — regressão: antes desta função existir,\n // TODO `<img>` era ignorado e nada aparecia. Um GIF/PNG 1×1 real tem\n // algumas dezenas de bytes; nenhuma figura de verdade de prova fica abaixo\n // de 1 KB decodificado.\n if (decodedDataUrlByteLength(src) < MIN_EMBED_BYTES) return null;\n const style = parseStyle(el.getAttribute(\"style\"));\n const width = readImageDimension(el, \"width\", style);\n const height = readImageDimension(el, \"height\", style);\n if (width == null || height == null) return null;\n // Mesmo teto de largura que insertImageFromFile/drag-drop já aplicam (área\n // de conteúdo de uma página A4) — sem isso, uma imagem colada que\n // declarasse largura maior estouraria a margem, e a mesma imagem inserida\n // via picker/drag ficaria menor que a colada (assimetria visível entre\n // dois caminhos que deveriam se comportar igual).\n let w = width;\n let h = height;\n if (w > MAX_INSERT_WIDTH) {\n h = (h * MAX_INSERT_WIDTH) / w;\n w = MAX_INSERT_WIDTH;\n }\n return { insert: { type: \"image\", src, width: Math.round(w), height: Math.round(h) } };\n}\n\n/** Lê uma dimensão do `<img>`: primeiro o atributo (`width`/`height`), senão\n * a propriedade `style` equivalente (convertida pra px via `cssLengthToPx`).\n *\n * O atributo só é aceito quando é um número cru (`\"568\"`, a forma que o\n * Google Docs manda) — NÃO quando tem sufixo, sobretudo `%`: `width=\"100%\"`\n * é HTML válido, e `Number.parseFloat` aceitaria `\"100%\"` como `100`, um\n * valor em PX completamente errado. Um atributo com sufixo cai pro `style`\n * em vez de ser mal-interpretado. */\nfunction readImageDimension(\n el: Element,\n attr: \"width\" | \"height\",\n style: Record<string, string>,\n): number | undefined {\n const raw = el.getAttribute(attr);\n if (raw && /^\\d+(\\.\\d+)?$/.test(raw.trim())) {\n const n = Number.parseFloat(raw);\n if (Number.isFinite(n) && n > 0) return n;\n }\n const fromStyle = style[attr];\n if (fromStyle) {\n const n = cssLengthToPx(fromStyle);\n if (n != null) return n;\n }\n return undefined;\n}\n\n/** CSS length → px. Aceita `px` (padrão quando a unidade falta), `pt`, `in`,\n * `cm`, `mm`. Unidades relativas ao contexto do documento de origem\n * (`%`/`em`/`rem`) não são resolvíveis aqui e devolvem `undefined` — mesma\n * filosofia de `parseFontSizePt`. */\nfunction cssLengthToPx(raw: string): number | undefined {\n const v = raw.trim();\n const m = /^(-?[\\d.]+)\\s*(px|pt|in|cm|mm)?$/i.exec(v);\n if (!m) return undefined;\n const num = Number.parseFloat(m[1]);\n if (!Number.isFinite(num) || num <= 0) return undefined;\n const unit = (m[2] ?? \"px\").toLowerCase();\n switch (unit) {\n case \"px\":\n return num;\n case \"pt\":\n return num * (96 / 72);\n case \"in\":\n return num * 96;\n case \"cm\":\n return num * (96 / 2.54);\n case \"mm\":\n return num * (96 / 25.4);\n default:\n return undefined;\n }\n}\n\n/** Negrito/itálico/sublinhado/tachado por NOME da tag. `style` explícito\n * (aplicado depois, em `applyStyleMarks`) tem prioridade e pode cancelar. */\nfunction applyTagMarks(tag: string, marks: MarkAttrs): MarkAttrs {\n switch (tag) {\n case \"b\":\n case \"strong\":\n return { ...marks, bold: true };\n case \"i\":\n case \"em\":\n return { ...marks, italic: true };\n case \"u\":\n return { ...marks, underline: true };\n case \"s\":\n case \"strike\":\n case \"del\":\n return { ...marks, strike: true };\n default:\n return marks;\n }\n}\n\nfunction parseStyle(styleAttr: string | null): Record<string, string> {\n const out: Record<string, string> = {};\n if (!styleAttr) return out;\n for (const decl of styleAttr.split(\";\")) {\n const idx = decl.indexOf(\":\");\n if (idx === -1) continue;\n const prop = decl.slice(0, idx).trim().toLowerCase();\n const val = decl.slice(idx + 1).trim();\n if (prop) out[prop] = val;\n }\n return out;\n}\n\n/**\n * Aplica overrides de `style` inline por cima das marcas herdadas/de tag.\n * A regra central: `font-weight`/`font-style`/`text-decoration` explícitos no\n * style VENCEM o nome da tag — é o que impede o wrapper `<b style=\"font-\n * weight:normal\">` do Google Docs de deixar o documento inteiro em negrito.\n *\n * `fontFamily` é intencionalmente NUNCA lida daqui — descartada sempre, junto\n * com `w:rFonts` no import-docx, pelo mesmo motivo (Arial-only). `fontSize` É\n * lida e normalizada para `pt` via `parseFontSizePt` — ver o comentário no\n * topo do arquivo.\n */\nfunction applyStyleMarks(styleAttr: string | null, marks: MarkAttrs): MarkAttrs {\n const decls = parseStyle(styleAttr);\n if (Object.keys(decls).length === 0) return marks;\n const next: MarkAttrs = { ...marks };\n\n if (\"font-weight\" in decls) {\n const v = decls[\"font-weight\"].toLowerCase();\n if (v === \"bold\" || /^[5-9]00$/.test(v)) next.bold = true;\n else if (v === \"normal\" || /^[1-4]00$/.test(v)) delete next.bold;\n }\n\n if (\"font-style\" in decls) {\n const v = decls[\"font-style\"].toLowerCase();\n if (v === \"italic\" || v === \"oblique\") next.italic = true;\n else if (v === \"normal\") delete next.italic;\n }\n\n const deco = decls[\"text-decoration-line\"] ?? decls[\"text-decoration\"];\n if (deco !== undefined) {\n const v = deco.toLowerCase();\n delete next.underline;\n delete next.strike;\n if (v.includes(\"underline\")) next.underline = true;\n if (v.includes(\"line-through\")) next.strike = true;\n }\n\n if (\"color\" in decls) {\n const css = normalizeCssColor(decls.color);\n if (css && !isBlack(css)) next.color = css;\n else delete next.color;\n }\n\n const bg = decls[\"background-color\"] ?? decls.background;\n if (bg !== undefined) {\n const css = normalizeCssColor(bg);\n if (css && css !== \"#ffffff\") next.highlight = css;\n else delete next.highlight;\n }\n\n if (\"font-size\" in decls) {\n const pt = parseFontSizePt(decls[\"font-size\"]);\n // Só troca quando dá para resolver. Quando NÃO dá (em/%/larger/... ou\n // valor absurdo), o local não sobrescreve — mantém o que já veio herdado\n // do ancestral em vez de apagar um tamanho que já tínhamos resolvido\n // corretamente. Não é o mesmo padrão de `color`/`highlight` (que apagam\n // no valor não resolvível): lá um valor ausente/errado é positivamente\n // \"sem cor\"; aqui um `em`/`%` não resolvível não é \"sem tamanho\", é\n // \"tamanho relativo que não sabemos calcular\" — silenciar é melhor que\n // apagar informação que já tínhamos.\n if (pt != null) next.fontSize = formatFontSizePt(pt);\n }\n\n return next;\n}\n\n/** `font-size` do CSS → pt, ou `undefined` quando a unidade depende do\n * contexto de herança do documento de origem (que não temos) ou o valor é\n * absurdo. `px`/`in` convertem para pt; `pt` passa direto; `em`/`rem`/`%`/\n * palavras-chave relativas (`larger`/`smaller`) e `medium`/`small`/`large`\n * (que também dependem do tamanho herdado) são ignorados de propósito — ver\n * o comentário no topo do arquivo. */\nfunction parseFontSizePt(raw: string): number | undefined {\n const v = raw.trim().toLowerCase();\n const m = /^(-?[\\d.]+)\\s*(pt|px|in)$/.exec(v);\n if (!m) return undefined;\n const num = Number.parseFloat(m[1]);\n if (!Number.isFinite(num)) return undefined;\n let pt: number;\n switch (m[2]) {\n case \"pt\":\n pt = num;\n break;\n case \"px\":\n pt = num * 0.75;\n break;\n case \"in\":\n pt = num * 72;\n break;\n default:\n return undefined;\n }\n // O bound usa o valor JÁ ARREDONDADO pra uma casa decimal — é isso que vai\n // pro `formatFontSizePt`. Sem isso, algo como `0.04pt` passa no `pt > 0`\n // aqui mas arredonda pra `\"0pt\"` lá na frente, violando a regra de \"≤0 não\n // emite\" por trás do arredondamento em vez de antes dele.\n const rounded = Math.round(pt * 10) / 10;\n if (rounded <= 0 || rounded > 200) return undefined;\n return rounded;\n}\n\n/** No máximo uma casa decimal, sem zero à toa: `37.5pt`, `11pt` — nunca\n * `37.50pt` nem `11.0pt`. */\nfunction formatFontSizePt(pt: number): string {\n const rounded = Math.round(pt * 10) / 10;\n const str = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);\n return `${str}pt`;\n}\n\nconst NAMED_COLORS: Record<string, string> = {\n black: \"#000000\",\n white: \"#ffffff\",\n red: \"#ff0000\",\n green: \"#008000\",\n blue: \"#0000ff\",\n yellow: \"#ffff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n gray: \"#808080\",\n grey: \"#808080\",\n orange: \"#ffa500\",\n purple: \"#800080\",\n};\n\n/** CSS color (hex/rgb(a)/nomeado) → hex normalizado, ou `undefined` quando\n * transparente/inválido/palavra-chave que não representa cor (inherit...). */\nfunction normalizeCssColor(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n const v = raw.trim().toLowerCase();\n if (!v || v === \"transparent\" || v === \"none\" || v === \"inherit\" || v === \"initial\") return undefined;\n if (v.startsWith(\"#\")) {\n let hex = v.slice(1);\n if (hex.length === 3) hex = hex.split(\"\").map((c) => c + c).join(\"\");\n return /^[0-9a-f]{6}$/.test(hex) ? `#${hex}` : undefined;\n }\n const rgbMatch = /^rgba?\\(([^)]+)\\)$/.exec(v);\n if (rgbMatch) {\n const parts = rgbMatch[1].split(\",\").map((s) => s.trim());\n if (parts.length < 3) return undefined;\n const alpha = parts[3] !== undefined ? Number.parseFloat(parts[3]) : 1;\n if (alpha === 0) return undefined;\n const toHex = (n: string) => {\n const num = Math.max(0, Math.min(255, Number.parseInt(n, 10) || 0));\n return num.toString(16).padStart(2, \"0\");\n };\n return `#${toHex(parts[0])}${toHex(parts[1])}${toHex(parts[2])}`;\n }\n return NAMED_COLORS[v];\n}\n\nfunction isBlack(hex: string): boolean {\n return hex.toLowerCase() === \"#000000\";\n}\n\nfunction readAlign(el: Element): AlignValue | undefined {\n const style = parseStyle(el.getAttribute(\"style\"));\n const v = style[\"text-align\"];\n if (!v) return undefined;\n switch (v.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(MAX_LIST_LEVEL, Math.trunc(n)));\n}\n\n/**\n * Normaliza `listLevel` de cada sequência CONTÍGUA de blocos `listItem` em\n * `blocks`, subtraindo o nível mínimo da sequência — o primeiro item da\n * colagem sempre acaba em nível 0, e as profundidades relativas entre os\n * itens são preservadas (ver I4 no comentário de `htmlToSlice`).\n *\n * Só desloca sequências com MAIS de um item. Um item sozinho não desloca:\n * o defeito de paginação que motiva esta normalização (`topLevelItemRanges`\n * em `Editor.tsx` só abrindo range em `level === 0`) exige pelo menos dois\n * itens pra se manifestar — com um item só, `topLevelItemRanges` devolve `[]`\n * e `renderListFragment` cai no fallback `renderTopLevel` (sem perda nem\n * duplicação). Deslocar um item sozinho SEMPRE daria nível 0 (o mínimo de um\n * conjunto de um elemento é o próprio elemento), apagando o nível real que\n * `parseWordListLevel` calculou a partir de `levelN`/`margin-left` — o que\n * destruiria informação sem nenhum ganho, porque o bug que isso fecha não\n * existe nesse caso.\n */\nfunction normalizeListLevels(blocks: SerializedBlock[]): void {\n let i = 0;\n while (i < blocks.length) {\n if (blocks[i].type !== \"listItem\") {\n i++;\n continue;\n }\n let j = i + 1;\n while (j < blocks.length && blocks[j].type === \"listItem\") j++;\n if (j - i > 1) {\n let min = Number.POSITIVE_INFINITY;\n for (let k = i; k < j; k++) {\n const lvl = (blocks[k].attrs.listLevel as number | undefined) ?? 0;\n if (lvl < min) min = lvl;\n }\n if (min > 0) {\n for (let k = i; k < j; k++) {\n const lvl = (blocks[k].attrs.listLevel as number | undefined) ?? 0;\n blocks[k].attrs.listLevel = clampLevel(lvl - min);\n }\n }\n }\n i = j;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Delta helpers\n// ---------------------------------------------------------------------------\n\nfunction pushOp(ops: DeltaOp[], text: string, marks: MarkAttrs): void {\n const hasMarks = Object.keys(marks).length > 0;\n ops.push(hasMarks ? { insert: text, attributes: { ...marks } } : { insert: text });\n}\n\n/** HTML colapsa espaço/tab/quebra-de-linha ASCII em um único espaço; `&nbsp;`\n * (já decodificado pelo DOMParser em U+00A0) é preservado literalmente — NÃO\n * vira espaço normal (I2). \"Nome:&nbsp;&nbsp;…&nbsp;Turma:\" (a linha mais\n * comum de uma prova) e os spans `mso-tab-count` do Word (corridas de\n * `&nbsp;`) dependem disso: colapsar U+00A0 pra espaço ASCII destrói o\n * espaçamento intencional do cabeçalho. As regexes de colapso\n * (`collapseRepeatedSpaces`) e trim (`trimEdgeWhitespace`) abaixo são\n * ASCII-only de propósito — `/ {2,}/` e `/[ \\t\\r\\n]+/` — então o nbsp passa\n * por elas intacto. */\nfunction normalizeWhitespace(raw: string): string {\n return raw.replace(/[ \\t\\r\\n]+/g, \" \");\n}\n\n/** Remove espaço em branco nas bordas do bloco — HTML não renderiza\n * espaço/quebra de linha logo após abrir ou logo antes de fechar um bloco. */\nfunction trimEdgeWhitespace(ops: DeltaOp[]): DeltaOp[] {\n let out = ops;\n if (out.length > 0 && typeof out[0].insert === \"string\") {\n const trimmed = out[0].insert.replace(/^ +/, \"\");\n if (trimmed.length === 0) out = out.slice(1);\n else if (trimmed !== out[0].insert) out = [{ ...out[0], insert: trimmed }, ...out.slice(1)];\n }\n if (out.length > 0 && typeof out[out.length - 1].insert === \"string\") {\n const last = out[out.length - 1];\n const trimmed = (last.insert as string).replace(/ +$/, \"\");\n if (trimmed.length === 0) out = out.slice(0, -1);\n else if (trimmed !== last.insert) out = [...out.slice(0, -1), { ...last, insert: trimmed }];\n }\n return out;\n}\n\nfunction mergeAdjacentOps(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push({ ...op });\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n\nfunction deltaToText(delta: DeltaOp[]): string {\n let s = \"\";\n for (const op of delta) if (typeof op.insert === \"string\") s += op.insert;\n return s;\n}\n\nfunction makeBlock(type: BlockType, delta: DeltaOp[], attrs: BlockAttrs): SerializedBlock {\n return { type, text: deltaToText(delta), delta, attrs };\n}\n","/**\n * Max display width (CSS px) when inserting an image, no matter the source\n * (drag/drop, file picker, `insertImageFromFile`, or an embed parsed from\n * pasted HTML in `htmlToSlice`). Matches the A4 content area, so an image\n * never overflows the page margin. Larger sources are scaled down keeping\n * aspect ratio.\n *\n * Shared constant so every insertion path clamps the same way — without it,\n * an image pasted from HTML (which carries its own declared width/height)\n * could overflow while the exact same image dropped as a file would be\n * clamped, a visible asymmetry between two paths that should behave\n * identically.\n */\nexport const MAX_INSERT_WIDTH = 600;\n","import { isImageEmbed, type ClipboardSlice, type DeltaOp } from \"@sofereditor/core\";\n\n/**\n * Alguma imagem do slice ainda está embutida como `data:` — candidata a subir\n * pro storage (`uploadImage`) antes de `insertSlice`. Usado pelo handler de\n * paste em `Editor.tsx` para decidir se vale a pena esperar por upload\n * (assíncrono) ou se pode inserir o slice na hora (o caso comum — colagem sem\n * imagem nenhuma).\n */\nexport function sliceHasDataImageEmbeds(slice: ClipboardSlice): boolean {\n return slice.blocks.some((b) =>\n b.delta.some((op) => isImageEmbed(op.insert) && op.insert.src.startsWith(\"data:image/\")),\n );\n}\n\n/**\n * Troca cada embed de imagem com `src` `data:` pela URL devolvida por\n * `upload`, num slice novo (não muta `slice`).\n *\n * `htmlToSlice` é deliberadamente pura (ver o comentário lá) e só emite\n * `data:` — subir pro storage é responsabilidade de quem tem o contexto do\n * editor, não do parser de HTML. Uma imagem do Google Docs em base64 tem\n * ~340KB; gravar isso direto no Y.Doc incha o documento e fura o storage.\n *\n * `upload` ausente (sem `uploadImage` configurado — caso do playground)\n * devolve o slice inalterado, mesmo comportamento que `insertImageFromFile`\n * já tem hoje.\n *\n * Falha de upload de UMA imagem não aborta a colagem inteira: mantém o\n * `data:` daquela imagem específica e segue para as outras. Registra no\n * console — falha silenciosa em produção seria pior que o log.\n */\nexport async function resolvePastedImageUploads(\n slice: ClipboardSlice,\n upload: ((file: File) => Promise<string>) | undefined,\n): Promise<ClipboardSlice> {\n if (!upload) return slice;\n const blocks = await Promise.all(\n slice.blocks.map(async (block) => {\n let changed = false;\n const delta: DeltaOp[] = await Promise.all(\n block.delta.map(async (op): Promise<DeltaOp> => {\n if (!isImageEmbed(op.insert) || !op.insert.src.startsWith(\"data:image/\")) return op;\n try {\n const file = await dataUrlToFile(op.insert.src);\n const newSrc = await upload(file);\n changed = true;\n return { ...op, insert: { ...op.insert, src: newSrc } };\n } catch (err) {\n console.error(\n \"[sofereditor] falha ao subir imagem colada para o storage — mantendo a imagem embutida (data:) nesta colagem\",\n err,\n );\n return op;\n }\n }),\n );\n return changed ? { ...block, delta } : block;\n }),\n );\n return { ...slice, blocks };\n}\n\n/** `data:<mime>[;base64],<...>` → `File`, com nome/extensão derivados do MIME.\n *\n * Decodifica via `fetch(dataUrl).then(r => r.blob())` em vez de um regex +\n * `atob` manual. Dois motivos, o segundo é o que importa:\n *\n * - `fetch` é nativo e trata as DUAS formas de `data:` URL — base64 (o caso\n * comum de imagem grande, tipo Google Docs) e URL-encoded/percent-encoded\n * (`data:image/svg+xml,%3Csvg...`, o padrão dominante de ícone inline em\n * sites). O regex anterior só reconhecia `;base64,`; contra uma forma\n * URL-encoded ele simplesmente não casava, e o MIME caía num default\n * inventado (`image/png`) com um `File` de 0 bytes — silencioso, sem\n * lançar, sem proteção do try/catch do chamador. Ver bug B1.\n * - o MIME do `File` sai do `Blob` que o próprio `fetch` decodificou, nunca\n * de um valor chutado — um jpeg do Google Docs enviado como `.png` (ou\n * pior, um SVG rotulado `image/png`) é uma falha de upload plausível em\n * APIs que validam extensão x content-type no servidor.\n *\n * Um `data:` malformado (ou que o `fetch` não consiga decodificar) REJEITA a\n * promise — não inventa um `File` vazio. O chamador (`resolvePastedImageUploads`)\n * já captura isso e preserva o `data:` original na colagem em vez de subir lixo. */\nexport async function dataUrlToFile(dataUrl: string): Promise<File> {\n const blob = await fetch(dataUrl).then((r) => r.blob());\n const mime = blob.type;\n return new File([blob], `imagem-colada-${Date.now()}.${extensionForMime(mime)}`, { type: mime });\n}\n\nconst MIME_EXTENSIONS: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/jpg\": \"jpg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n \"image/bmp\": \"bmp\",\n \"image/svg+xml\": \"svg\",\n};\n\nfunction extensionForMime(mime: string): string {\n return MIME_EXTENSIONS[mime.toLowerCase()] ?? \"png\";\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useState,\n type CSSProperties,\n type JSX,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport { getFragmentForOffset } from \"./dom-bridge\";\n\ninterface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\ninterface BehindImageSelectAffordanceProps {\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n rootRef: RefObject<HTMLDivElement>;\n /** Select the embed (shows the resize/move overlay). */\n onSelect: () => void;\n}\n\nconst ACCENT = \"#3b82f6\";\n\n/**\n * Hover affordance to select a `behind` image (z-index:-1) even when text sits\n * on top of it. The image's own box doesn't receive the click there — the text\n * /block does — so the overlay can't appear from a plain click. On hover we draw\n * a dashed outline (pointer-events:none, so it never steals a text click) plus a\n * clickable badge that selects the image. Lives in the un-clipped `.ed-root`\n * layer, positioned over the image (same measure pattern as ImageResizeOverlay).\n * Inline styles only — the package ships no CSS.\n */\nexport function BehindImageSelectAffordance({\n blockIndex,\n offset,\n cellIndex,\n rootRef,\n onSelect,\n}: BehindImageSelectAffordanceProps): JSX.Element | null {\n const [rect, setRect] = useState<Rect | null>(null);\n\n const locateImage = useCallback((): HTMLImageElement | null => {\n const root = rootRef.current;\n if (!root) return null;\n let container: HTMLElement | null = null;\n if (cellIndex != null) {\n container = root.querySelector<HTMLElement>(\n `[data-block-index=\"${blockIndex}\"] [data-cell-index=\"${cellIndex}\"]`,\n );\n } else {\n container = getFragmentForOffset(root, blockIndex, offset);\n }\n if (!container) return null;\n const fragmentStart = container.dataset.fragmentStart\n ? Number.parseInt(container.dataset.fragmentStart, 10) || 0\n : 0;\n const localOffset = offset - fragmentStart;\n return container.querySelector<HTMLImageElement>(\n `img[data-embed=\"image\"][data-embed-offset=\"${localOffset}\"]`,\n );\n }, [rootRef, blockIndex, offset, cellIndex]);\n\n const measure = useCallback(() => {\n const img = locateImage();\n const root = rootRef.current;\n if (!img || !root) {\n setRect((prev) => (prev === null ? prev : null));\n return;\n }\n const r = img.getBoundingClientRect();\n const rr = root.getBoundingClientRect();\n const next: Rect = {\n top: r.top - rr.top + root.scrollTop,\n left: r.left - rr.left + root.scrollLeft,\n width: r.width,\n height: r.height,\n };\n setRect((prev) =>\n prev &&\n prev.top === next.top &&\n prev.left === next.left &&\n prev.width === next.width &&\n prev.height === next.height\n ? prev\n : next,\n );\n }, [locateImage, rootRef]);\n\n useLayoutEffect(() => {\n measure();\n let cancelled = false;\n const remeasure = () => {\n if (!cancelled) measure();\n };\n const id =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(remeasure)\n : (queueMicrotask(remeasure), 0);\n return () => {\n cancelled = true;\n if (typeof cancelAnimationFrame === \"function\" && typeof id === \"number\" && id > 0) {\n cancelAnimationFrame(id);\n }\n };\n }, [measure]);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onScroll = () => measure();\n window.addEventListener(\"resize\", onScroll);\n root.addEventListener(\"scroll\", onScroll);\n return () => {\n window.removeEventListener(\"resize\", onScroll);\n root.removeEventListener(\"scroll\", onScroll);\n };\n }, [measure, rootRef]);\n\n if (!rect) return null;\n\n const outlineStyle: CSSProperties = {\n position: \"absolute\",\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n border: `1.5px dashed ${ACCENT}`,\n borderRadius: 2,\n boxSizing: \"border-box\",\n pointerEvents: \"none\",\n zIndex: 4,\n };\n\n const badgeStyle: CSSProperties = {\n position: \"absolute\",\n top: rect.top - 2,\n left: rect.left - 2,\n width: 24,\n height: 24,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: ACCENT,\n color: \"#fff\",\n borderRadius: 4,\n cursor: \"pointer\",\n pointerEvents: \"auto\",\n boxShadow: \"0 1px 3px rgba(0,0,0,0.3)\",\n zIndex: 5,\n };\n\n const onBadgeDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return;\n e.preventDefault();\n e.stopPropagation();\n onSelect();\n };\n\n return (\n <>\n <div style={outlineStyle} contentEditable={false} aria-hidden />\n <div\n style={badgeStyle}\n contentEditable={false}\n onPointerDown={onBadgeDown}\n role=\"button\"\n aria-label=\"Selecionar imagem\"\n title=\"Selecionar imagem\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M5 9l-3 3 3 3M9 5l3-3 3 3M15 19l-3 3-3-3M19 9l3 3-3 3M2 12h20M12 2v20\" />\n </svg>\n </div>\n </>\n );\n}\n","import { type CSSProperties, type JSX } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { MOD_SYMBOL } from \"./platform\";\n\ninterface LinkHoverTooltipProps {\n href: string;\n /** Link bounding box in viewport coordinates (from getBoundingClientRect). */\n rect: { top: number; left: number; bottom: number; width: number };\n}\n\nconst MAX_WIDTH = 360;\nconst GAP = 6;\n\n/**\n * Small floating tooltip shown while hovering a link in the editor. Displays the\n * link URL plus a hint that Cmd/Ctrl+click opens it. Lives at `position: fixed`\n * (viewport coords from the link's rect) so it isn't clipped by the paginated\n * page boxes; `pointerEvents: none` so it never steals the hover/click from the\n * link underneath. The package ships no CSS — all styling is inline.\n */\nexport function LinkHoverTooltip({ href, rect }: LinkHoverTooltipProps): JSX.Element {\n // Prefer above the link; flip below when too close to the viewport top.\n const above = rect.top > 48;\n const viewportW = typeof window !== \"undefined\" ? window.innerWidth : MAX_WIDTH + 16;\n const left = Math.max(8, Math.min(rect.left, viewportW - MAX_WIDTH - 8));\n\n const style: CSSProperties = {\n position: \"fixed\",\n left,\n top: above ? rect.top - GAP : rect.bottom + GAP,\n transform: above ? \"translateY(-100%)\" : undefined,\n maxWidth: MAX_WIDTH,\n background: \"#1f2937\",\n color: \"#fff\",\n fontSize: 12,\n lineHeight: 1.4,\n padding: \"6px 9px\",\n borderRadius: 6,\n boxShadow: \"0 4px 14px rgba(0,0,0,0.28)\",\n zIndex: 60,\n pointerEvents: \"none\",\n };\n\n const urlStyle: CSSProperties = {\n display: \"block\",\n maxWidth: MAX_WIDTH - 18,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n fontWeight: 600,\n };\n\n const hintStyle: CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n marginTop: 3,\n fontSize: 11,\n opacity: 0.85,\n };\n\n // Keycap look (the package ships no CSS): a small key-shaped chip around the\n // modifier glyph and the mouse-click glyph.\n const keycapStyle: CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minWidth: 16,\n height: 16,\n padding: \"0 4px\",\n fontSize: 11,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n background: \"rgba(255,255,255,0.16)\",\n border: \"1px solid rgba(255,255,255,0.4)\",\n borderRadius: 4,\n boxShadow: \"inset 0 -1px 0 rgba(0,0,0,0.25)\",\n };\n\n // Render into <body> so `position: fixed` stays viewport-relative even if the\n // editor is nested under a transformed/filtered ancestor (which would turn it\n // into the containing block for fixed elements). SSR-safe: no body, no portal.\n if (typeof document === \"undefined\") return <></>;\n\n return createPortal(\n <div style={style} contentEditable={false} aria-hidden role=\"tooltip\">\n <span style={urlStyle}>{href}</span>\n <span style={hintStyle}>\n <kbd style={keycapStyle}>{MOD_SYMBOL}</kbd>\n <span style={{ opacity: 0.8 }}>+ clique para abrir</span>\n </span>\n </div>,\n document.body,\n );\n}\n","/**\n * Platform detection for keyboard-modifier conventions. On macOS the \"command\"\n * modifier is ⌘ (`metaKey`); elsewhere it's Ctrl (`ctrlKey`). Computed once.\n */\nexport const IS_MAC: boolean = (() => {\n if (typeof navigator === \"undefined\") return false;\n // NB: case-INSENSITIVE. Chrome's `navigator.userAgentData.platform` returns\n // \"macOS\" (lowercase m), while `navigator.platform` returns \"MacIntel\" — a\n // `/Mac/` (case-sensitive) test silently missed \"macOS\", so Macs were treated\n // as non-Mac (showed \"Ctrl\", and Cmd+click didn't open).\n const platform =\n (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ||\n navigator.platform ||\n navigator.userAgent ||\n \"\";\n return /mac|iphone|ipad|ipod/i.test(platform);\n})();\n\n/** Text label for the platform's primary command modifier (\"Cmd\" / \"Ctrl\"). */\nexport const MOD_LABEL: string = IS_MAC ? \"Cmd\" : \"Ctrl\";\n\n/**\n * Keyboard GLYPH for the platform's command modifier: ⌘ (Command) on macOS,\n * ⌃ (Control) elsewhere. Used for keycap-style hints instead of the text label.\n */\nexport const MOD_SYMBOL: string = IS_MAC ? \"⌘\" : \"⌃\";\n\n/** True when the platform's \"open in new tab\" modifier is held during a click. */\nexport function hasOpenModifier(e: { metaKey: boolean; ctrlKey: boolean }): boolean {\n return IS_MAC ? e.metaKey : e.ctrlKey;\n}\n","import { createContext, useContext, type JSX, type ReactNode } from \"react\";\nimport { LinkDialog } from \"./LinkDialog\";\nimport { PageConfigDialog } from \"./PageConfigDialog\";\nimport { ImageCaptionDialog } from \"./ImageCaptionDialog\";\nimport { FormulaDialog } from \"./FormulaDialog\";\nimport type { UseEditorResult } from \"./useEditor\";\n\nconst EditorContext = createContext<UseEditorResult | null>(null);\n\nexport interface EditorProviderProps {\n editor: UseEditorResult;\n children: ReactNode;\n /**\n * De onde servir as 20 fontes .woff2 do MathLive (296 KB). Sem elas o\n * campo desenha com fonte do sistema — feio e SILENCIOSO. O caminho muda\n * por app por causa do base path, então é o app que sabe.\n */\n mathliveFontsDirectory?: string;\n}\n\nexport function EditorProvider({\n editor,\n children,\n mathliveFontsDirectory,\n}: EditorProviderProps): JSX.Element {\n return (\n <EditorContext.Provider value={editor}>\n {children}\n <LinkDialog />\n <PageConfigDialog />\n <ImageCaptionDialog />\n <FormulaDialog fontsDirectory={mathliveFontsDirectory} />\n </EditorContext.Provider>\n );\n}\n\nexport function useEditorContext(): UseEditorResult {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditorContext must be used inside an <EditorProvider>\");\n }\n return ctx;\n}\n","import {\n useEffect,\n useRef,\n useState,\n type FormEvent,\n type JSX,\n} from \"react\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\n\n/**\n * Modal dialog for entering a link URL. Replaces the bare `window.prompt`\n * previously wired to Cmd+K and the toolbar link button. Renders only when\n * `editor.linkRequest` is set; consumes that state via `resolveLinkRequest`.\n *\n * Uses the native `<dialog>` element for focus trapping, ESC handling, and the\n * browser-provided backdrop.\n */\nexport function LinkDialog(): JSX.Element | null {\n const { linkRequest, resolveLinkRequest } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [href, setHref] = useState(\"\");\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (linkRequest) {\n setHref(linkRequest.initialHref);\n if (!dialog.open) dialog.showModal();\n // Defer focus + select-all to after the dialog finishes opening.\n queueMicrotask(() => {\n const input = inputRef.current;\n if (input) {\n input.focus();\n input.select();\n }\n });\n } else if (dialog.open) {\n dialog.close();\n }\n }, [linkRequest]);\n\n if (!linkRequest) return null;\n\n const onSubmit = (e: FormEvent) => {\n e.preventDefault();\n resolveLinkRequest(href.trim());\n };\n\n const onRemove = () => {\n // Empty string signals \"remove the link mark\".\n resolveLinkRequest(\"\");\n };\n\n const onCancel = () => {\n resolveLinkRequest(null);\n };\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-link-dialog\"\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onSubmit} className=\"ed-link-dialog-form\">\n <div className=\"ed-link-dialog-row\">\n <label htmlFor=\"ed-link-href\">URL</label>\n <input\n id=\"ed-link-href\"\n ref={inputRef}\n type=\"url\"\n inputMode=\"url\"\n value={href}\n onChange={(e) => setHref(e.target.value)}\n placeholder=\"https://exemplo.com\"\n autoComplete=\"off\"\n />\n </div>\n <div className=\"ed-link-dialog-actions\">\n {linkRequest.initialHref ? (\n <button type=\"button\" onClick={onRemove} className=\"ed-link-dialog-remove\">\n Remover link\n </button>\n ) : (\n <span />\n )}\n <div className=\"ed-link-dialog-confirm\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" className=\"ed-link-dialog-apply\">\n Aplicar\n </button>\n </div>\n </div>\n </form>\n </dialog>\n );\n}\n","import type { CSSProperties } from \"react\";\n\n/**\n * Inline centering for the editor's native `<dialog showModal()>` modals\n * (link / caption / page-config). The package ships no CSS, so it can't rely on\n * the consumer keeping the UA `dialog { margin: auto }` centering intact — some\n * apps (Tailwind/HeroUI resets, custom `dialog { margin: 0 }`) override it and\n * the modal then opens in the top-left corner. `position: fixed` + translate is\n * reset-proof; `maxHeight`/`overflow` keep a tall dialog from spilling\n * off-screen when translated.\n */\nexport const DIALOG_CENTER_STYLE: CSSProperties = {\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n margin: 0,\n maxHeight: \"90vh\",\n overflow: \"auto\",\n};\n","import { useEffect, useMemo, useRef, useState, type FormEvent, type JSX } from \"react\";\nimport {\n MARGIN_PRESETS,\n PAGE_PRESETS,\n detectPreset,\n mmToPx,\n pxToMm,\n type PageSettings,\n type PagePreset,\n} from \"@sofereditor/core\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\n\ntype MarginPreset = \"normal\" | \"narrow\" | \"wide\" | \"custom\";\n\n/**\n * Modal dialog to configure page size and margins. Mirrors `LinkDialog` —\n * driven by `editor.pageConfigRequest`. On apply, writes via\n * `editor.setPageSettings`; on cancel, no write.\n */\nexport function PageConfigDialog(): JSX.Element | null {\n const { pageConfigRequest, resolvePageConfigRequest, setPageSettings } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n\n // Form state. Sizes are kept in mm for user-facing inputs; we convert to px\n // when applying. Width/height pair determines the size preset.\n const [sizePreset, setSizePreset] = useState<PagePreset>(\"a4\");\n const [widthMm, setWidthMm] = useState(210);\n const [heightMm, setHeightMm] = useState(297);\n const [marginPreset, setMarginPreset] = useState<MarginPreset>(\"normal\");\n const [marginTopMm, setMarginTopMm] = useState(25.4);\n const [marginBottomMm, setMarginBottomMm] = useState(25.4);\n const [marginLeftMm, setMarginLeftMm] = useState(25.4);\n const [marginRightMm, setMarginRightMm] = useState(25.4);\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (pageConfigRequest) {\n hydrateFromSettings(pageConfigRequest.initial, {\n setSizePreset,\n setWidthMm,\n setHeightMm,\n setMarginPreset,\n setMarginTopMm,\n setMarginBottomMm,\n setMarginLeftMm,\n setMarginRightMm,\n });\n if (!dialog.open) dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [pageConfigRequest]);\n\n // When the user picks a size preset, snap width/height back to the canonical\n // values. \"custom\" leaves the user free to edit.\n useEffect(() => {\n if (sizePreset === \"custom\") return;\n const p = PAGE_PRESETS[sizePreset];\n setWidthMm(roundMm(pxToMm(p.width)));\n setHeightMm(roundMm(pxToMm(p.height)));\n }, [sizePreset]);\n\n // When the user picks a margin preset, set all 4 margins to that value.\n useEffect(() => {\n if (marginPreset === \"custom\") return;\n const px = MARGIN_PRESETS[marginPreset];\n const mm = roundMm(pxToMm(px));\n setMarginTopMm(mm);\n setMarginBottomMm(mm);\n setMarginLeftMm(mm);\n setMarginRightMm(mm);\n }, [marginPreset]);\n\n const customWidth = sizePreset === \"custom\";\n\n // Live preview of the dimensions in px so the test/QA loop is easy to read.\n const previewPx = useMemo(\n () => ({\n width: Math.round(mmToPx(widthMm)),\n height: Math.round(mmToPx(heightMm)),\n }),\n [widthMm, heightMm],\n );\n\n if (!pageConfigRequest) return null;\n\n const onApply = (e: FormEvent) => {\n e.preventDefault();\n const next: Partial<PageSettings> = {\n width: mmToPx(widthMm),\n height: mmToPx(heightMm),\n marginTop: mmToPx(marginTopMm),\n marginBottom: mmToPx(marginBottomMm),\n marginLeft: mmToPx(marginLeftMm),\n marginRight: mmToPx(marginRightMm),\n };\n setPageSettings(next);\n resolvePageConfigRequest(true);\n };\n const onCancel = () => resolvePageConfigRequest(false);\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-page-config-dialog\"\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onApply} className=\"ed-page-config-form\">\n <h2>Configurar página</h2>\n\n <fieldset>\n <legend>Tamanho</legend>\n <div className=\"ed-page-config-row\">\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"a4\"}\n onChange={() => setSizePreset(\"a4\")}\n />{\" \"}\n A4 (210 × 297 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"letter\"}\n onChange={() => setSizePreset(\"letter\")}\n />{\" \"}\n Carta (216 × 279 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"oficio\"}\n onChange={() => setSizePreset(\"oficio\")}\n />{\" \"}\n Ofício (216 × 330 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"custom\"}\n onChange={() => setSizePreset(\"custom\")}\n />{\" \"}\n Personalizado\n </label>\n </div>\n <div className=\"ed-page-config-row\">\n <label>\n Largura (mm){\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={50}\n max={1000}\n value={widthMm}\n onChange={(e) => setWidthMm(Number(e.target.value))}\n disabled={!customWidth}\n />\n </label>\n <label>\n Altura (mm){\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={50}\n max={1000}\n value={heightMm}\n onChange={(e) => setHeightMm(Number(e.target.value))}\n disabled={!customWidth}\n />\n </label>\n <span className=\"ed-page-config-preview\">\n {previewPx.width} × {previewPx.height} px\n </span>\n </div>\n </fieldset>\n\n <fieldset>\n <legend>Margens</legend>\n <div className=\"ed-page-config-row\">\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"normal\"}\n onChange={() => setMarginPreset(\"normal\")}\n />{\" \"}\n Normal (25,4 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"narrow\"}\n onChange={() => setMarginPreset(\"narrow\")}\n />{\" \"}\n Estreita (12,7 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"wide\"}\n onChange={() => setMarginPreset(\"wide\")}\n />{\" \"}\n Larga (50,8 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"custom\"}\n onChange={() => setMarginPreset(\"custom\")}\n />{\" \"}\n Personalizada\n </label>\n </div>\n <div className=\"ed-page-config-row\">\n <label>\n Topo{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginTopMm}\n onChange={(e) => {\n setMarginTopMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n <label>\n Inferior{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginBottomMm}\n onChange={(e) => {\n setMarginBottomMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n <label>\n Esquerda{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginLeftMm}\n onChange={(e) => {\n setMarginLeftMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n <label>\n Direita{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginRightMm}\n onChange={(e) => {\n setMarginRightMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n </div>\n </fieldset>\n\n <div className=\"ed-page-config-actions\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" className=\"ed-page-config-apply\">\n Aplicar\n </button>\n </div>\n </form>\n </dialog>\n );\n}\n\nfunction roundMm(mm: number): number {\n return Math.round(mm * 10) / 10;\n}\n\nfunction hydrateFromSettings(\n s: PageSettings,\n setters: {\n setSizePreset: (v: PagePreset) => void;\n setWidthMm: (v: number) => void;\n setHeightMm: (v: number) => void;\n setMarginPreset: (v: MarginPreset) => void;\n setMarginTopMm: (v: number) => void;\n setMarginBottomMm: (v: number) => void;\n setMarginLeftMm: (v: number) => void;\n setMarginRightMm: (v: number) => void;\n },\n): void {\n const preset = detectPreset(s.width, s.height);\n setters.setSizePreset(preset);\n setters.setWidthMm(roundMm(pxToMm(s.width)));\n setters.setHeightMm(roundMm(pxToMm(s.height)));\n const top = roundMm(pxToMm(s.marginTop));\n const bottom = roundMm(pxToMm(s.marginBottom));\n const left = roundMm(pxToMm(s.marginLeft));\n const right = roundMm(pxToMm(s.marginRight));\n setters.setMarginTopMm(top);\n setters.setMarginBottomMm(bottom);\n setters.setMarginLeftMm(left);\n setters.setMarginRightMm(right);\n setters.setMarginPreset(detectMarginPreset(s));\n}\n\nfunction detectMarginPreset(s: PageSettings): MarginPreset {\n const TOL = 1; // px\n const allEqual =\n Math.abs(s.marginTop - s.marginBottom) <= TOL &&\n Math.abs(s.marginTop - s.marginLeft) <= TOL &&\n Math.abs(s.marginTop - s.marginRight) <= TOL;\n if (!allEqual) return \"custom\";\n for (const [name, px] of Object.entries(MARGIN_PRESETS) as Array<[keyof typeof MARGIN_PRESETS, number]>) {\n if (Math.abs(s.marginTop - px) <= TOL) return name as MarginPreset;\n }\n return \"custom\";\n}\n","import { useEffect, useRef, useState, type FormEvent, type JSX } from \"react\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\nimport type { CaptionAlign } from \"./useEditor\";\n\n/**\n * Modal dialog for entering an image caption. Mirrors `LinkDialog` — driven by\n * `editor.imageCaptionRequest`. Empty input is treated as \"remove caption\".\n *\n * The only configurable property besides the text itself is the caption's\n * horizontal alignment (left / center / right). Word-style figure-caption\n * controls stop here intentionally — see `ImageEmbed.captionAlign`.\n */\nexport function ImageCaptionDialog(): JSX.Element | null {\n const { imageCaptionRequest, resolveImageCaptionRequest } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [caption, setCaption] = useState(\"\");\n const [align, setAlign] = useState<CaptionAlign>(\"center\");\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (imageCaptionRequest) {\n setCaption(imageCaptionRequest.initialCaption);\n setAlign(imageCaptionRequest.initialAlign);\n if (!dialog.open) dialog.showModal();\n queueMicrotask(() => {\n const input = inputRef.current;\n if (input) {\n input.focus();\n input.select();\n }\n });\n } else if (dialog.open) {\n dialog.close();\n }\n }, [imageCaptionRequest]);\n\n if (!imageCaptionRequest) return null;\n\n const onSubmit = (e: FormEvent) => {\n e.preventDefault();\n resolveImageCaptionRequest({ caption, align });\n };\n const onRemove = () => resolveImageCaptionRequest({ caption: \"\", align });\n const onCancel = () => resolveImageCaptionRequest(null);\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-caption-dialog ed-link-dialog\"\n // Centraliza por estilo inline (o pacote não envia CSS): consumidores com\n // reset global de `dialog` (ex.: `margin:0`) quebram a centralização do\n // <dialog> nativo via UA `margin:auto` → o modal abria no canto superior\n // esquerdo. `position:fixed` + translate é à prova de reset.\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onSubmit} className=\"ed-link-dialog-form\">\n <div className=\"ed-link-dialog-row\">\n <label htmlFor=\"ed-caption-input\">Legenda</label>\n <input\n id=\"ed-caption-input\"\n ref={inputRef}\n type=\"text\"\n value={caption}\n onChange={(e) => setCaption(e.target.value)}\n placeholder=\"Ex.: Figura 1 — Mapa do Brasil\"\n autoComplete=\"off\"\n />\n </div>\n <div className=\"ed-link-dialog-row\">\n <span>Alinhamento</span>\n <div className=\"ed-caption-align-group\" role=\"radiogroup\" aria-label=\"Alinhamento da legenda\">\n {([\"left\", \"center\", \"right\"] as const).map((value) => (\n <label key={value} className=\"ed-caption-align-option\">\n <input\n type=\"radio\"\n name=\"caption-align\"\n value={value}\n checked={align === value}\n onChange={() => setAlign(value)}\n />\n {value === \"left\" ? \"Esquerda\" : value === \"center\" ? \"Centro\" : \"Direita\"}\n </label>\n ))}\n </div>\n </div>\n <div className=\"ed-link-dialog-actions\">\n {imageCaptionRequest.initialCaption ? (\n <button type=\"button\" onClick={onRemove} className=\"ed-link-dialog-remove\">\n Remover legenda\n </button>\n ) : (\n <span />\n )}\n <div className=\"ed-link-dialog-confirm\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" className=\"ed-link-dialog-apply\">\n Aplicar\n </button>\n </div>\n </div>\n </form>\n </dialog>\n );\n}\n","import { useCallback, useEffect, useRef, useState, type FormEvent, type JSX } from \"react\";\nimport type { FormulaRender } from \"@sofereditor/math\";\nimport type { MathfieldElement } from \"mathlive\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\nimport { PALETA, paraMathlive, conteudoDoBotao } from \"./formulaSnippet\";\nimport { podeInserir, motivoBloqueio } from \"./formulaGuarda\";\n\n/**\n * Caracteres que a tecla morta de circunflexo do teclado ABNT2 (o brasileiro)\n * produz quando o professor a pressiona e a próxima tecla não aceita acento —\n * `^` seguido de `2`, por exemplo.\n *\n * `U+02C6` é o circunflexo modificador, e `U+0302` o acento combinante. O\n * MathLive trata os dois como texto literal, então digitar \"x\", circunflexo,\n * \"2\" saía **`xˆ2`** — o acento impresso ao lado do x, e o 2 na linha de\n * baixo. Era o caminho mais natural do mundo para escrever \"x ao quadrado\"\n * numa prova, e o único que não funcionava.\n *\n * O `^` ASCII (U+005E) NÃO entra nesta lista: esse o MathLive já trata certo\n * sozinho. O `²` (U+00B2) também já vira `x^{2}` sem ajuda.\n */\nconst CIRCUNFLEXO_DE_TECLA_MORTA = new Set([\"\\u02C6\", \"\\u0302\"]);\n\n/**\n * Modal de fórmula. Espelha `ImageCaptionDialog` — dirigido por\n * `editor.formulaRequest`.\n *\n * O preview usa o MESMO renderer que a inserção vai usar. Se o preview falha,\n * o botão de inserir fica desabilitado: nunca inserir um embed cujo render\n * falhou, senão entra no documento um <img> quebrado.\n */\nexport function FormulaDialog({\n fontsDirectory,\n}: {\n fontsDirectory?: string;\n}): JSX.Element | null {\n const { formulaRequest, resolveFormulaRequest } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const hostRef = useRef<HTMLDivElement>(null);\n const fieldRef = useRef<MathfieldElement | null>(null);\n const [latex, setLatex] = useState(\"\");\n const [display, setDisplay] = useState(false);\n const [abaAtiva, setAbaAtiva] = useState(0);\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (formulaRequest) {\n setLatex(formulaRequest.initialLatex);\n setDisplay(formulaRequest.initialDisplay);\n setAbaAtiva(0);\n if (!dialog.open) dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [formulaRequest]);\n\n // O renderer chega por import dinâmico quando o modal abre. Fica em ref, não\n // em state: trocá-lo não precisa re-renderizar, quem re-renderiza é o preview.\n const rendererRef = useRef<((l: string, d: boolean) => FormulaRender) | null>(null);\n // Construtor do <math-field>. Ref e não state: quem re-renderiza é o campo,\n // montado imperativamente na Task 2.\n const mathfieldCtorRef = useRef<typeof MathfieldElement | null>(null);\n // Renderer dos botões da paleta. Ref e não state pelo mesmo motivo do\n // `mathfieldCtorRef` acima: quem re-renderiza é o `setCarregando(false)`\n // do mesmo `.then()`.\n const markupRef = useRef<((latex: string) => string) | null>(null);\n const [preview, setPreview] = useState<FormulaRender | null>(null);\n const [carregando, setCarregando] = useState(false);\n // Separado de `preview` de propósito: `preview` é limpo pelo efeito de\n // render toda vez que `latex`/`display` mudam (abaixo), então se o erro de\n // carregamento morasse lá ele sumiria assim que o professor digitasse\n // qualquer coisa — com o renderer ainda ausente e o motivo do \"Carregando…\"\n // eterno escondido de novo.\n const [erroCarregando, setErroCarregando] = useState<string | null>(null);\n\n useEffect(() => {\n if (!formulaRequest || rendererRef.current) return;\n setCarregando(true);\n setErroCarregando(null);\n // Import DINÂMICO dos dois: mantém mathjax-full E mathlive fora do\n // bundle principal. Ver o teste mathliveLazy.test.ts — trocar por\n // import estático não dá erro nenhum, só fica lento para todo mundo.\n void Promise.all([import(\"@sofereditor/math\"), import(\"mathlive\")])\n .then(([math, mathlive]) => {\n rendererRef.current = math.renderLatexToSvg;\n mathfieldCtorRef.current = mathlive.MathfieldElement;\n markupRef.current = mathlive.convertLatexToMarkup;\n // São 240 KB de sons de tecla no pacote. Editor de prova não apita.\n // `soundsDirectory` e `fontsDirectory` são ESTÁTICOS na classe, não\n // opções por instância — daí ficarem aqui, uma vez por página, em\n // vez de na montagem imperativa do campo.\n mathlive.MathfieldElement.soundsDirectory = null;\n if (fontsDirectory) {\n mathlive.MathfieldElement.fontsDirectory = fontsDirectory;\n }\n setCarregando(false);\n })\n .catch(() => {\n // Chunk falhou ao carregar — o caso comum é um deploy que trocou o\n // hash dos assets enquanto a prova estava aberta na aba. Sem isto,\n // `carregando` ficava travado em `true` para sempre: modal aberto,\n // \"Carregando o renderizador…\" eterno, botão Inserir desabilitado\n // sem nenhuma explicação e sem saída a não ser Cancelar.\n setCarregando(false);\n setErroCarregando(\n \"Não foi possível carregar o editor de fórmulas. Recarregue a página e tente de novo.\",\n );\n });\n }, [formulaRequest]);\n\n // Sem debounce: o render é síncrono e leva menos de um milissegundo para as\n // fórmulas desta paleta, depois que o módulo já carregou.\n useEffect(() => {\n const render = rendererRef.current;\n if (!render || !latex.trim()) {\n setPreview(null);\n return;\n }\n setPreview(render(latex, display));\n }, [latex, display, carregando]);\n\n // Precisa ser capture (`true` no addEventListener) e chegar antes do\n // \"keyboard sink\" interno do MathLive — medido: o `beforeinput` chega\n // cancelável no <math-field> antes de chegar no sink. Um `inlineShortcuts`\n // com a mesma chave NÃO funciona: o caractere de tecla morta não passa pelo\n // caminho de atalhos.\n const onCircunflexoMorto = useCallback((e: Event) => {\n const ev = e as InputEvent;\n if (!ev.data || !CIRCUNFLEXO_DE_TECLA_MORTA.has(ev.data)) return;\n ev.preventDefault();\n ev.stopPropagation();\n fieldRef.current?.insert(\"^{#?}\", { focus: true });\n }, []);\n\n // Montagem imperativa do <math-field>, não JSX: o elemento não tem tipo em\n // `IntrinsicElements`, o React 18 põe atributo (não propriedade) em custom\n // element, e o construtor só existe depois do import dinâmico resolver —\n // o JSX teria que renderizar condicionalmente um elemento ainda indefinido.\n useEffect(() => {\n const Ctor = mathfieldCtorRef.current;\n const host = hostRef.current;\n if (!Ctor || !host || !formulaRequest) return;\n\n const mf = new Ctor();\n mf.className = \"ed-formula-field\";\n const onInput = () => setLatex(mf.getValue(\"latex\"));\n mf.addEventListener(\"input\", onInput);\n mf.addEventListener(\"beforeinput\", onCircunflexoMorto, true);\n\n // MONTAR PRIMEIRO, CONFIGURAR DEPOIS — a ordem é o ponto, e custou uma\n // tela branca para descobrir.\n //\n // Boa parte dos acessores do MathfieldElement começa com\n // `if (!this._mathfield) throw new Error(\"Mathfield not mounted\")`, e o\n // `_mathfield` só nasce no `connectedCallback`. O `inlineShortcuts` é o\n // pior deles: tem essa guarda no getter E no setter, então a linha\n // `{ ...mf.inlineShortcuts }` abaixo lançava duas vezes.\n //\n // Como isso roda dentro de um efeito do React, o erro não estraga só o\n // modal: derruba a árvore inteira. O professor clicava em \"Inserir\n // fórmula\" e o editor virava tela branca, com a prova aberta.\n //\n // Nenhum teste de unidade pega isto — o jsdom não roda o MathLive. Só\n // navegador de verdade.\n host.appendChild(mf);\n\n // Teclado virtual desligado: decisão do usuário. Em desktop rouba altura\n // e duplica a paleta, em inglês e sem sen/tg/cotg.\n mf.mathVirtualKeyboardPolicy = \"manual\";\n // Atalhos de digitação em português. Sem isto o único atalho rápido é o\n // embutido \"sin\", que imprime \"sin\" na prova.\n mf.inlineShortcuts = {\n ...mf.inlineShortcuts,\n sen: \"\\\\operatorname{sen}\",\n tg: \"\\\\operatorname{tg}\",\n cotg: \"\\\\operatorname{cotg}\",\n // Vírgula decimal. Em modo matemático a vírgula crua é átomo de\n // pontuação e ganha espaço DEPOIS de si: digitar \"3,14\" sai impresso\n // como \"3, 14\" (medido: 4.4ex contra 4.023ex do agrupado). A paleta\n // tem um botão `{,}`, mas ninguém clica num botão para uma tecla que\n // está no teclado — e decimal aparece em toda prova de matemática,\n // física e química.\n //\n // O `after: \"digit\"` é o que separa os dois usos da vírgula, e foi\n // verificado no navegador: \"3,14\" vira `3{,}14` e \"f(x,y)\" continua\n // `f\\left(x,y\\right)`, com a vírgula de lista intacta — que é o\n // comportamento certo, ali ela É pontuação.\n \",\": { after: \"digit\", value: \"{,}\" },\n };\n mf.setValue(formulaRequest.initialLatex);\n // Sem esta linha, editar uma fórmula existente abre com o campo cheio e o\n // botão Inserir DESABILITADO: `latex` continuaria \"\" e o gate leria campo\n // vazio. Lê de volta do campo em vez de reusar `initialLatex` porque o\n // MathLive pode normalizar na entrada, e o estado tem que ser o que o\n // campo realmente tem.\n setLatex(mf.getValue(\"latex\"));\n fieldRef.current = mf;\n queueMicrotask(() => mf.focus());\n\n return () => {\n mf.removeEventListener(\"input\", onInput);\n mf.removeEventListener(\"beforeinput\", onCircunflexoMorto, true);\n mf.remove();\n fieldRef.current = null;\n };\n }, [formulaRequest, carregando]);\n\n if (!formulaRequest) return null;\n\n const markup = markupRef.current;\n const motivo = motivoBloqueio(latex, preview);\n const podeSubmeter = podeInserir(latex, preview);\n\n const onSubmit = (e: FormEvent) => {\n e.preventDefault();\n if (!podeSubmeter) return;\n resolveFormulaRequest({ latex, display });\n };\n const onCancel = () => resolveFormulaRequest(null);\n\n const onPaleta = (snippet: string) => {\n const mf = fieldRef.current;\n if (!mf) return;\n mf.insert(paraMathlive(snippet), { focus: true });\n setLatex(mf.getValue(\"latex\"));\n };\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-formula-dialog ed-link-dialog\"\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onSubmit} className=\"ed-formula-form\">\n <div className=\"ed-formula-abas\" role=\"tablist\">\n {PALETA.map((cat, i) => (\n <button\n key={cat.nome}\n type=\"button\"\n role=\"tab\"\n aria-selected={i === abaAtiva}\n className=\"ed-formula-aba\"\n onClick={() => setAbaAtiva(i)}\n >\n {cat.nome}\n </button>\n ))}\n </div>\n <div\n className=\"ed-formula-paleta\"\n role=\"tabpanel\"\n style={{ gridTemplateColumns: `repeat(${PALETA[abaAtiva].colunas}, 1fr)` }}\n >\n {PALETA[abaAtiva].itens.map((p) => (\n <button\n key={p.label}\n type=\"button\"\n className=\"ed-formula-paleta-btn\"\n title={p.titulo ?? p.label}\n aria-label={p.titulo ?? p.label}\n onClick={() => onPaleta(p.snippet)}\n // Markup vem do `convertLatexToMarkup` do MathLive sobre um\n // snippet ESTÁTICO da nossa PALETA — não é entrada de usuário.\n // `paraMarkup` (não `paraMathlive`, que é para o campo e usa\n // `#?` — inválido para este renderer) troca `{}` por `{□}`\n // só para o ícone: `^{}`/`_{}` crus saem VAZIOS deste\n // renderer (verificado, não deduzido — ver formulaSnippet.ts).\n {...conteudoDoBotao(markup, p.snippet, p.label)}\n />\n ))}\n </div>\n <div ref={hostRef} className=\"ed-formula-host\" />\n <label className=\"ed-formula-display\">\n <input\n type=\"checkbox\"\n checked={display}\n onChange={(e) => setDisplay(e.target.checked)}\n />\n Fórmula em bloco (centralizada, limites acima e abaixo)\n </label>\n <div className=\"ed-formula-preview\" aria-live=\"polite\">\n {carregando ? (\n <span className=\"ed-formula-vazio\">Carregando o editor…</span>\n ) : erroCarregando ? (\n <span className=\"ed-formula-erro\" role=\"alert\">\n {erroCarregando}\n </span>\n ) : motivo ? (\n <span className=\"ed-formula-erro\" role=\"alert\">\n {motivo}\n </span>\n ) : null}\n </div>\n <div className=\"ed-formula-acoes\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" disabled={!podeSubmeter}>\n Inserir\n </button>\n </div>\n </form>\n </dialog>\n );\n}\n","export interface CategoriaPaleta {\n /** Rótulo da aba. */\n nome: string;\n /** 4 para rótulos em palavra, 6 para rótulos em caractere. */\n colunas: 4 | 6;\n itens: readonly { label: string; snippet: string; titulo?: string }[];\n}\n\n/**\n * Paleta do modal de fórmula: 82 itens em 7 categorias, cobrindo do 6º ano ao\n * pré-vestibular, mais física e química de prova.\n *\n * Todos os 82 foram verificados contra o renderer real com\n * `packages: ['base','ams']` + o import por efeito colateral do\n * `AmsConfiguration` — zero falhas. Nenhum exige extensão nova do MathJax.\n * Ao acrescentar um item, rode-o pelo `renderLatexToSvg` antes de commitar.\n *\n * NOTAÇÃO BRASILEIRA: `\\sin` renderiza \"sin\" e `\\tan` renderiza \"tan\". Prova\n * brasileira escreve \"sen\" e \"tg\". Por isso as três funções abaixo usam\n * `\\operatorname{}`. NÃO troque para as formas nativas do LaTeX: a paleta\n * continuaria parecendo certa e a prova impressa sairia errada.\n *\n * `titulo`: nome acessível/tooltip do botão quando `label` é um glifo solto\n * (símbolo, letra grega, seta) e não diz nada sozinho — o `title` do botão\n * usa `titulo ?? label`. Itens com rótulo em palavra (Estruturas) dispensam.\n */\nexport const PALETA: readonly CategoriaPaleta[] = [\n {\n nome: \"Estruturas\",\n colunas: 4,\n itens: [\n { label: \"Fração\", snippet: \"\\\\frac{}{}\" },\n { label: \"Expoente\", snippet: \"^{}\" },\n { label: \"Índice\", snippet: \"_{}\" },\n { label: \"Raiz\", snippet: \"\\\\sqrt{}\" },\n { label: \"Raiz n-ésima\", snippet: \"\\\\sqrt[{}]{}\" },\n { label: \"Somatório\", snippet: \"\\\\sum_{}^{}\" },\n { label: \"Produtório\", snippet: \"\\\\prod_{}^{}\" },\n { label: \"Integral\", snippet: \"\\\\int_{}^{}\" },\n { label: \"Limite\", snippet: \"\\\\lim_{{} \\\\to {}}\" },\n { label: \"Derivada\", snippet: \"\\\\frac{d}{d{}}\" },\n {\n label: \"Matriz 2×2\",\n snippet: \"\\\\begin{pmatrix} {} & {} \\\\\\\\ {} & {} \\\\end{pmatrix}\",\n },\n { label: \"Sistema\", snippet: \"\\\\begin{cases} {} \\\\\\\\ \\\\end{cases}\" },\n { label: \"Parênteses\", snippet: \"\\\\left({}\\\\right)\" },\n { label: \"Binomial\", snippet: \"\\\\binom{}{}\" },\n { label: \"Módulo\", snippet: \"\\\\left|{}\\\\right|\" },\n ],\n },\n {\n nome: \"Símbolos\",\n colunas: 6,\n itens: [\n { label: \"±\", snippet: \"\\\\pm\", titulo: \"mais ou menos\" },\n { label: \"×\", snippet: \"\\\\times\", titulo: \"multiplicação\" },\n { label: \"÷\", snippet: \"\\\\div\", titulo: \"divisão\" },\n { label: \"·\", snippet: \"\\\\cdot\", titulo: \"multiplicação (ponto)\" },\n { label: \"∞\", snippet: \"\\\\infty\", titulo: \"infinito\" },\n { label: \"°\", snippet: \"^\\\\circ\", titulo: \"grau\" },\n { label: \"%\", snippet: \"\\\\%\", titulo: \"por cento\" },\n { label: \",\", snippet: \"{,}\", titulo: \"vírgula decimal\" },\n ],\n },\n {\n nome: \"Relações\",\n colunas: 6,\n itens: [\n { label: \"≠\", snippet: \"\\\\neq\", titulo: \"diferente de\" },\n { label: \"≈\", snippet: \"\\\\approx\", titulo: \"aproximadamente\" },\n { label: \"≡\", snippet: \"\\\\equiv\", titulo: \"idêntico a\" },\n { label: \"≤\", snippet: \"\\\\leq\", titulo: \"menor ou igual a\" },\n { label: \"≥\", snippet: \"\\\\geq\", titulo: \"maior ou igual a\" },\n { label: \"∝\", snippet: \"\\\\propto\", titulo: \"proporcional a\" },\n { label: \"⊥\", snippet: \"\\\\perp\", titulo: \"perpendicular a\" },\n { label: \"∥\", snippet: \"\\\\parallel\", titulo: \"paralelo a\" },\n { label: \"≅\", snippet: \"\\\\cong\", titulo: \"congruente a\" },\n { label: \"∼\", snippet: \"\\\\sim\", titulo: \"semelhante a\" },\n ],\n },\n {\n nome: \"Gregas\",\n colunas: 6,\n itens: [\n { label: \"α\", snippet: \"\\\\alpha\", titulo: \"alfa\" },\n { label: \"β\", snippet: \"\\\\beta\", titulo: \"beta\" },\n { label: \"γ\", snippet: \"\\\\gamma\", titulo: \"gama\" },\n { label: \"δ\", snippet: \"\\\\delta\", titulo: \"delta\" },\n { label: \"ε\", snippet: \"\\\\varepsilon\", titulo: \"épsilon\" },\n { label: \"θ\", snippet: \"\\\\theta\", titulo: \"teta\" },\n { label: \"λ\", snippet: \"\\\\lambda\", titulo: \"lambda\" },\n { label: \"μ\", snippet: \"\\\\mu\", titulo: \"mi\" },\n { label: \"π\", snippet: \"\\\\pi\", titulo: \"pi\" },\n { label: \"ρ\", snippet: \"\\\\rho\", titulo: \"rô\" },\n { label: \"σ\", snippet: \"\\\\sigma\", titulo: \"sigma\" },\n { label: \"φ\", snippet: \"\\\\varphi\", titulo: \"fi\" },\n { label: \"ω\", snippet: \"\\\\omega\", titulo: \"ômega\" },\n { label: \"Δ\", snippet: \"\\\\Delta\", titulo: \"delta maiúsculo\" },\n {\n label: \"Σ\",\n snippet: \"\\\\Sigma\",\n titulo: \"sigma maiúsculo — letra grega; para somatório use Estruturas\",\n },\n {\n label: \"Π\",\n snippet: \"\\\\Pi\",\n titulo: \"pi maiúsculo — letra grega; para produtório use Estruturas\",\n },\n { label: \"Ω\", snippet: \"\\\\Omega\", titulo: \"ômega maiúsculo\" },\n { label: \"Φ\", snippet: \"\\\\Phi\", titulo: \"fi maiúsculo\" },\n ],\n },\n {\n nome: \"Conjuntos\",\n colunas: 6,\n itens: [\n { label: \"∈\", snippet: \"\\\\in\", titulo: \"pertence a\" },\n { label: \"∉\", snippet: \"\\\\notin\", titulo: \"não pertence a\" },\n { label: \"⊂\", snippet: \"\\\\subset\", titulo: \"contido em\" },\n { label: \"⊆\", snippet: \"\\\\subseteq\", titulo: \"contido ou igual a\" },\n { label: \"∪\", snippet: \"\\\\cup\", titulo: \"união\" },\n { label: \"∩\", snippet: \"\\\\cap\", titulo: \"interseção\" },\n { label: \"∅\", snippet: \"\\\\varnothing\", titulo: \"conjunto vazio\" },\n { label: \"ℕ\", snippet: \"\\\\mathbb{N}\", titulo: \"conjunto dos naturais\" },\n { label: \"ℤ\", snippet: \"\\\\mathbb{Z}\", titulo: \"conjunto dos inteiros\" },\n { label: \"ℚ\", snippet: \"\\\\mathbb{Q}\", titulo: \"conjunto dos racionais\" },\n { label: \"ℝ\", snippet: \"\\\\mathbb{R}\", titulo: \"conjunto dos reais\" },\n { label: \"ℂ\", snippet: \"\\\\mathbb{C}\", titulo: \"conjunto dos complexos\" },\n { label: \"∀\", snippet: \"\\\\forall\", titulo: \"para todo\" },\n { label: \"∃\", snippet: \"\\\\exists\", titulo: \"existe\" },\n { label: \"{ }\", snippet: \"\\\\{{}\\\\}\", titulo: \"chaves de conjunto\" },\n ],\n },\n {\n nome: \"Funções\",\n colunas: 6,\n itens: [\n { label: \"sen\", snippet: \"\\\\operatorname{sen}\", titulo: \"seno\" },\n { label: \"cos\", snippet: \"\\\\cos\", titulo: \"cosseno\" },\n { label: \"tg\", snippet: \"\\\\operatorname{tg}\", titulo: \"tangente\" },\n { label: \"cotg\", snippet: \"\\\\operatorname{cotg}\", titulo: \"cotangente\" },\n { label: \"log\", snippet: \"\\\\log_{}\", titulo: \"logaritmo\" },\n { label: \"ln\", snippet: \"\\\\ln\", titulo: \"logaritmo natural\" },\n { label: \"exp\", snippet: \"\\\\exp\", titulo: \"exponencial\" },\n ],\n },\n {\n nome: \"Setas\",\n colunas: 6,\n itens: [\n { label: \"→\", snippet: \"\\\\to\", titulo: \"tende a / vai para\" },\n { label: \"←\", snippet: \"\\\\leftarrow\", titulo: \"vem de / sentido inverso\" },\n { label: \"⇒\", snippet: \"\\\\Rightarrow\", titulo: \"implica em\" },\n { label: \"⇔\", snippet: \"\\\\Leftrightarrow\", titulo: \"se e somente se\" },\n { label: \"⇌\", snippet: \"\\\\rightleftharpoons\", titulo: \"equilíbrio químico\" },\n { label: \"vetor AB\", snippet: \"\\\\overrightarrow{}\", titulo: \"vetor entre dois pontos (AB)\" },\n { label: \"vetor\", snippet: \"\\\\vec{}\", titulo: \"vetor (física)\" },\n { label: \"∠\", snippet: \"\\\\angle\", titulo: \"ângulo\" },\n { label: \"△\", snippet: \"\\\\triangle\", titulo: \"triângulo\" },\n ],\n },\n];\n\n/**\n * Traduz o snippet da paleta para o dialeto de inserção do MathLive.\n *\n * O dado fica em `{}` e não em `#?` porque `#` é `macro parameter character`\n * no MathJax: guardar `#?` faria o `formulaPaleta.render.test.ts`, que passa\n * os 82 itens pelo renderer do documento, falhar em toda estrutura.\n *\n * Cada `{}` vira `{#?}`, COM as chaves, e uniformemente — não há caso\n * especial. Isso foi medido no navegador contra as 19 estruturas da paleta\n * que têm `{}`, e não deduzido:\n *\n * `{}` → `{#?}` 19/19 produzem `\\placeholder{}` no `getValue()`\n * `{}` → `#?` 17/19 — `\\vec{}` e `\\overrightarrow{}` saem malformados\n * (`\\vec{\\placeholder}{}`), porque um macro que pega\n * argumento por posição agarra só o `#` e deixa o `?`\n *\n * Se for tentador \"simplificar\" tirando as chaves porque os atalhos do\n * próprio MathLive usam `#?` solto (`\\sum_{#?}^{#?}` tem, a célula de\n * `pmatrix` não tem): as duas formas funcionam para a maioria, e a com\n * chaves funciona para todas.\n *\n * Conferido nos 82 itens (teste abaixo): nenhum tem `{}` que não seja\n * destino de digitação — `\\operatorname{sen}`, `\\mathbb{R}` e\n * `\\begin{pmatrix}` têm conteúdo entre as chaves, então não batem com o\n * padrão vazio.\n */\nexport function paraMathlive(snippet: string): string {\n return snippet.replaceAll(\"{}\", \"{#?}\");\n}\n\n/**\n * Traduz o snippet da paleta para o dialeto de PREVIEW do botão (via\n * `convertLatexToMarkup`) — não confundir com `paraMathlive` acima, que é\n * para inserção no campo.\n *\n * `convertLatexToMarkup` é um renderer estático (sem interatividade de\n * mathfield): passado o `{}` cru, `^{}` e `_{}` — Expoente e Índice — saem\n * **completamente vazios**. Não é feio, é NADA: `convertLatexToMarkup(\"^{}\")`\n * devolve `<span class=\"ML__msubsup\"></span>`, sem glifo, sem linha, sem\n * caixa — verificado com `node` contra o `mathlive/ssr` real, não deduzido.\n * O botão de Expoente ficaria com o mesmo problema que esta tarefa existe\n * pra resolver: sem forma nenhuma, pior que a palavra que troca.\n *\n * `\\placeholder{}` (o marcador visual do MathLive) NÃO ajuda aqui: também\n * verificado, `convertLatexToMarkup(\"\\\\placeholder{}\")` devolve\n * `<span class=\"ML__base\"> </span>` — um espaço, nada visível. O placeholder\n * tracejado do Docs é comportamento do mathfield interativo, não deste\n * renderer estático.\n *\n * O que funciona, testado nos mesmos 82 itens (teste abaixo): trocar cada\n * `{}` por `{□}` (U+25A1 WHITE SQUARE). Mesma regra do `paraMathlive` —\n * SEMPRE com as chaves, sem caso especial — pelo mesmo motivo: `\\vec{}` e\n * `\\overrightarrow{}` são macro de um argumento por posição, e `\\vec□` sem\n * chaves arriscaria o mesmo problema de argumento mal agarrado que motivou\n * manter as chaves lá.\n */\nexport function paraMarkup(snippet: string): string {\n return snippet.replaceAll(\"{}\", \"{□}\");\n}\n\n/**\n * Decide o conteúdo do botão da paleta: markup renderizado quando o renderer\n * do MathLive já carregou, senão o rótulo em texto.\n *\n * Existe como função separada por um motivo concreto, não por estética: no\n * jsdom o `import(\"mathlive\")` do modal não entrega o `convertLatexToMarkup`\n * a tempo, então o botão sempre cai no rótulo e **nenhum teste de componente\n * consegue exercitar o caminho do markup**. Medido: trocar `paraMarkup` por\n * `paraMathlive` na ligação deixava a suíte inteira verde (648/648) com os\n * botões de Fração e Expoente renderizando VAZIO — porque\n * `convertLatexToMarkup(\"^{#?}\")` devolve markup sem glifo nenhum, contra\n * `\"^{□}\"` do `paraMarkup`.\n *\n * Isolado assim, o teste chama direto e a troca morre.\n */\nexport function conteudoDoBotao(\n markup: ((latex: string) => string) | null,\n snippet: string,\n label: string,\n):\n | { dangerouslySetInnerHTML: { __html: string } }\n | { children: string } {\n if (!markup) return { children: label };\n return { dangerouslySetInnerHTML: { __html: markup(paraMarkup(snippet)) } };\n}\n","/** O que o MathLive serializa numa caixa **nunca tocada**. */\nexport const PLACEHOLDER = \"\\\\placeholder{}\";\n\n/**\n * Grupo vazio: `{}` ou `{ }`.\n *\n * É como volta uma caixa que o professor PREENCHEU e depois esvaziou —\n * `\\frac{1}{}` de quem apertou Backspace no denominador para corrigir. O\n * `\\placeholder{}` não cobre esse caso: ele marca só o que nunca foi tocado.\n */\nconst GRUPO_VAZIO = /\\{\\s*\\}/;\n\n/**\n * Célula vazia dentro de um ambiente de array (`pmatrix`, `cases`…).\n *\n * Célula esvaziada não volta como `{}` — volta como **nada**, entre dois\n * separadores: `\\begin{pmatrix}1 & 2\\\\ 3 & \\end{pmatrix}`. Nenhuma regex de\n * grupo alcança isso, e a matriz é o item da paleta com mais caixas.\n */\nconst CELULA_VAZIA = /(?:\\\\begin\\{[a-zA-Z]+\\*?\\}|&|\\\\\\\\)\\s*(?:&|\\\\\\\\|\\\\end\\{)/;\n\ntype Preview = { ok: boolean; error?: string } | null;\n\n/**\n * Três travas, porque uma fórmula pela metade **não** entra em branco no\n * documento — entra quebrada ou mutilada, e sem aviso.\n *\n * O caso que motivou as duas travas novas, medido: a professora clica em\n * Fração, preenche, e aperta Backspace uma vez para corrigir o denominador.\n * O campo vira `\\frac{1}{}`, o MathJax renderiza com `ok: true` um SVG de\n * 919 bytes com um único glifo (o \"1\", sem barra e sem denominador), e o\n * botão Inserir ficava **habilitado, sem mensagem nenhuma**. Nove formas\n * passavam assim: `\\frac{}{2}`, `x^{}`, `x_{}`, `\\sqrt{}`, `\\log_{}`,\n * `\\left|{}\\right|`, célula de matriz e linha de sistema.\n *\n * NÃO trocar por `getValue('latex-without-placeholders')`, que o MathLive\n * oferece: ele apaga a marca e devolve `\\frac{}{}`, que renderiza vazio —\n * é exatamente o bug que este gate existe para fechar.\n */\nexport function podeInserir(latex: string, preview: Preview): boolean {\n if (motivoBloqueio(latex, preview) !== null) return false;\n return latex.trim() !== \"\" && preview?.ok === true;\n}\n\nexport function motivoBloqueio(latex: string, preview: Preview): string | null {\n if (\n latex.includes(PLACEHOLDER) ||\n GRUPO_VAZIO.test(latex) ||\n CELULA_VAZIA.test(latex)\n ) {\n return \"Preencha os campos em branco da fórmula.\";\n }\n if (preview && !preview.ok) return preview.error ?? null;\n return null;\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type JSX,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { ImageEmbed } from \"@sofereditor/core\";\nimport { findBlockIndex, getFragmentForOffset } from \"./dom-bridge\";\n\ninterface ImageResizeOverlayProps {\n rootRef: RefObject<HTMLDivElement>;\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n embed: ImageEmbed;\n /** Called once on pointer-up with the final width/height (in CSS px). */\n onCommit: (width: number, height: number) => void;\n /**\n * Called once on pointer-up after a move-drag with the new offset\n * coordinates (CSS px). Only fired when `embed.layout` is non-inline.\n */\n onCommitMove?: (offsetX: number, offsetY: number) => void;\n /**\n * Called once on pointer-up after a move-drag when the image was dropped on a\n * DIFFERENT page than its current anchor. Re-anchors the embed to a block on\n * the destination page. Only fired for non-inline layouts.\n */\n onReanchor?: (\n to: { blockIndex: number; offset: number; cellIndex?: number },\n newOffsetX: number,\n newOffsetY: number,\n ) => void;\n}\n\ntype Corner = \"nw\" | \"ne\" | \"sw\" | \"se\";\n\ninterface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\n/**\n * Floating overlay drawn over the currently-selected image embed: 4 corner\n * handles + outline. Drag a handle to resize keeping aspect ratio. Changes are\n * applied live to the `<img>` style during the drag (no Y.Doc transactions per\n * frame); a single commit happens on pointer-up so Undo treats it as one step.\n */\nexport function ImageResizeOverlay({\n rootRef,\n blockIndex,\n offset,\n cellIndex,\n embed,\n onCommit,\n onCommitMove,\n onReanchor,\n}: ImageResizeOverlayProps): JSX.Element | null {\n const [rect, setRect] = useState<Rect | null>(null);\n const [isMoving, setIsMoving] = useState(false);\n const imgRef = useRef<HTMLImageElement | null>(null);\n\n const locateImage = useCallback((): HTMLImageElement | null => {\n const root = rootRef.current;\n if (!root) return null;\n let container: HTMLElement | null = null;\n if (cellIndex != null) {\n container = root.querySelector<HTMLElement>(\n `[data-block-index=\"${blockIndex}\"] [data-cell-index=\"${cellIndex}\"]`,\n );\n } else {\n container = getFragmentForOffset(root, blockIndex, offset);\n }\n if (!container) return null;\n // The renderInline data-embed-offset is LOCAL to the fragment / container.\n const fragmentStart = container.dataset.fragmentStart\n ? Number.parseInt(container.dataset.fragmentStart, 10) || 0\n : 0;\n const localOffset = offset - fragmentStart;\n return container.querySelector<HTMLImageElement>(\n `img[data-embed=\"image\"][data-embed-offset=\"${localOffset}\"]`,\n );\n }, [rootRef, blockIndex, offset, cellIndex]);\n\n const measure = useCallback(() => {\n const img = locateImage();\n if (!img) {\n setRect((prev) => (prev === null ? prev : null));\n return;\n }\n imgRef.current = img;\n const root = rootRef.current;\n if (!root) return;\n const r = img.getBoundingClientRect();\n const rr = root.getBoundingClientRect();\n const next: Rect = {\n top: r.top - rr.top + root.scrollTop,\n left: r.left - rr.left + root.scrollLeft,\n width: r.width,\n height: r.height,\n };\n setRect((prev) =>\n prev &&\n prev.top === next.top &&\n prev.left === next.left &&\n prev.width === next.width &&\n prev.height === next.height\n ? prev\n : next,\n );\n }, [locateImage, rootRef]);\n\n useLayoutEffect(() => {\n measure();\n // Pagination runs in a SEPARATE effect after this one, on LATER frames. So\n // right after an embed prop change OR a re-anchor (blockIndex/offset change\n // from a cross-page drag), the target <img> may not be locatable yet —\n // `measure()` sets rect=null and the overlay renders nothing. Re-measure\n // across a few frames UNTIL the image is found (rect set), then stop.\n // A single rAF wasn't enough: pagination can take several frames to place\n // the re-anchored block, leaving the overlay hidden until the next\n // selection change (bug: \"can't click to select the image right after it\n // re-anchors onto the new page\").\n let cancelled = false;\n let frame = 0;\n let rafId = 0;\n const remeasure = () => {\n if (cancelled) return;\n measure();\n // Keep retrying while the image still isn't locatable (bounded budget).\n if (!locateImage() && frame++ < 30) {\n rafId =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(remeasure)\n : (queueMicrotask(remeasure), 0);\n }\n };\n rafId =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(remeasure)\n : (queueMicrotask(remeasure), 0);\n return () => {\n cancelled = true;\n if (typeof cancelAnimationFrame === \"function\" && typeof rafId === \"number\" && rafId > 0) {\n cancelAnimationFrame(rafId);\n }\n };\n }, [\n measure,\n locateImage,\n embed.width,\n embed.height,\n embed.align,\n embed.src,\n embed.layout,\n embed.offsetX,\n embed.offsetY,\n ]);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onScroll = () => measure();\n window.addEventListener(\"resize\", onScroll);\n root.addEventListener(\"scroll\", onScroll);\n return () => {\n window.removeEventListener(\"resize\", onScroll);\n root.removeEventListener(\"scroll\", onScroll);\n };\n }, [measure, rootRef]);\n\n /*\n * The `useLayoutEffect` above re-measures synchronously when embed props\n * change — but the pagination engine runs its own re-layout in a SEPARATE\n * effect right after. That post-pagination shift can move the image by\n * dozens of pixels (e.g. when `align: center` flips it to `display: block`\n * and the paragraph reflows), leaving the overlay at the pre-shift\n * coordinates.\n *\n * A ResizeObserver on both the image AND the editor root fires whenever\n * either box changes — covers reflow, resize, font load, page repagination,\n * and image-load-late-decoded scenarios. Falls back to no-op in jsdom etc.\n */\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") return;\n const img = locateImage();\n const root = rootRef.current;\n if (!img || !root) return;\n const ro = new ResizeObserver(() => measure());\n ro.observe(img);\n ro.observe(root);\n return () => ro.disconnect();\n }, [locateImage, measure, rootRef, embed.layout, embed.align, embed.src]);\n\n if (!rect) return null;\n\n const layout = embed.layout ?? \"inline\";\n const movable = layout !== \"inline\" && onCommitMove !== undefined;\n\n return (\n <div\n className=\"ed-image-overlay\"\n style={{ top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n contentEditable={false}\n >\n {isMoving && (\n // Non-clipped drag preview (Tier 2): the overlay lives in .ed-root and\n // follows the cursor, so the image stays visible while it crosses the\n // page boundary (the real, clipped figure is hidden during the drag).\n // Inline styles only — the package does not ship CSS.\n <img\n src={embed.src}\n alt=\"\"\n aria-hidden\n draggable={false}\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"fill\",\n opacity: 0.8,\n pointerEvents: \"none\",\n }}\n />\n )}\n {movable && (\n <MoveHandle\n layout={layout}\n startOffsetX={embed.offsetX ?? 0}\n startOffsetY={embed.offsetY ?? 0}\n imgRef={imgRef}\n onCommit={onCommitMove!}\n onLiveChange={measure}\n rootRef={rootRef}\n blockIndex={blockIndex}\n offset={offset}\n cellIndex={cellIndex}\n onReanchor={onReanchor}\n onMoveStart={() => setIsMoving(true)}\n onMoveEnd={() => setIsMoving(false)}\n />\n )}\n <Handle\n corner=\"nw\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n <Handle\n corner=\"ne\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n <Handle\n corner=\"sw\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n <Handle\n corner=\"se\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n </div>\n );\n}\n\ninterface MoveHandleProps {\n layout: \"wrap-left\" | \"wrap-right\" | \"behind\" | \"front\" | \"inline\";\n startOffsetX: number;\n startOffsetY: number;\n imgRef: RefObject<HTMLImageElement | null>;\n onCommit: (ox: number, oy: number) => void;\n onLiveChange: () => void;\n rootRef: RefObject<HTMLDivElement>;\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n onReanchor?: (\n to: { blockIndex: number; offset: number; cellIndex?: number },\n newOffsetX: number,\n newOffsetY: number,\n ) => void;\n /** Fired when a move-drag starts/ends, so the overlay can show a drag preview. */\n onMoveStart?: () => void;\n onMoveEnd?: () => void;\n}\n\nfunction MoveHandle({\n layout,\n startOffsetX,\n startOffsetY,\n imgRef,\n onCommit,\n onLiveChange,\n rootRef,\n blockIndex,\n offset,\n cellIndex,\n onReanchor,\n onMoveStart,\n onMoveEnd,\n}: MoveHandleProps): JSX.Element {\n const dragRef = useRef<{\n startX: number;\n startY: number;\n liveOX: number;\n liveOY: number;\n } | null>(null);\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return;\n if (!imgRef.current) return;\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n dragRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n liveOX: startOffsetX,\n liveOY: startOffsetY,\n };\n // Hide the real (clipped) image and show a non-clipped preview in the\n // overlay for the duration of the drag, so the image stays visible while\n // it crosses the page boundary (Tier 2). The figure keeps being moved\n // below (drives the overlay/preview position via onLiveChange) — it's just\n // invisible. Restored in finish().\n const styled = imgRef.current.closest<HTMLElement>(\".ed-figure\") ?? imgRef.current;\n styled.style.opacity = \"0\";\n onMoveStart?.();\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d || !imgRef.current) return;\n const dx = e.clientX - d.startX;\n const dy = e.clientY - d.startY;\n // Post-#11 the positioned/styled element is the <figure> wrapper, not the\n // <img> (which renders position:static inside it). Mutate the figure so the\n // live drag actually moves the image; fall back to the img defensively.\n const styled = imgRef.current.closest<HTMLElement>(\".ed-figure\") ?? imgRef.current;\n let nox = startOffsetX;\n let noy = startOffsetY;\n if (layout === \"wrap-left\") {\n // dx → right margin (gap to text); negative dx clamped to 8 (min gap).\n nox = Math.max(8, startOffsetX + dx);\n noy = Math.max(0, startOffsetY + dy);\n styled.style.marginRight = `${nox}px`;\n styled.style.marginTop = `${noy}px`;\n } else if (layout === \"wrap-right\") {\n // dx is mirrored: dragging LEFT increases the gap to text.\n nox = Math.max(8, startOffsetX - dx);\n noy = Math.max(0, startOffsetY + dy);\n styled.style.marginLeft = `${nox}px`;\n styled.style.marginTop = `${noy}px`;\n } else if (layout === \"behind\" || layout === \"front\") {\n nox = startOffsetX + dx;\n noy = startOffsetY + dy;\n styled.style.left = `${nox}px`;\n styled.style.top = `${noy}px`;\n }\n d.liveOX = nox;\n d.liveOY = noy;\n onLiveChange();\n };\n\n // Clamp a behind/front vertical offset so the image stays within `frag`'s\n // page content box (never clipped by .ed-page-content overflow:hidden).\n // Offsets are fragment-local (position:absolute relative to the .ed-block).\n // Used for BOTH the same-page path and the re-anchor path (dest fragment).\n const clampOffsetToFragment = (frag: HTMLElement, oy: number): number => {\n const img = imgRef.current;\n if (!img) return oy;\n const page = frag.closest<HTMLElement>(\".ed-page\");\n const content = page?.querySelector<HTMLElement>(\".ed-page-content\");\n if (!content) return oy;\n const fragRect = frag.getBoundingClientRect();\n const contentRect = content.getBoundingClientRect();\n const imgH = img.getBoundingClientRect().height;\n // Allowed top range in fragment-local px: [contentTop, contentBottom-imgH].\n const minLocalTop = contentRect.top - fragRect.top;\n const maxLocalTop = contentRect.bottom - fragRect.top - imgH;\n return Math.round(Math.max(minLocalTop, Math.min(oy, maxLocalTop)));\n };\n\n const clampedCommit = (ox: number, oy: number) => {\n const img = imgRef.current;\n const root = rootRef.current;\n if (!img || !root || (layout !== \"behind\" && layout !== \"front\")) {\n onCommit(ox, oy);\n return;\n }\n const frag = getFragmentForOffset(root, blockIndex, offset);\n if (!frag) {\n onCommit(ox, oy);\n return;\n }\n onCommit(ox, clampOffsetToFragment(frag, oy));\n };\n\n const finish = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d) return;\n dragRef.current = null;\n try {\n (e.target as HTMLElement).releasePointerCapture(e.pointerId);\n } catch {\n // ignore\n }\n\n // End the drag preview and restore the real image's visibility. Restoring\n // imperatively (not just relying on React's commit re-render) avoids a\n // one-frame flicker between the commit and the re-render.\n if (imgRef.current) {\n const styled = imgRef.current.closest<HTMLElement>(\".ed-figure\") ?? imgRef.current;\n styled.style.opacity = \"\";\n }\n onMoveEnd?.();\n\n const img = imgRef.current;\n const root = rootRef.current;\n const liveOX = Math.round(d.liveOX);\n const liveOY = Math.round(d.liveOY);\n\n // Tables / wrap-anchors-in-cell are out of scope: keep the current path.\n // Re-anchoring only happens for non-inline embeds dropped on a DIFFERENT\n // top-level page, with onReanchor wired.\n if (!img || !root || onReanchor === undefined || cellIndex != null) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n const imgRect = img.getBoundingClientRect();\n const centerY = imgRect.top + imgRect.height / 2;\n\n // Destination page by GEOMETRY. `elementFromPoint(center)` was unreliable:\n // it returns null in the gap between pages or off-viewport, so cross-page\n // drops failed to re-anchor and snapped back to page 1. Instead pick the\n // .ed-page whose box CONTAINS the image center, else the NEAREST page —\n // never null. imgRect and getBoundingClientRect share the viewport-relative\n // coordinate space, so the comparison holds at any scroll.\n const pages = Array.from(root.querySelectorAll<HTMLElement>(\".ed-page\"));\n let destPage: HTMLElement | null = null;\n let bestPageDist = Infinity;\n for (const pg of pages) {\n const r = pg.getBoundingClientRect();\n if (centerY >= r.top && centerY <= r.bottom) {\n destPage = pg;\n break;\n }\n const dist = centerY < r.top ? r.top - centerY : centerY - r.bottom;\n if (dist < bestPageDist) {\n bestPageDist = dist;\n destPage = pg;\n }\n }\n\n // Current anchor page = the page owning the current fragment.\n const currentFrag = getFragmentForOffset(root, blockIndex, offset);\n const currentPage = currentFrag?.closest<HTMLElement>(\".ed-page\") ?? null;\n\n // Same page (or no page resolved) → existing same-anchor path.\n if (destPage == null || destPage === currentPage) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n // ---- Re-anchor to a block on the destination page ----\n // Destination anchor block by GEOMETRY: the block fragment on destPage whose\n // top is nearest the dropped image's top (no elementFromPoint).\n const blocksOnPage = Array.from(\n destPage.querySelectorAll<HTMLElement>(\"[data-block-index]\"),\n );\n let destFrag: HTMLElement | null = null;\n let bestBlockDist = Infinity;\n for (const b of blocksOnPage) {\n const dist = Math.abs(b.getBoundingClientRect().top - imgRect.top);\n if (dist < bestBlockDist) {\n bestBlockDist = dist;\n destFrag = b;\n }\n }\n\n // Empty destination page (no block fragments) → keep same-anchor + clamp.\n if (!destFrag) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n const destBlockIndex = findBlockIndex(destFrag, root);\n if (destBlockIndex == null) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n // Destination offset = the fragment's first rendered offset (fragmentStart),\n // so the anchor lands in the FRACTION of the block that lives on this page.\n const destOffset = destFrag.dataset.fragmentStart\n ? Number.parseInt(destFrag.dataset.fragmentStart, 10) || 0\n : 0;\n\n // Recompute absolute coords relative to the destination fragment box, and\n // CLAMP so the re-anchored image is never clipped on the destination page.\n // behind/front render position:absolute; left:ox; top:oy relative to the\n // .ed-block fragment (position:relative). So ox = imgLeft - fragLeft.\n const fragRect = destFrag.getBoundingClientRect();\n const newOffsetX = Math.round(imgRect.left - fragRect.left);\n const newOffsetY = clampOffsetToFragment(\n destFrag,\n Math.round(imgRect.top - fragRect.top),\n );\n\n onReanchor(\n { blockIndex: destBlockIndex, offset: destOffset, cellIndex: undefined },\n newOffsetX,\n newOffsetY,\n );\n };\n\n return (\n <div\n className=\"ed-image-move\"\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={finish}\n onPointerCancel={finish}\n />\n );\n}\n\ninterface HandleProps {\n corner: Corner;\n startW: number;\n startH: number;\n imgRef: RefObject<HTMLImageElement | null>;\n onCommit: (w: number, h: number) => void;\n onLiveChange: () => void;\n}\n\nconst MIN_W = 20;\n\nfunction Handle({\n corner,\n startW,\n startH,\n imgRef,\n onCommit,\n onLiveChange,\n}: HandleProps): JSX.Element {\n const dragRef = useRef<{\n startX: number;\n startY: number;\n startW: number;\n startH: number;\n aspect: number;\n liveW: number;\n liveH: number;\n } | null>(null);\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return;\n if (!imgRef.current) return;\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const aspect = startW > 0 && startH > 0 ? startW / startH : 1;\n dragRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n startW,\n startH,\n aspect,\n liveW: startW,\n liveH: startH,\n };\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d || !imgRef.current) return;\n const dxRaw = e.clientX - d.startX;\n // SE/NE handles grow with +dx; NW/SW grow with -dx.\n const dx = corner === \"se\" || corner === \"ne\" ? dxRaw : -dxRaw;\n let w = Math.max(MIN_W, d.startW + dx);\n let h = w / d.aspect;\n // Apply live to the img element directly — no transaction.\n imgRef.current.style.width = `${w}px`;\n imgRef.current.style.height = `${h}px`;\n imgRef.current.width = Math.round(w);\n imgRef.current.height = Math.round(h);\n d.liveW = w;\n d.liveH = h;\n onLiveChange();\n };\n\n const finish = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d) return;\n dragRef.current = null;\n try {\n (e.target as HTMLElement).releasePointerCapture(e.pointerId);\n } catch {\n // ignore\n }\n onCommit(Math.round(d.liveW), Math.round(d.liveH));\n };\n\n return (\n <div\n className={`ed-image-handle ${corner}`}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={finish}\n onPointerCancel={finish}\n />\n );\n}\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type JSX,\n type RefObject,\n} from \"react\";\nimport {\n decodeSelection,\n type EditorDocument,\n type EncodedCursor,\n} from \"@sofereditor/core\";\nimport { locatePoint } from \"./dom-bridge\";\n\n/**\n * Subconjunto mínimo da Awareness do y-protocols que o overlay usa. Tipar assim\n * evita que `@sofereditor/react` dependa de `y-protocols` — o objeto vem de fora\n * (de `useCollab().binding.awareness`) e é estruturalmente compatível.\n */\nexport interface AwarenessLike {\n clientID: number;\n getStates(): Map<number, Record<string, unknown>>;\n on(event: \"change\", cb: () => void): void;\n off(event: \"change\", cb: () => void): void;\n setLocalStateField(field: string, value: unknown): void;\n}\n\ninterface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\ninterface PeerCursorView {\n clientId: number;\n name: string;\n color: string;\n caret: { top: number; left: number; height: number } | null;\n selection: Rect[];\n}\n\ninterface RemoteCursorsOverlayProps {\n rootRef: RefObject<HTMLDivElement>;\n doc: EditorDocument;\n awareness: AwarenessLike;\n /** Muda a cada edição/repaginação → re-medir os carets remotos. */\n revision?: unknown;\n}\n\nconst DEFAULT_COLOR = \"#3b82f6\";\n\n/**\n * Desenha o caret + seleção + nome de cada usuário remoto sobre o conteúdo\n * paginado. Posições remotas chegam via awareness (campo `cursor`, codificado\n * com Y.RelativePosition pelo core) e são resolvidas para coordenadas DOM com\n * o mesmo bridge da seleção local — ciente de fragmentação por página.\n */\nexport function RemoteCursorsOverlay({\n rootRef,\n doc,\n awareness,\n revision,\n}: RemoteCursorsOverlayProps): JSX.Element | null {\n const [views, setViews] = useState<PeerCursorView[]>([]);\n const rafRef = useRef<number | null>(null);\n\n const measure = useCallback(() => {\n const root = rootRef.current;\n if (!root) {\n setViews((prev) => (prev.length === 0 ? prev : []));\n return;\n }\n const rr = root.getBoundingClientRect();\n const out: PeerCursorView[] = [];\n\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === awareness.clientID) return;\n const s = state as {\n user?: { name?: string; color?: string };\n cursor?: EncodedCursor;\n };\n if (!s.cursor) return;\n const sel = decodeSelection(doc, s.cursor);\n if (!sel) return;\n\n const color = s.user?.color || DEFAULT_COLOR;\n const name = s.user?.name || \"Anônimo\";\n\n const focus = locatePoint(root, sel.focus);\n let caret: PeerCursorView[\"caret\"] = null;\n if (focus) {\n try {\n const range = document.createRange();\n range.setStart(focus.node, focus.offset);\n range.collapse(true);\n let rect = range.getBoundingClientRect();\n if (rect.height === 0 && focus.node.nodeType === Node.ELEMENT_NODE) {\n rect = (focus.node as HTMLElement).getBoundingClientRect();\n }\n caret = {\n top: rect.top - rr.top + root.scrollTop,\n left: rect.left - rr.left + root.scrollLeft,\n height: rect.height || 18,\n };\n } catch {\n /* range inválido — sem caret */\n }\n }\n\n const collapsed =\n sel.anchor.blockIndex === sel.focus.blockIndex &&\n (sel.anchor.cellIndex ?? -1) === (sel.focus.cellIndex ?? -1) &&\n sel.anchor.offset === sel.focus.offset;\n\n const selection: Rect[] = [];\n if (!collapsed && focus) {\n const anchor = locatePoint(root, sel.anchor);\n if (anchor) {\n try {\n const r = document.createRange();\n r.setStart(anchor.node, anchor.offset);\n r.setEnd(focus.node, focus.offset);\n if (r.collapsed) {\n r.setStart(focus.node, focus.offset);\n r.setEnd(anchor.node, anchor.offset);\n }\n for (const cr of Array.from(r.getClientRects())) {\n if (cr.width === 0 || cr.height === 0) continue;\n selection.push({\n top: cr.top - rr.top + root.scrollTop,\n left: cr.left - rr.left + root.scrollLeft,\n width: cr.width,\n height: cr.height,\n });\n }\n } catch {\n /* seleção cross-fragmento inválida — ignora */\n }\n }\n }\n\n out.push({ clientId, name, color, caret, selection });\n });\n\n setViews(out);\n }, [rootRef, doc, awareness]);\n\n const scheduleMeasure = useCallback(() => {\n if (rafRef.current != null) return;\n const raf =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame\n : (cb: FrameRequestCallback) => setTimeout(() => cb(0), 16) as unknown as number;\n rafRef.current = raf(() => {\n rafRef.current = null;\n measure();\n });\n }, [measure]);\n\n // Awareness: re-mede quando qualquer peer muda cursor/entra/sai.\n useEffect(() => {\n const onChange = () => scheduleMeasure();\n awareness.on(\"change\", onChange);\n scheduleMeasure();\n return () => awareness.off(\"change\", onChange);\n }, [awareness, scheduleMeasure]);\n\n // Edição/repaginação local.\n useEffect(() => {\n scheduleMeasure();\n }, [revision, scheduleMeasure]);\n\n // Resize do root + scroll re-posicionam os carets.\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onScroll = () => scheduleMeasure();\n root.addEventListener(\"scroll\", onScroll, { passive: true });\n let ro: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\") {\n ro = new ResizeObserver(() => scheduleMeasure());\n ro.observe(root);\n }\n return () => {\n root.removeEventListener(\"scroll\", onScroll);\n ro?.disconnect();\n };\n }, [rootRef, scheduleMeasure]);\n\n if (views.length === 0) return null;\n\n const layer: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n pointerEvents: \"none\",\n zIndex: 6,\n };\n\n return (\n <div className=\"ed-remote-cursors\" aria-hidden style={layer}>\n {views.map((v) => (\n <div key={v.clientId}>\n {v.selection.map((r, i) => (\n <div\n key={i}\n style={{\n position: \"absolute\",\n top: r.top,\n left: r.left,\n width: r.width,\n height: r.height,\n background: v.color,\n opacity: 0.22,\n borderRadius: 2,\n }}\n />\n ))}\n {v.caret && (\n <div\n style={{\n position: \"absolute\",\n top: v.caret.top,\n left: v.caret.left,\n height: v.caret.height,\n width: 2,\n background: v.color,\n }}\n >\n <span\n style={{\n position: \"absolute\",\n top: -15,\n left: -1,\n fontSize: 10,\n lineHeight: \"14px\",\n padding: \"0 4px\",\n borderRadius: 3,\n color: \"#fff\",\n background: v.color,\n whiteSpace: \"nowrap\",\n fontFamily: \"system-ui, sans-serif\",\n userSelect: \"none\",\n }}\n >\n {v.name}\n </span>\n </div>\n )}\n </div>\n ))}\n </div>\n );\n}\n","import { useMemo, useRef, type CSSProperties, type JSX, type ReactNode } from \"react\";\nimport {\n answerLineStyle,\n cellBorderStyle,\n normalizarLarguras,\n tableRectSelection,\n type BlockAttrs,\n type CellAttrs,\n type SerializedBlock,\n type StyleRecord,\n} from \"@sofereditor/core\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { renderInline } from \"./renderInline\";\nimport { sliceDelta } from \"./sliceDelta\";\nimport { TableResizeOverlay } from \"./TableResizeOverlay\";\n\nexport interface NodeViewFragment {\n index: number;\n start: number;\n end: number;\n}\n\ninterface NodeViewProps {\n block: SerializedBlock;\n index: number;\n /** Optional nested content. Used by `listItem` to host the nested <ol>/<ul>. */\n children?: ReactNode;\n /** Optional fragment slice — when present, the rendered DOM covers only chars [start, end). */\n fragment?: NodeViewFragment;\n /**\n * Optional table-row fragmentation slice for Sub-phase 4.6. When set, only\n * the rows in `[rowStart, rowEnd)` are rendered, and the resize overlay is\n * suppressed except on the FIRST fragment.\n */\n tableFragment?: { index: number; rowStart: number; rowEnd: number };\n}\n\nexport function NodeView({ block, index, children, fragment, tableFragment }: NodeViewProps): JSX.Element {\n if (block.type === \"table\") {\n return <TableView block={block} index={index} tableFragment={tableFragment} />;\n }\n const inline = fragment\n ? renderInline(sliceDelta(block.delta, fragment.start, fragment.end), index)\n : renderInline(block.delta, index);\n const common = commonBlockProps(block.attrs, index, block.type, fragment);\n\n switch (block.type) {\n case \"heading\": {\n const level = clampLevel(block.attrs.level);\n const Tag = (`h${level}` as unknown) as keyof JSX.IntrinsicElements;\n return (\n <Tag\n {...common}\n className={`ed-block ed-heading ed-heading-${level}${alignClass(block.attrs.align)}${fragmentClass(fragment)}`}\n >\n {inline}\n </Tag>\n );\n }\n case \"blockquote\":\n return (\n <blockquote\n {...common}\n className={`ed-block ed-blockquote${alignClass(block.attrs.align)}${fragmentClass(fragment)}`}\n >\n {inline}\n </blockquote>\n );\n case \"codeBlock\":\n return (\n <pre\n {...common}\n data-language={block.attrs.language ?? undefined}\n className={`ed-block ed-codeblock${alignClass(block.attrs.align)}`}\n >\n <code>{inline}</code>\n </pre>\n );\n case \"listItem\": {\n const level = clampListLevel(block.attrs.listLevel);\n return (\n <li\n {...common}\n data-list-kind={block.attrs.listKind ?? \"bullet\"}\n data-list-level={level}\n className={`ed-block ed-listitem${alignClass(block.attrs.align)}`}\n >\n {inline}\n {children}\n </li>\n );\n }\n case \"paragraph\":\n default:\n return (\n <div\n {...common}\n className={`ed-block ed-paragraph${alignClass(block.attrs.align)}${fragmentClass(fragment)}`}\n >\n {inline}\n </div>\n );\n }\n}\n\nexport function commonBlockProps(\n attrs: BlockAttrs,\n index: number,\n type: string,\n fragment: NodeViewFragment | undefined,\n) {\n const style: CSSProperties = {};\n if (attrs.align) style.textAlign = attrs.align;\n // Linha de resposta: régua inferior + entrelinha. Vem de um helper puro em\n // core para o HTML de servidor emitir exatamente o mesmo estilo.\n Object.assign(style, answerLineStyle(attrs) ?? {});\n const base: Record<string, unknown> = {\n \"data-block-index\": index,\n \"data-block-type\": type,\n dir: attrs.dir,\n style: Object.keys(style).length > 0 ? style : undefined,\n };\n if (!fragment) return base;\n base[\"data-fragment-index\"] = fragment.index;\n base[\"data-fragment-start\"] = fragment.start;\n base[\"data-fragment-end\"] = fragment.end;\n return base;\n}\n\nfunction fragmentClass(f?: NodeViewFragment): string {\n if (!f) return \"\";\n // First fragment keeps top margin, last keeps bottom; intermediate fragments drop both.\n return ` ed-fragment ed-fragment-${f.index}`;\n}\n\nfunction alignClass(a?: BlockAttrs[\"align\"]): string {\n return a ? ` ed-align-${a}` : \"\";\n}\n\n/**\n * Inline style for a table cell's visual attrs (align, bgColor).\n * Returns undefined when the cell has none, so the <td> carries no style attr.\n */\nexport function cellStyle(\n attrs?: CellAttrs,\n border?: StyleRecord,\n): CSSProperties | undefined {\n const style: CSSProperties = { ...(border as CSSProperties | undefined) };\n if (attrs?.align) style.textAlign = attrs.align;\n if (attrs?.bgColor) style.backgroundColor = attrs.bgColor;\n return Object.keys(style).length > 0 ? style : undefined;\n}\n\nfunction clampLevel(l: BlockAttrs[\"level\"]): 1 | 2 | 3 | 4 | 5 | 6 {\n if (l === 2 || l === 3 || l === 4 || l === 5 || l === 6) return l;\n return 1;\n}\n\nfunction clampListLevel(l: BlockAttrs[\"listLevel\"]): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\ninterface TableViewProps {\n block: SerializedBlock;\n index: number;\n tableFragment?: { index: number; rowStart: number; rowEnd: number };\n}\n\nfunction TableView({ block, index, tableFragment }: TableViewProps): JSX.Element {\n const rows = clampPositive(block.attrs.rows);\n const cols = clampPositive(block.attrs.cols);\n const cells = block.cells ?? [];\n const widths = normalizarLarguras(block.attrs.colWidths as number[] | undefined, cols);\n const tableWidth = block.attrs.tableWidth;\n const rowHeights = block.attrs.rowHeights as number[] | undefined;\n const tableRef = useRef<HTMLTableElement | null>(null);\n\n // Row range to render. Default = full table.\n const rowStart = tableFragment?.rowStart ?? 0;\n const rowEnd = tableFragment?.rowEnd ?? rows;\n const isFirstFragment = !tableFragment || tableFragment.index === 0;\n\n // Recompute the rectangular cell selection each render. Reading the snapshot\n // off `editor.snapshot` ties this to the same render that NodeView is in.\n const editor = useEditorContext();\n const rect = useMemo(() => {\n if (editor.selection.anchor.blockIndex !== index) return null;\n if (editor.selection.focus.blockIndex !== index) return null;\n return tableRectSelection(editor.doc, editor.selection);\n }, [editor.doc, editor.selection, index]);\n\n return (\n <div className=\"ed-table-wrap\">\n <table\n ref={tableRef}\n data-block-index={index}\n data-block-type=\"table\"\n className=\"ed-block ed-table\"\n style={typeof tableWidth === \"number\" ? { width: `${tableWidth}%` } : undefined}\n >\n <colgroup>\n {widths.map((w, c) => (\n <col key={c} style={{ width: `${w}%` }} />\n ))}\n </colgroup>\n <tbody>\n {Array.from({ length: rowEnd - rowStart }, (_, i) => {\n const r = rowStart + i;\n return (\n <tr\n key={r}\n data-cell-row={r}\n // `height` no <tr> é tratado pelo CSS de tabela como MÍNIMO, não\n // fixo — conteúdo maior empurra a linha. Não usar max-height nem\n // overflow: hidden aqui, ou texto some da prova.\n style={\n typeof rowHeights?.[r] === \"number\"\n ? { height: `${rowHeights[r]}px` }\n : undefined\n }\n >\n {Array.from({ length: cols }, (_, c) => {\n const flat = r * cols + c;\n const cell = cells[flat];\n if (cell?.attrs.covered === true) return null;\n const rowspan = spanAttr(cell?.attrs.rowspan);\n const colspan = spanAttr(cell?.attrs.colspan);\n const delta = cell?.delta ?? [];\n const inRect =\n rect !== null &&\n r >= rect.top && r <= rect.bottom &&\n c >= rect.left && c <= rect.right;\n // Cell align is applied via inline textAlign; block-level align uses ed-align-* classes instead.\n return (\n <td\n key={c}\n data-cell-index={flat}\n data-cell-row={r}\n data-cell-col={c}\n data-cell-rowspan={rowspan}\n data-cell-colspan={colspan}\n rowSpan={rowspan > 1 ? rowspan : undefined}\n colSpan={colspan > 1 ? colspan : undefined}\n style={cellStyle(\n cell?.attrs,\n // rowStart/rowEnd são os limites do FRAGMENTO: numa tabela\n // quebrada entre páginas, cada página fecha a própria caixa.\n cellBorderStyle(\n block.attrs.borderPreset,\n { row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },\n \"screen\",\n block.attrs.borderColor,\n ),\n )}\n className={inRect ? \"ed-cell ed-cell--selected\" : \"ed-cell\"}\n >\n {renderInline(delta, `t${index}-c${flat}`)}\n </td>\n );\n })}\n </tr>\n );\n })}\n </tbody>\n </table>\n {isFirstFragment && (\n <TableResizeOverlay tableRef={tableRef} blockIndex={index} cols={cols} rows={rows} />\n )}\n </div>\n );\n}\n\nfunction spanAttr(v: unknown): number {\n if (typeof v !== \"number\" || !Number.isFinite(v) || v < 1) return 1;\n return Math.trunc(v);\n}\n\nfunction clampPositive(n: unknown): number {\n if (typeof n !== \"number\" || !Number.isFinite(n) || n <= 0) return 1;\n return Math.trunc(n);\n}\n","import { Fragment, memo, type CSSProperties, type ReactNode } from \"react\";\nimport {\n BLANK_STYLE,\n isImageEmbed,\n splitUnderscoreRuns,\n type DeltaOp,\n type ImageEmbed,\n type MarkAttrs,\n} from \"@sofereditor/core\";\nimport { hasOpenModifier } from \"./platform\";\n\n/**\n * Convert a Y.Text delta into ReactNodes.\n *\n * Wrap order (innermost → outermost): bold → italic → underline → strike → font/color span → link.\n * Semantic tags carry bold/italic/underline/strike; color and fonts go on a styled <span>.\n *\n * Keys are stable on `${blockIndex}-${runningOffset}` so React reconciliation\n * survives split/merge of runs under collaboration.\n */\nexport function renderInline(delta: DeltaOp[], keyPrefix: number | string): ReactNode {\n if (\n delta.length === 0 ||\n (delta.length === 1 &&\n typeof delta[0].insert === \"string\" &&\n delta[0].insert === \"\")\n ) {\n return <br data-empty=\"true\" />;\n }\n let offset = 0;\n // Float-wrap images (wrap-left / wrap-right) are emitted as the FIRST DOM\n // children of the block so CSS float can flow the whole paragraph around\n // them — float only wraps siblings that come after it in DOM order. A\n // zero-width phantom span is left at the embed's original offset so the\n // caret-position math in dom-bridge keeps counting the embed as 1 char in\n // its true model position.\n const wrapAnchors: ReactNode[] = [];\n const body: ReactNode[] = [];\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n const text = op.insert;\n if (text.length === 0) continue;\n const key = `${keyPrefix}-${offset}`;\n body.push(<Fragment key={key}>{wrap(text, op.attributes)}</Fragment>);\n offset += text.length;\n } else if (isImageEmbed(op.insert)) {\n const key = `${keyPrefix}-${offset}`;\n const layout = op.insert.layout ?? \"inline\";\n // Marks aplicadas ao 1-char range que cobre o embed: hoje só\n // `comment` é renderizada visualmente (espelha o tratamento de\n // texto em `wrap()`). Outras marks (bold/italic/etc.) não fazem\n // sentido em embed visual e são ignoradas.\n const commentMarkId = op.attributes?.comment?.markId;\n if (layout === \"wrap-left\" || layout === \"wrap-right\") {\n wrapAnchors.push(\n <ImageEmbedView key={`${key}-anchor`} embed={op.insert} offset={offset} wrapAnchor />,\n );\n body.push(\n <span\n key={`${key}-phantom`}\n data-embed-phantom=\"true\"\n data-embed-offset={offset}\n contentEditable={false}\n style={PHANTOM_STYLE}\n />,\n );\n } else {\n const imgNode = <ImageEmbedView key={key} embed={op.insert} offset={offset} />;\n if (commentMarkId) {\n body.push(\n <span key={`${key}-cmt`} data-comment-id={commentMarkId} className=\"ed-comment\">\n {imgNode}\n </span>,\n );\n } else {\n body.push(imgNode);\n }\n }\n offset += 1;\n } else if (op.insert != null) {\n // Unknown embed kind — reserve the offset slot so caret math stays correct.\n offset += 1;\n }\n }\n if (wrapAnchors.length === 0 && body.length === 0) return <br data-empty=\"true\" />;\n return [...wrapAnchors, ...body];\n}\n\nconst PHANTOM_STYLE: CSSProperties = {\n display: \"inline-block\",\n width: 0,\n height: 0,\n overflow: \"hidden\",\n verticalAlign: \"baseline\",\n};\n\n// Inline style for links (the package ships no CSS). `color`/underline give the\n// link its visual affordance; `cursor: \"text\"` matches the surrounding editable\n// text — the browser's UA stylesheet makes `<a href>` use `cursor: pointer`,\n// which flickers pointer↔text across the link boundary inside a contentEditable\n// (hard to click / \"cursor oscila\"). To FOLLOW a link, Cmd/Ctrl+click.\nconst LINK_STYLE: CSSProperties = {\n color: \"#2563eb\",\n textDecoration: \"underline\",\n cursor: \"text\",\n};\n\ninterface ImageEmbedViewProps {\n embed: ImageEmbed;\n offset: number;\n wrapAnchor?: boolean;\n}\n\n/**\n * `embed.src` is a (potentially huge) base64 data URL. Y.Text.toDelta() builds\n * a fresh `ImageEmbed` object every time the parent snapshot recomputes — so\n * by default React would re-render `<img src={...}>` on every keystroke even\n * when nothing about the image changed. Memoizing by *value* on the cheap\n * fields and skipping the src deep-compare (it's stable while the embed is\n * stable; if src actually changes, width/height typically change too) keeps\n * the `<img>` DOM node fully stable across keystrokes.\n */\nfunction imageEmbedPropsEqual(a: ImageEmbedViewProps, b: ImageEmbedViewProps): boolean {\n if (a.offset !== b.offset || a.wrapAnchor !== b.wrapAnchor) return false;\n const x = a.embed;\n const y = b.embed;\n if (x === y) return true;\n return (\n x.width === y.width &&\n x.height === y.height &&\n x.layout === y.layout &&\n x.align === y.align &&\n x.offsetX === y.offsetX &&\n x.offsetY === y.offsetY &&\n x.caption === y.caption &&\n x.captionAlign === y.captionAlign &&\n // src is the expensive compare — only fall back to ref equality. Fresh\n // toDelta() objects share the same string reference for embed payloads\n // because Y.js stores the embed in its content map and re-exposes it.\n x.src === y.src\n );\n}\n\nconst ImageEmbedView = memo(ImageEmbedViewImpl, imageEmbedPropsEqual);\n\nfunction ImageEmbedViewImpl({\n embed,\n offset,\n wrapAnchor,\n}: ImageEmbedViewProps): ReactNode {\n const layout = embed.layout ?? \"inline\";\n const align = embed.align;\n const ox = embed.offsetX ?? 0;\n const oy = embed.offsetY ?? 0;\n const hasCaption = !!embed.caption && embed.caption.length > 0;\n\n // The outer style positions the wrapper (float / absolute / inline-block /\n // block-with-align). The img itself stays at intrinsic width × height so the\n // existing caret-position math (`data-embed-offset`) and ImageResizeOverlay\n // queries (`img[data-embed=\"image\"]`) keep working. A `<figcaption>` lives\n // inside the wrapper when a caption is set.\n let outerStyle: CSSProperties;\n switch (layout) {\n case \"wrap-left\":\n outerStyle = {\n float: \"left\",\n width: embed.width,\n marginRight: Math.max(8, ox),\n marginTop: Math.max(0, oy),\n marginBottom: 4,\n };\n break;\n case \"wrap-right\":\n outerStyle = {\n float: \"right\",\n width: embed.width,\n marginLeft: Math.max(8, ox),\n marginTop: Math.max(0, oy),\n marginBottom: 4,\n };\n break;\n case \"behind\":\n case \"front\":\n outerStyle = {\n position: \"absolute\",\n left: ox,\n top: oy,\n width: embed.width,\n zIndex: layout === \"behind\" ? -1 : 2,\n };\n break;\n case \"inline\":\n default:\n outerStyle = align\n ? {\n display: \"block\",\n width: embed.width,\n marginLeft: align === \"left\" ? 0 : \"auto\",\n marginRight: align === \"right\" ? 0 : \"auto\",\n }\n : {\n display: \"inline-block\",\n // Fórmula inline traz o deslocamento de base que o MathJax mediu;\n // sem ele a fórmula flutua no topo da linha em vez de sentar nela.\n verticalAlign: embed.formula?.vAlign ?? \"text-bottom\",\n width: embed.width,\n };\n break;\n }\n\n // The img always carries the data-* attributes used by Editor/Overlay.\n //\n // Structure-stable rendering (bug #11): the <img> ALWAYS lives inside a\n // <figure> that carries `outerStyle`, so adding/removing a caption never\n // changes the <img>'s parent type. React then keeps the same <img> DOM node\n // instead of unmounting+remounting it — a remount would force a synchronous\n // base64 re-decode (`decoding=\"sync\"` below), freezing the UI on large\n // images. The <img> therefore always uses the intrinsic-size style; the\n // <figure> owns the positioning.\n const imgStyle: CSSProperties = {\n width: embed.width,\n height: embed.height,\n display: \"block\",\n };\n\n // `decoding=\"sync\"` + `loading=\"eager\"` evita o piscar quando a paginação\n // remonta o <img> (consumidor com `renderPageHeader` faz a 1ª página ter\n // marginTop diferente, o que muda o DOM pai do bloco quando ele cruza\n // páginas): com cache do browser quente, o frame de remount já vem pintado.\n const img = (\n <img\n src={embed.src}\n width={embed.width}\n height={embed.height}\n contentEditable={false}\n draggable={false}\n decoding=\"sync\"\n loading=\"eager\"\n data-embed=\"image\"\n data-embed-offset={offset}\n data-embed-align={align ?? \"\"}\n data-embed-layout={layout}\n data-wrap-anchor={wrapAnchor ? \"true\" : undefined}\n style={imgStyle}\n alt=\"\"\n />\n );\n\n // Always render the <figure> wrapper (structure-stable; see imgStyle note).\n // The <figcaption> is the ONLY part that toggles with the caption — the\n // <img> stays mounted, so no re-decode/freeze.\n const captionAlign = embed.captionAlign ?? \"center\";\n return (\n <figure\n className=\"ed-figure\"\n contentEditable={false}\n data-embed-figure={hasCaption ? \"true\" : undefined}\n data-embed-layout={layout}\n style={outerStyle}\n >\n {img}\n {hasCaption && (\n <figcaption\n className=\"ed-figcaption\"\n data-caption-align={captionAlign}\n style={{ textAlign: captionAlign }}\n >\n {embed.caption}\n </figcaption>\n )}\n </figure>\n );\n}\n\n/**\n * Corridas de 3+ underlines viram um traço contínuo (lacuna de prova).\n *\n * Os caracteres PERMANECEM no DOM como nós de texto: `dom-bridge` mapeia\n * posição de DOM → offset do modelo somando `textContent.length`, então dividir\n * a run em sub-spans é seguro, mas introduzir ou remover caracteres não seria.\n */\nfunction decorateBlanks(text: string): ReactNode {\n const segs = splitUnderscoreRuns(text);\n // Caminho comum (texto sem lacuna): devolve a string crua, sem wrapper.\n if (segs.length === 1 && !segs[0].blank) return text;\n return segs.map((s, i) =>\n s.blank ? (\n <span key={i} data-blank=\"true\" style={BLANK_STYLE as CSSProperties}>\n {s.text}\n </span>\n ) : (\n <Fragment key={i}>{s.text}</Fragment>\n ),\n );\n}\n\nfunction wrap(text: string, attrs?: MarkAttrs): ReactNode {\n // A lacuna fica DENTRO dos wrappers de mark: é decoração do texto, não uma mark.\n let node: ReactNode = decorateBlanks(text);\n if (!attrs) return node;\n\n if (attrs.bold) node = <strong>{node}</strong>;\n if (attrs.italic) node = <em>{node}</em>;\n if (attrs.underline) node = <u>{node}</u>;\n if (attrs.strike) node = <s>{node}</s>;\n\n const styleParts: CSSProperties = {};\n if (attrs.color) styleParts.color = attrs.color;\n if (attrs.highlight) styleParts.backgroundColor = attrs.highlight;\n if (attrs.fontFamily) styleParts.fontFamily = attrs.fontFamily;\n if (attrs.fontSize) styleParts.fontSize = attrs.fontSize;\n if (Object.keys(styleParts).length > 0) {\n node = <span style={styleParts}>{node}</span>;\n }\n\n if (attrs.link?.href) {\n node = (\n <a\n href={attrs.link.href}\n title={attrs.link.title}\n rel=\"noopener noreferrer\"\n target=\"_blank\"\n style={LINK_STYLE}\n onClick={(e) => {\n // Plain click edits the link text (caret), never navigates. The\n // platform \"open\" modifier (⌘ on macOS, Ctrl elsewhere) opens the\n // link directly in a new tab. We `preventDefault` in BOTH cases and\n // open via `window.open` ourselves: inside a contentEditable the\n // browser doesn't reliably navigate an `<a>` on modifier-click, so\n // relying on the default action did nothing.\n e.preventDefault();\n if (hasOpenModifier(e) && attrs.link?.href) {\n window.open(attrs.link.href, \"_blank\", \"noopener,noreferrer\");\n }\n }}\n >\n {node}\n </a>\n );\n }\n\n if (attrs.comment?.markId) {\n node = (\n <span data-comment-id={attrs.comment.markId} className=\"ed-comment\">\n {node}\n </span>\n );\n }\n\n return node;\n}\n","import type { DeltaOp } from \"@sofereditor/core\";\n\n/** Total number of inserted characters. Embeds (non-string inserts) count as 1. */\nexport function deltaLength(delta: DeltaOp[]): number {\n let sum = 0;\n for (const op of delta) {\n if (typeof op.insert === \"string\") sum += op.insert.length;\n else if (op.insert != null) sum += 1;\n }\n return sum;\n}\n\n/**\n * Return the subset of `delta` covering `[start, end)` characters. Attributes\n * are preserved on each intersecting op. Embeds are atomic length-1 ops:\n * included whole if their slot intersects the range.\n */\nexport function sliceDelta(delta: DeltaOp[], start: number, end: number): DeltaOp[] {\n if (end <= start) return [];\n const out: DeltaOp[] = [];\n let cursor = 0;\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n const opStart = cursor;\n const opEnd = cursor + op.insert.length;\n cursor = opEnd;\n if (opEnd <= start) continue;\n if (opStart >= end) break;\n const sliceFrom = Math.max(opStart, start) - opStart;\n const sliceTo = Math.min(opEnd, end) - opStart;\n out.push({\n insert: op.insert.slice(sliceFrom, sliceTo),\n attributes: op.attributes,\n });\n } else if (op.insert != null) {\n const opStart = cursor;\n const opEnd = cursor + 1;\n cursor = opEnd;\n if (opEnd <= start) continue;\n if (opStart >= end) break;\n out.push({ insert: op.insert, attributes: op.attributes });\n }\n }\n return out;\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type JSX,\n type RefObject,\n} from \"react\";\nimport { MIN_LINHA_PX, normalizarLarguras } from \"@sofereditor/core\";\nimport { useEditorContext } from \"./EditorContext\";\n\ninterface Props {\n /** Ref to the underlying `<table>` element. */\n tableRef: RefObject<HTMLTableElement | null>;\n blockIndex: number;\n cols: number;\n rows: number;\n}\n\n/**\n * O arrasto chega em px de tela; o modelo é proporção. A conversão é contra\n * a largura RENDERIZADA da tabela, que é justamente o que torna o arrasto\n * exato: mover o dedo 60 px numa tabela de 600 px é mover a divisa 10 pontos,\n * e 10 pontos renderizam 60 px. Ida e volta fecham.\n */\nexport function deltaPctDoArrasto(dxPx: number, larguraTabelaPx: number): number {\n if (!(larguraTabelaPx > 0)) return 0;\n return (dxPx / larguraTabelaPx) * 100;\n}\n\n/**\n * Divide `deltaPx` em partes IGUAIS (não proporcionais) entre as linhas de\n * `base` — é o que faz a alça da base da tabela preservar uma linha que o\n * professor deixou alta de propósito, em vez de ela crescer/encolher mais\n * que as outras. Cada resultado passa pelo mesmo piso de `setRowHeight`\n * (`MIN_LINHA_PX`), então encolher demais nunca estoura para negativo — a\n * conta simplesmente para no piso.\n *\n * Pura e sem DOM: é o coração da alça de \"altura total\" e do braço vertical\n * da alça de canto, testável sem montar o editor.\n */\nexport function distribuirAltura(base: number[], deltaPx: number): number[] {\n if (base.length === 0) return base;\n const fatia = deltaPx / base.length;\n return base.map((h) => Math.max(MIN_LINHA_PX, Math.round(h + fatia)));\n}\n\n/**\n * Estado de arrasto — um só ref, discriminado por `tipo`, para as cinco\n * famílias de alça (divisa de coluna, largura total, divisa de linha, altura\n * total, canto). Em TODOS os ramos o delta do `onPointerMove` é calculado\n * contra o que foi lido aqui no `pointerdown`, nunca contra o modelo/DOM\n * corrente — é a regra que mata o emborrachado (ver `deltaPctDoArrasto` e o\n * brief da Task 3 para a medição do defeito original na coluna).\n */\ntype Arrasto =\n | { tipo: \"coluna\"; boundary: number; startX: number; baseWidths: number[]; tableWidthPx: number }\n | { tipo: \"larguraTotal\"; startX: number; baseWidthPct: number; utilPx: number }\n | { tipo: \"linha\"; row: number; startY: number; baseHeightPx: number }\n | { tipo: \"alturaTotal\"; startY: number; baseHeights: number[] }\n | {\n tipo: \"canto\";\n startX: number;\n startY: number;\n baseWidthPct: number;\n utilPx: number;\n baseHeights: number[];\n };\n\n/**\n * Renders absolute-positioned drag handles for every resizable boundary of a\n * table: column divisas, the table's own right edge (width), row divisas,\n * the table's own bottom edge (height, distributed evenly across rows via\n * `distribuirAltura`), and a corner handle that drives both axes at once.\n *\n * O delta é sempre calculado contra o estado do MODELO (ou, para linhas, do\n * DOM RENDERIZADO — ver nota abaixo) lido uma única vez no `pointerdown`,\n * nunca contra o corrente: acumular contra o DOM/modelo corrente é o que\n * produzia o salto elástico que a Task 3 matou para colunas, e o mesmo vale\n * aqui para as outras quatro famílias.\n *\n * Altura de linha é MÍNIMO, não largura — por isso a base de uma alça de\n * linha/altura-total/canto é lida do LAYOUT MEDIDO (`layout.bottoms`), não\n * do atributo do modelo: se o modelo não tem `rowHeights` (ou tem um mínimo\n * menor que o conteúdo), o `<tr>` renderiza mais alto que o mínimo gravado, e\n * ancorar no mínimo faria o arrasto de CRESCER ter uma \"zona morta\" antes de\n * a borda começar a se mexer. Ancorar no renderizado faz crescer colar no\n * dedo desde o primeiro pixel — e faz ENCOLHER abaixo do conteúdo gravar o\n * valor sem mover o desenho, que é exatamente o comportamento esperado (ver\n * `onPointerUp`/`measure` abaixo).\n *\n * No `pointerup` a alça reancora via `measure()` — aqui isso importa MAIS do\n * que na coluna: como altura é mínimo, arrastar a base para cima do que o\n * conteúdo permite grava o valor mas NÃO move o desenho, e a alça (se não\n * reancorada) ficaria flutuando onde o dedo soltou, longe da borda real.\n * `measure()` NÃO é chamado durante o `pointermove` — chamá-lo ali voltaria\n * a ler o renderizado a cada quadro e reintroduziria o defeito por outro\n * caminho, agora nas linhas.\n *\n * Lives as a sibling of the `<table>` inside a `position: relative` wrapper.\n * Handles are `contentEditable={false}` so they don't interfere with caret\n * placement inside cells.\n */\nexport function TableResizeOverlay({ tableRef, blockIndex, cols, rows }: Props): JSX.Element | null {\n const editor = useEditorContext();\n // Handle positions (column rights, row bottoms, in px relative to the\n // table's own top-left) and the table's own rendered box. Recomputed after\n // every layout via `measure()`.\n const [layout, setLayout] = useState<{\n rights: number[];\n bottoms: number[];\n width: number;\n height: number;\n } | null>(null);\n const arrastoRef = useRef<Arrasto | null>(null);\n\n type Layout = { rights: number[]; bottoms: number[]; width: number; height: number };\n\n /**\n * Mede column right-edges e row bottom-edges relativos ao canto\n * superior-esquerdo da própria tabela — SEMPRE contra o DOM corrente, sem\n * depender do estado `layout` (que só é atualizado quando `measure()`\n * decide chamar `setLayout`, ou seja, pode estar um `ResizeObserver` atrás\n * do real). Usada tanto por `measure()` (pra atualizar o estado usado no\n * posicionamento visual das alças) quanto pelos `pointerdown` de\n * linha/altura-total/canto (pra garantir que `baseHeightPx`/`baseHeights`\n * nunca partam de uma medição velha).\n */\n const measureNow = useCallback((): Layout | null => {\n const table = tableRef.current;\n if (!table) return null;\n const tableRect = table.getBoundingClientRect();\n // Prefer measuring via `<col>` elements when present.\n const cgCols = table.querySelectorAll<HTMLTableColElement>(\"colgroup > col\");\n let rights: number[] = [];\n if (cgCols.length === cols) {\n let acc = 0;\n cgCols.forEach((c) => {\n acc += c.getBoundingClientRect().width;\n rights.push(acc);\n });\n } else {\n // Fallback: use the first row's <td> bounding rects.\n const firstRowCells = table.querySelectorAll<HTMLTableCellElement>(\"tbody > tr:first-child > td\");\n let acc = 0;\n firstRowCells.forEach((td) => {\n // Account for colSpan: split the width evenly across the spanned cols.\n const cs = Math.max(1, td.colSpan || 1);\n const w = td.getBoundingClientRect().width / cs;\n for (let i = 0; i < cs; i++) {\n acc += w;\n rights.push(acc);\n }\n });\n }\n if (rights.length !== cols) {\n // Column shape mismatch — bail out rather than place handles in wrong spots.\n return null;\n }\n // Row bottoms — same accumulating approach, via each `<tr>`'s rendered\n // height. Rows stack directly under the table's top edge (no <thead>\n // here), so the accumulation starts at 0 exactly like the column rights\n // above.\n const trs = table.querySelectorAll<HTMLTableRowElement>(\"tbody > tr\");\n const bottoms: number[] = [];\n let accY = 0;\n trs.forEach((tr) => {\n accY += tr.getBoundingClientRect().height;\n bottoms.push(accY);\n });\n // Row/height handles only make sense when this DOM covers the WHOLE\n // table. A table split across pages renders only the first fragment's\n // rows here (`isFirstFragment` in NodeView) — `trs.length < rows` in\n // that case. Rather than null out the entire overlay (which would also\n // kill the still-valid column handles), just suppress the row-based\n // handles: `bottoms` stays empty and callers skip them.\n return {\n rights,\n bottoms: trs.length === rows ? bottoms : [],\n width: tableRect.width,\n height: tableRect.height,\n };\n }, [cols, rows, tableRef]);\n\n const measure = useCallback(() => {\n setLayout(measureNow());\n }, [measureNow]);\n\n useLayoutEffect(() => {\n measure();\n }, [measure]);\n\n useEffect(() => {\n const table = tableRef.current;\n if (!table) return;\n const ro = new ResizeObserver(() => measure());\n ro.observe(table);\n return () => ro.disconnect();\n }, [measure, tableRef]);\n\n const onPointerUp = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n arrastoRef.current = null;\n // Remede: reancora a alça na borda REAL (renderizada). Necessário\n // sempre que o arrasto pode ter batido num piso antes do dedo — coluna\n // no MIN_COLUNA_PCT, linha no MIN_LINHA_PX ou no próprio conteúdo (que\n // não tem \"mínimo\" nenhum pra bater, mas também não obedece um mínimo\n // MENOR que ele mesmo). Chamado só aqui — nunca durante o pointermove\n // (ver docstring do componente).\n measure();\n try {\n (e.currentTarget as Element).releasePointerCapture(e.pointerId);\n } catch {\n /* ignore */\n }\n },\n [measure],\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n const d = arrastoRef.current;\n if (!d) return;\n switch (d.tipo) {\n case \"coluna\": {\n const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.tableWidthPx);\n editor.setColumnBoundary(blockIndex, d.boundary, dPct, d.baseWidths);\n break;\n }\n case \"larguraTotal\": {\n const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.utilPx);\n editor.setTableWidth(blockIndex, d.baseWidthPct + dPct);\n break;\n }\n case \"linha\": {\n editor.setRowHeight(blockIndex, d.row, d.baseHeightPx + (e.clientY - d.startY));\n break;\n }\n case \"alturaTotal\": {\n editor.setRowHeights(blockIndex, distribuirAltura(d.baseHeights, e.clientY - d.startY));\n break;\n }\n case \"canto\": {\n const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.utilPx);\n editor.setTableWidth(blockIndex, d.baseWidthPct + dPct);\n editor.setRowHeights(blockIndex, distribuirAltura(d.baseHeights, e.clientY - d.startY));\n break;\n }\n }\n },\n [blockIndex, editor],\n );\n\n // `.ed-table-wrap` is always the table's parent (see TableView) and is the\n // containing block the inline `width:X%` style resolves against.\n const containerWidthPxOf = useCallback((table: HTMLTableElement, tableWidthPx: number): number => {\n return table.parentElement?.getBoundingClientRect().width || tableWidthPx;\n }, []);\n\n const onColumnPointerDown = useCallback(\n (boundary: number) => (e: React.PointerEvent<HTMLDivElement>) => {\n // Prevent the editor from losing focus / caret being placed on the handle.\n e.preventDefault();\n e.stopPropagation();\n const table = tableRef.current;\n if (!table) return;\n const tableWidthPx = table.getBoundingClientRect().width || 1;\n const attrs = editor.doc.getBlockAttrs(blockIndex);\n const baseWidths = normalizarLarguras(attrs.colWidths as number[] | undefined, cols);\n arrastoRef.current = { tipo: \"coluna\", boundary, startX: e.clientX, baseWidths, tableWidthPx };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [tableRef, editor, blockIndex, cols],\n );\n\n const onTableWidthPointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n const table = tableRef.current;\n if (!table) return;\n const tableWidthPx = table.getBoundingClientRect().width || 1;\n // `<table style=\"width:X%\">` is a percentage of `.ed-table-wrap`\n // (which has no width of its own and simply fills the page column),\n // NOT of the table's own current width. Whenever `baseWidthPct !== 100`\n // those two differ, and using `tableWidthPx` there overshoots by a\n // factor of `100 / baseWidthPct` — invisible on a fresh 100%-wide\n // table, but a second drag of the right edge after a resize reproduces\n // the exact rubber-banding Task 3 killed, just on the outer edge.\n const utilPx = containerWidthPxOf(table, tableWidthPx);\n const attrs = editor.doc.getBlockAttrs(blockIndex);\n const baseWidthPct = typeof attrs.tableWidth === \"number\" ? attrs.tableWidth : 100;\n arrastoRef.current = { tipo: \"larguraTotal\", startX: e.clientX, baseWidthPct, utilPx };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [tableRef, editor, blockIndex, containerWidthPxOf],\n );\n\n // Converte um array acumulado de bottoms em alturas por-linha.\n const heightsFromBottoms = (bottoms: number[]): number[] =>\n bottoms.map((b, i) => b - (i === 0 ? 0 : bottoms[i - 1]!));\n\n const onRowPointerDown = useCallback(\n (row: number) => (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n // Medição FRESCA, não o estado `layout` — que só é atualizado quando o\n // `ResizeObserver` dispara (assíncrono) e pode estar um frame atrás de\n // uma edição de conteúdo que acabou de mudar a altura desta linha.\n const m = measureNow();\n if (!m || m.bottoms.length === 0) return;\n const baseHeightPx = m.bottoms[row]! - (row === 0 ? 0 : m.bottoms[row - 1]!);\n arrastoRef.current = { tipo: \"linha\", row, startY: e.clientY, baseHeightPx };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [measureNow],\n );\n\n const onTableHeightPointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n const m = measureNow();\n if (!m || m.bottoms.length === 0) return;\n arrastoRef.current = { tipo: \"alturaTotal\", startY: e.clientY, baseHeights: heightsFromBottoms(m.bottoms) };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [measureNow],\n );\n\n const onCornerPointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n const table = tableRef.current;\n const m = measureNow();\n if (!table || !m) return;\n const tableWidthPx = table.getBoundingClientRect().width || 1;\n const utilPx = containerWidthPxOf(table, tableWidthPx);\n const attrs = editor.doc.getBlockAttrs(blockIndex);\n const baseWidthPct = typeof attrs.tableWidth === \"number\" ? attrs.tableWidth : 100;\n arrastoRef.current = {\n tipo: \"canto\",\n startX: e.clientX,\n startY: e.clientY,\n baseWidthPct,\n utilPx,\n baseHeights: heightsFromBottoms(m.bottoms),\n };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [tableRef, editor, blockIndex, measureNow, containerWidthPxOf],\n );\n\n if (!layout) return null;\n\n const colHandleStyle = (x: number): CSSProperties => ({\n position: \"absolute\",\n top: 0,\n left: `${x - 3}px`,\n width: \"6px\",\n height: `${layout.height}px`,\n cursor: \"col-resize\",\n zIndex: 5,\n });\n const rowHandleStyle = (y: number): CSSProperties => ({\n position: \"absolute\",\n left: 0,\n top: `${y - 3}px`,\n width: `${layout.width}px`,\n height: \"6px\",\n cursor: \"row-resize\",\n zIndex: 5,\n });\n const cornerHandleStyle: CSSProperties = {\n position: \"absolute\",\n left: `${layout.width - 5}px`,\n top: `${layout.height - 5}px`,\n width: \"10px\",\n height: \"10px\",\n cursor: \"nwse-resize\",\n zIndex: 6,\n };\n\n return (\n <>\n {/* Column divisas + the table's own right edge (width). */}\n {layout.rights.map((x, c) =>\n c < cols - 1 ? (\n <div\n key={`col-${c}`}\n contentEditable={false}\n className=\"ed-col-resize-handle\"\n style={colHandleStyle(x)}\n onPointerDown={onColumnPointerDown(c)}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label={`Redimensionar coluna ${c + 1}`}\n role=\"separator\"\n />\n ) : (\n <div\n key=\"col-total\"\n contentEditable={false}\n className=\"ed-col-resize-handle\"\n style={colHandleStyle(x)}\n onPointerDown={onTableWidthPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label=\"Largura da tabela\"\n role=\"separator\"\n />\n ),\n )}\n {/* Row divisas + the table's own bottom edge (height). */}\n {layout.bottoms.map((y, r) =>\n r < rows - 1 ? (\n <div\n key={`row-${r}`}\n contentEditable={false}\n className=\"ed-row-resize-handle\"\n style={rowHandleStyle(y)}\n onPointerDown={onRowPointerDown(r)}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label={`Redimensionar linha ${r + 1}`}\n role=\"separator\"\n />\n ) : (\n <div\n key=\"row-total\"\n contentEditable={false}\n className=\"ed-row-resize-handle\"\n style={rowHandleStyle(y)}\n onPointerDown={onTableHeightPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label=\"Altura da tabela\"\n role=\"separator\"\n />\n ),\n )}\n {/* Corner: drives width + height at once. */}\n <div\n contentEditable={false}\n className=\"ed-table-corner-handle\"\n style={cornerHandleStyle}\n onPointerDown={onCornerPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label=\"Redimensionar tabela\"\n role=\"separator\"\n />\n </>\n );\n}\n","import { useLayoutEffect, useRef, useState, type RefObject } from \"react\";\n\n/**\n * Sub-phase 3.2 — page layout with intra-paragraph fragmentation.\n *\n * Two-phase render cycle:\n * 1. \"measure\" phase: editor renders with the default (unfragmented) layout\n * — every top-level on page 1. After the DOM commits, `usePageLayout`\n * reads each top-level's geometry, runs `computeLayout`, and sets the\n * real layout (with page splits and fragment offsets).\n * 2. \"stable\" phase: editor renders the real layout. No further measurement\n * until `deps` change (typically `[topLevels]`), at which point we go\n * back to step 1.\n *\n * Fragmentation only applies to \"fragmentable\" top-levels (paragraph, heading,\n * blockquote — single Y.Text blocks). List groups and code blocks are placed\n * atomically: if they don't fit they jump to the next page and bleed if larger\n * than a page.\n */\n\nexport interface PageGeometry {\n /** Page width in CSS px (A4 @ 96dpi = ~794). */\n width: number;\n /** Page height in CSS px (A4 @ 96dpi = ~1123). */\n height: number;\n marginTop: number;\n marginBottom: number;\n marginLeft: number;\n marginRight: number;\n /**\n * Espaço adicional somado ao marginTop só na 1ª página. Útil para clientes\n * que rendem um header fixo (timbrado, identificação) na pág 1 via\n * `renderPageHeader` e não querem que esse espaço seja reservado nas demais.\n * Quando omitido, todas as páginas têm a mesma `contentHeight`.\n */\n firstPageExtraTop?: number;\n}\n\nexport const A4_PAGE: PageGeometry = {\n width: 794,\n height: 1123,\n marginTop: 96,\n marginBottom: 96,\n marginLeft: 96,\n marginRight: 96,\n};\n\nexport function contentHeight(g: PageGeometry, pageIndex: number = 0): number {\n const extra = pageIndex === 0 ? g.firstPageExtraTop ?? 0 : 0;\n return g.height - g.marginTop - g.marginBottom - extra;\n}\n\nexport function contentWidth(g: PageGeometry): number {\n return g.width - g.marginLeft - g.marginRight;\n}\n\nexport interface BlockFragment {\n index: number;\n start: number;\n end: number;\n}\n\nexport interface TableRowFragment {\n /** 0, 1, 2... sequential fragment id within the table. */\n index: number;\n /** First row included (inclusive). */\n rowStart: number;\n /** First row NOT included (exclusive). */\n rowEnd: number;\n}\n\nexport interface ListItemRangeFragment {\n /** 0, 1, 2... sequential fragment id within the list group. */\n index: number;\n /** First top-level item (level 0) included (inclusive). */\n itemStart: number;\n /** First top-level item (level 0) NOT included (exclusive). */\n itemEnd: number;\n}\n\nexport interface PageSlot {\n topLevelIndex: number;\n /** Set when this slot represents a paragraph/heading/blockquote line-level fragment. */\n fragment?: BlockFragment;\n /** Set when this slot is a row-range fragment of a table (Sub-phase 4.6). */\n tableFragment?: TableRowFragment;\n /** Set when this slot is an item-range fragment of an ordered/bullet list. */\n listFragment?: ListItemRangeFragment;\n}\n\nexport interface PageEntry {\n slots: PageSlot[];\n}\n\nexport interface PageLayout {\n pages: PageEntry[];\n}\n\nexport function defaultPageLayout(topLevelCount: number): PageLayout {\n return {\n pages: [\n {\n slots: Array.from({ length: topLevelCount }, (_, i) => ({ topLevelIndex: i })),\n },\n ],\n };\n}\n\nexport type PaginationPhase = \"measure\" | \"stable\";\n\nexport interface PageLayoutResult {\n layout: PageLayout;\n /**\n * \"measure\" between a deps change and the post-commit measurement; \"stable\"\n * once the layout matches the rendered DOM. Consumers can hide the caret /\n * suppress visible flashes during \"measure\" (the DOM is briefly in the\n * unfragmented intermediate state and the native caret may jump to the start\n * of the line before useLayoutEffect re-applies the model selection).\n */\n phase: PaginationPhase;\n}\n\nexport function usePageLayout(\n rootRef: RefObject<HTMLElement>,\n geometry: PageGeometry,\n topLevelCount: number,\n deps: ReadonlyArray<unknown>,\n): PageLayoutResult {\n const [phase, setPhase] = useState<PaginationPhase>(\"measure\");\n const [layout, setLayout] = useState<PageLayout>(() => defaultPageLayout(topLevelCount));\n const lastDepsRef = useRef<unknown[] | null>(null);\n\n // Reset effect: when `deps` (consumer-provided), `topLevelCount`, or\n // `geometry` change, go back to the unfragmented default layout and\n // re-enter the measure phase.\n //\n // Why a useLayoutEffect (instead of `setState` in render body, the previous\n // approach): with concurrent rendering / Strict Mode, mutating\n // `lastDepsRef.current` during render is fragile — React may discard a\n // render pass while keeping the ref mutation, so a subsequent pass sees\n // \"deps unchanged\" and the reset never fires. The historical symptom was\n // a doc whose N blocks arrived entirely via remote `Y.applyUpdate`\n // (e.g. an Hocuspocus / y-websocket sync): the editor's snapshot updated\n // to N in `useEditor`, but `layout` stayed at 1 page × 1 slot — the reset\n // condition in the old render-body path silently missed firing.\n //\n // Doing the reset in a useLayoutEffect runs after the commit (so refs are\n // stable and visible), but BEFORE paint (so the user never sees a frame\n // with stale layout). The phase change schedules a synchronous re-render\n // and the measure effect below picks up immediately.\n //\n // Equality uses `shallowArrayEqual` for deps (consumer's responsibility to\n // pass stable references / structural keys) and `layoutsDeepEqual` for\n // the layout itself (fragment-aware — see the historical bug where shallow\n // equality let a fragmented layout matching `1 page × 1 slot` survive the\n // reset and lock subsequent measures via `!isUnfragmented(layout)`).\n useLayoutEffect(() => {\n const currentDeps = [...deps, topLevelCount, geometry];\n if (lastDepsRef.current !== null && shallowArrayEqual(lastDepsRef.current, currentDeps)) {\n return;\n }\n lastDepsRef.current = currentDeps;\n setLayout((prev) => {\n const next = defaultPageLayout(topLevelCount);\n return layoutsDeepEqual(prev, next) ? prev : next;\n });\n setPhase(\"measure\");\n // No dependency array: this effect runs after every commit and decides\n // internally whether deps changed. We can't put `[...deps, topLevelCount, geometry]`\n // here because `deps` is a variable-length array from the caller — React's\n // hook deps must be a fixed-shape list. Running every commit is cheap (one\n // shallow array compare).\n });\n\n // Measure effect: when in the measure phase with an unfragmented layout,\n // read the DOM and compute the real (possibly multi-page, possibly\n // fragmented) layout. Returns to the \"stable\" phase.\n useLayoutEffect(() => {\n if (phase !== \"measure\") return;\n const root = rootRef.current;\n if (!root) return;\n // Only safe to measure when the DOM is in the unfragmented state.\n if (!isUnfragmented(layout)) return;\n const next = computeLayout(root, geometry, topLevelCount);\n if (!layoutsDeepEqual(layout, next)) setLayout(next);\n setPhase(\"stable\");\n }, [phase, layout, geometry, topLevelCount, rootRef]);\n\n return { layout, phase };\n}\n\n// ---------- Layout computation ----------\n\nfunction isUnfragmented(layout: PageLayout): boolean {\n return (\n layout.pages.length === 1 &&\n layout.pages[0].slots.every(\n (s) =>\n s.fragment === undefined &&\n s.tableFragment === undefined &&\n s.listFragment === undefined,\n )\n );\n}\n\nfunction computeLayout(\n root: HTMLElement,\n geometry: PageGeometry,\n topLevelCount: number,\n): PageLayout {\n const tops = collectTopLevelElements(root);\n if (tops.length === 0) return defaultPageLayout(topLevelCount);\n\n // `firstPageExtraTop` faz a pág 0 ter content height menor; usar uma função\n // permite recomputar quando empurramos novas páginas.\n const limitFor = (pageIndex: number) => contentHeight(geometry, pageIndex);\n const pages: PageEntry[] = [{ slots: [] }];\n let currentPage = pages[0];\n let limit = limitFor(0);\n let used = 0;\n\n const cap = Math.min(tops.length, topLevelCount);\n for (let i = 0; i < cap; i++) {\n const el = tops[i];\n const h = elTotalHeight(el);\n\n if (used + h <= limit) {\n currentPage.slots.push({ topLevelIndex: i });\n used += h;\n continue;\n }\n\n if (isFragmentable(el)) {\n const result = placeFragmentedBlock(el, i, pages, used, limitFor);\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = result.lastUsed;\n continue;\n }\n\n if (isTableTopLevel(el)) {\n const result = placeFragmentedTable(el, i, pages, used, limitFor);\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = result.lastUsed;\n continue;\n }\n\n if (isListTopLevel(el)) {\n const result = placeFragmentedList(el, i, pages, used, limitFor);\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = result.lastUsed;\n continue;\n }\n\n // Atomic top-level that doesn't fit\n if (currentPage.slots.length === 0) {\n // empty current page; place anyway (bleed if huge)\n currentPage.slots.push({ topLevelIndex: i });\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n } else {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n currentPage.slots.push({ topLevelIndex: i });\n used = h;\n }\n }\n\n // Strip trailing empty page if any\n if (pages.length > 1 && pages[pages.length - 1].slots.length === 0) pages.pop();\n\n return { pages };\n}\n\nfunction placeFragmentedBlock(\n el: HTMLElement,\n topLevelIndex: number,\n pages: PageEntry[],\n startUsed: number,\n limitFor: (pageIndex: number) => number,\n): { lastUsed: number } {\n let limit = limitFor(pages.length - 1);\n const totalChars = getBlockTextLength(el);\n if (totalChars === 0) {\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n // Measure every original line. Splitting only at line boundaries is what\n // makes fragmentation work correctly: when fragment K starts at an original\n // line boundary AND is rendered at the same width, its line wrapping matches\n // the original exactly, so our measured height predicts the rendered height.\n const lines = measureLines(el);\n if (lines.length === 0) {\n // Couldn't measure (no text nodes?) — place atomically as last resort.\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n let lineIdx = 0;\n let currentPage = pages[pages.length - 1];\n let used = startUsed;\n let fragIndex = 0;\n\n while (lineIdx < lines.length) {\n const available = Math.max(0, limit - used);\n if (available <= 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n\n // Greedy: include as many consecutive lines as fit. bestK is the last fitting line index.\n const startTop = lines[lineIdx].top;\n let bestK = -1;\n for (let k = lineIdx; k < lines.length; k++) {\n const h = lines[k].bottom - startTop;\n if (h <= available) bestK = k;\n else break;\n }\n\n if (bestK < 0) {\n // Not even one line fits in the remaining space.\n if (currentPage.slots.length === 0) {\n // Page is empty; force-place a single oversized line (bleed).\n bestK = lineIdx;\n } else {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n }\n\n const fragStart = lines[lineIdx].start;\n const fragEnd = bestK + 1 < lines.length ? lines[bestK + 1].start : totalChars;\n const fragHeight = lines[bestK].bottom - startTop;\n\n currentPage.slots.push({\n topLevelIndex,\n fragment: { index: fragIndex, start: fragStart, end: fragEnd },\n });\n used += fragHeight;\n fragIndex++;\n lineIdx = bestK + 1;\n\n if (lineIdx < lines.length) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n }\n }\n\n return { lastUsed: used };\n}\n\ninterface LineMetric {\n /** Char offset (within the block) of the first char on this line. */\n start: number;\n /** Viewport-y top of the line. */\n top: number;\n /** Viewport-y bottom of the line. */\n bottom: number;\n}\n\n/**\n * Walk every text node inside `el`, measuring each character's rect to detect\n * line boundaries. Linear in total character count.\n *\n * Memoized by `(offsetWidth, textContent)`: a block whose width and text\n * haven't changed (the common case during typing — only ONE block changes per\n * keystroke) skips the measurement entirely. This is the perf core of Sub-phase\n * 3.3's incremental repagination story.\n *\n * NOTE: cached `top`/`bottom` are viewport-absolute, but `placeFragmentedBlock`\n * only ever uses RELATIVE differences (`lines[k].bottom - lines[j].top`), which\n * are invariant to scroll/layout offsets — so cached values stay correct.\n */\nconst lineMetricsCache = new Map<string, LineMetric[]>();\nconst LINE_METRICS_CACHE_LIMIT = 200;\n\nfunction measureLines(el: HTMLElement): LineMetric[] {\n const text = el.textContent ?? \"\";\n const cacheKey = `${el.offsetWidth}|${text}`;\n const cached = lineMetricsCache.get(cacheKey);\n if (cached) {\n // LRU touch\n lineMetricsCache.delete(cacheKey);\n lineMetricsCache.set(cacheKey, cached);\n return cached;\n }\n\n const fresh = measureLinesUncached(el);\n lineMetricsCache.set(cacheKey, fresh);\n if (lineMetricsCache.size > LINE_METRICS_CACHE_LIMIT) {\n const oldest = lineMetricsCache.keys().next().value;\n if (oldest !== undefined) lineMetricsCache.delete(oldest);\n }\n return fresh;\n}\n\nfunction measureLinesUncached(el: HTMLElement): LineMetric[] {\n const range = document.createRange();\n const lines: LineMetric[] = [];\n\n // Walk text nodes AND image embeds — embeds count as 1 char in offset\n // arithmetic and contribute their bounding rect as a line (or part of one).\n const walker = document.createTreeWalker(\n el,\n NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,\n blockBoundaryFilter(el),\n );\n let n: Node | null;\n let charOffset = 0;\n\n while ((n = walker.nextNode())) {\n if (n.nodeType === Node.TEXT_NODE) {\n const text = n.textContent ?? \"\";\n for (let i = 0; i < text.length; i++) {\n try {\n range.setStart(n, i);\n range.setEnd(n, i + 1);\n } catch {\n continue;\n }\n const rect = range.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) continue;\n const last = lines[lines.length - 1];\n if (!last || rect.top >= last.bottom) {\n lines.push({ start: charOffset + i, top: rect.top, bottom: rect.bottom });\n } else {\n last.top = Math.min(last.top, rect.top);\n last.bottom = Math.max(last.bottom, rect.bottom);\n }\n }\n charOffset += text.length;\n continue;\n }\n if (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embed === \"image\"\n ) {\n const rect = (n as HTMLElement).getBoundingClientRect();\n if (rect.height > 0) {\n const last = lines[lines.length - 1];\n if (!last || rect.top >= last.bottom) {\n lines.push({ start: charOffset, top: rect.top, bottom: rect.bottom });\n } else {\n last.top = Math.min(last.top, rect.top);\n last.bottom = Math.max(last.bottom, rect.bottom);\n }\n }\n charOffset += 1;\n }\n }\n return lines;\n}\n\n// ---------- DOM measurement primitives ----------\n\nfunction elTotalHeight(el: HTMLElement): number {\n const cs = getComputedStyle(el);\n const m = parseFloat(cs.marginTop || \"0\") + parseFloat(cs.marginBottom || \"0\");\n return el.offsetHeight + m;\n}\n\nfunction isFragmentable(el: HTMLElement): boolean {\n // Top-level fragmentable types: paragraph, heading, blockquote.\n // Tables fragment via `placeFragmentedTable` (row-level), not the line engine.\n const type = el.getAttribute(\"data-block-type\");\n if (type === \"paragraph\") return true;\n if (type === \"heading\") return true;\n if (type === \"blockquote\") return true;\n return false;\n}\n\n/**\n * The top-level for a table block is a `<div class=\"ed-table-wrap\">` wrapper —\n * the `data-block-type=\"table\"` attribute lives on the nested `<table>`.\n */\nfunction isTableTopLevel(el: HTMLElement): boolean {\n return el.querySelector(':scope > table[data-block-type=\"table\"]') !== null;\n}\n\ninterface PlaceFragmentedTableResult { lastUsed: number; }\n\n/**\n * Sub-phase 4.6 — split a table that doesn't fit on the current page across\n * pages at row boundaries.\n *\n * Constraints:\n * - A break between rows `r` and `r+1` is INVALID when any cell with\n * `rowSpan > 1` whose owner sits at row `r' <= r` extends into row `r+1`.\n * We compute this by walking each row's `<td>`s: a `rowSpan = N > 1` at row\n * `r` blocks the boundaries `(r, r+1), (r+1, r+2), … (r+N-2, r+N-1)`.\n *\n * - If no clean break fits the available height (e.g. a very tall span at the\n * top of the table), and the current page already has content, we move to\n * the next page and try again. If even an empty page can't fit a valid\n * prefix, we bleed the smallest legal prefix rather than loop forever.\n *\n * Heights come from `<tr>.getBoundingClientRect()` — these are measured against\n * the unfragmented render, but row layout is invariant under fragmentation as\n * long as the `<colgroup>` widths don't change (they don't), and as long as a\n * span isn't cut (which is exactly the constraint above).\n */\nfunction placeFragmentedTable(\n el: HTMLElement,\n topLevelIndex: number,\n pages: PageEntry[],\n startUsed: number,\n limitFor: (pageIndex: number) => number,\n): PlaceFragmentedTableResult {\n let limit = limitFor(pages.length - 1);\n const table = el.querySelector(':scope > table[data-block-type=\"table\"]') as HTMLTableElement | null;\n const tbody = table?.querySelector(':scope > tbody');\n const trs = tbody ? (Array.from(tbody.children) as HTMLTableRowElement[]) : [];\n if (trs.length === 0) {\n // Empty / no rows — fall back to atomic.\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n const rowHeights = trs.map((tr) => tr.getBoundingClientRect().height);\n\n // Invalid break boundaries — indices `k` for which the break between row `k`\n // and `k+1` is blocked by a rowspan crossing it.\n const invalid = new Set<number>();\n for (let r = 0; r < trs.length; r++) {\n const tds = Array.from(trs[r].children) as HTMLTableCellElement[];\n for (const td of tds) {\n const rs = td.rowSpan || 1;\n if (rs > 1) {\n for (let k = r; k <= r + rs - 2 && k < trs.length - 1; k++) {\n invalid.add(k);\n }\n }\n }\n }\n\n // Vertical padding/margin around the table proper, distributed conservatively\n // onto the FIRST fragment only — keeps subsequent fragments compact.\n const wrapHeight = elTotalHeight(el);\n const tableHeight = table ? table.getBoundingClientRect().height : 0;\n const chrome = Math.max(0, wrapHeight - tableHeight);\n\n let rowIdx = 0;\n let used = startUsed;\n let fragIndex = 0;\n let currentPage = pages[pages.length - 1];\n\n while (rowIdx < trs.length) {\n const overhead = fragIndex === 0 ? chrome : 0;\n const available = Math.max(0, limit - used - overhead);\n if (available <= 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n\n // Greedy: keep extending the run as long as height fits and we haven't\n // passed a valid break boundary. Track the LAST valid `bestEnd`.\n let bestEnd = -1;\n let accH = 0;\n for (let k = rowIdx; k < trs.length; k++) {\n accH += rowHeights[k];\n if (accH > available) break;\n if (k === trs.length - 1 || !invalid.has(k)) bestEnd = k + 1;\n }\n\n if (bestEnd < 0) {\n if (currentPage.slots.length === 0) {\n // Empty page yet still no valid prefix fits — find the smallest legal\n // prefix and bleed (better than an infinite loop).\n let k = rowIdx;\n while (k < trs.length - 1 && invalid.has(k)) k++;\n bestEnd = k + 1;\n } else {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n }\n\n let fragHeight = 0;\n for (let k = rowIdx; k < bestEnd; k++) fragHeight += rowHeights[k];\n if (fragIndex === 0) fragHeight += chrome;\n\n currentPage.slots.push({\n topLevelIndex,\n tableFragment: { index: fragIndex, rowStart: rowIdx, rowEnd: bestEnd },\n });\n used += fragHeight;\n fragIndex++;\n rowIdx = bestEnd;\n\n if (rowIdx < trs.length) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n }\n }\n\n return { lastUsed: used };\n}\n\n/**\n * A top-level for a list group is the `<ol>` or `<ul>` rendered by\n * `renderListTree`. Its direct `<li>` children are the level-0 entries of\n * the group; nested sub-lists live INSIDE those `<li>`s and travel with them.\n */\nfunction isListTopLevel(el: HTMLElement): boolean {\n if (el.tagName !== \"OL\" && el.tagName !== \"UL\") return false;\n return el.classList.contains(\"ed-list\");\n}\n\ninterface PlaceFragmentedListResult { lastUsed: number; }\n\n/**\n * Split a list group that doesn't fit on the current page across pages at\n * top-level `<li>` boundaries. Nested sub-lists stay attached to their parent\n * `<li>` (we never break inside a nested list — that's reserved for the rare\n * case where a single top-level item is itself taller than a full page, in\n * which case we bleed it).\n *\n * Heights come from `<li>.getBoundingClientRect()` measured against the\n * unfragmented render. They include the nested sub-list height when present.\n */\nfunction placeFragmentedList(\n el: HTMLElement,\n topLevelIndex: number,\n pages: PageEntry[],\n startUsed: number,\n limitFor: (pageIndex: number) => number,\n): PlaceFragmentedListResult {\n let limit = limitFor(pages.length - 1);\n\n // Direct <li> children of the top-level <ol>/<ul>. Filter strictly by\n // `.ed-listitem` class so any other DOM injected by the consumer (markers,\n // dividers) doesn't get counted as an item.\n const items: HTMLLIElement[] = [];\n for (const c of Array.from(el.children)) {\n if (c instanceof HTMLLIElement && c.classList.contains(\"ed-listitem\")) {\n items.push(c);\n }\n }\n\n if (items.length === 0) {\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n const heights = items.map((li) => elTotalHeight(li));\n // Vertical padding/margin contributed by the wrapping <ol>/<ul> itself\n // (margin-block + padding-block beyond the sum of item heights). We charge\n // this overhead only to the FIRST fragment's page so subsequent continuation\n // fragments don't double-count it.\n const sumHeights = heights.reduce((a, b) => a + b, 0);\n const wrapperOverhead = Math.max(0, elTotalHeight(el) - sumHeights);\n\n let i = 0;\n let currentPage = pages[pages.length - 1];\n let used = startUsed;\n let fragIndex = 0;\n\n while (i < items.length) {\n const available = Math.max(0, limit - used);\n // Greedy: include as many consecutive items as fit. bestK is the last\n // fitting item index.\n const isFirstFragment = fragIndex === 0;\n let bestK = -1;\n let runningH = isFirstFragment ? wrapperOverhead : 0;\n for (let k = i; k < items.length; k++) {\n runningH += heights[k];\n if (runningH <= available) bestK = k;\n else break;\n }\n\n if (bestK < 0) {\n // Not even one item fits in the remaining space.\n if (currentPage.slots.length === 0) {\n // Page is empty; force-place a single oversized item (bleed).\n bestK = i;\n } else {\n // Move to next page and retry.\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n }\n\n currentPage.slots.push({\n topLevelIndex,\n listFragment: { index: fragIndex, itemStart: i, itemEnd: bestK + 1 },\n });\n\n // Account for consumed height on the current page.\n let consumed = isFirstFragment ? wrapperOverhead : 0;\n for (let k = i; k <= bestK; k++) consumed += heights[k];\n used += consumed;\n\n i = bestK + 1;\n fragIndex++;\n if (i < items.length) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n }\n }\n\n return { lastUsed: used };\n}\n\nfunction collectTopLevelElements(root: HTMLElement): HTMLElement[] {\n // The root may be in one of three shapes:\n // - flat: root > top1, top2, ...\n // - paged simple: root > .ed-page > .ed-page-content > top1, top2, ...\n // - paged + fragments: same as above, but some \"tops\" are fragments of the\n // same block index. We don't expect to be called in\n // that state (caller guards on isUnfragmented), so\n // collecting direct children is fine.\n const pageContents = root.querySelectorAll<HTMLElement>(\":scope > .ed-page > .ed-page-content\");\n if (pageContents.length > 0) {\n const out: HTMLElement[] = [];\n pageContents.forEach((pc) => {\n Array.from(pc.children).forEach((c) => out.push(c as HTMLElement));\n });\n return out;\n }\n return Array.from(root.children) as HTMLElement[];\n}\n\n/**\n * Sum text-node lengths inside `el` plus 1 for each image embed, skipping any\n * descendant `[data-block-index]`. Mirrors the model's notion of \"length\".\n */\nfunction getBlockTextLength(el: HTMLElement): number {\n const walker = document.createTreeWalker(\n el,\n NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,\n blockBoundaryFilter(el),\n );\n let n: Node | null;\n let sum = 0;\n while ((n = walker.nextNode())) {\n if (n.nodeType === Node.TEXT_NODE) {\n sum += (n.textContent ?? \"\").length;\n } else if (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embed === \"image\"\n ) {\n sum += 1;\n }\n }\n return sum;\n}\n\nfunction blockBoundaryFilter(blockEl: HTMLElement): NodeFilter {\n return {\n acceptNode(n: Node) {\n let p: Node | null = n.parentNode;\n while (p && p !== blockEl) {\n if (p.nodeType === Node.ELEMENT_NODE) {\n const e = p as HTMLElement;\n if (e.dataset.blockIndex != null) return NodeFilter.FILTER_REJECT;\n }\n p = p.parentNode;\n }\n return NodeFilter.FILTER_ACCEPT;\n },\n };\n}\n\n// ---------- Equality helpers ----------\n\nfunction shallowArrayEqual(a: unknown[], b: unknown[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n\nfunction layoutsDeepEqual(a: PageLayout, b: PageLayout): boolean {\n if (a.pages.length !== b.pages.length) return false;\n for (let i = 0; i < a.pages.length; i++) {\n const sA = a.pages[i].slots;\n const sB = b.pages[i].slots;\n if (sA.length !== sB.length) return false;\n for (let j = 0; j < sA.length; j++) {\n if (sA[j].topLevelIndex !== sB[j].topLevelIndex) return false;\n const fA = sA[j].fragment;\n const fB = sB[j].fragment;\n if (fA || fB) {\n if (!fA || !fB) return false;\n if (fA.index !== fB.index || fA.start !== fB.start || fA.end !== fB.end) return false;\n }\n const tA = sA[j].tableFragment;\n const tB = sB[j].tableFragment;\n if (tA || tB) {\n if (!tA || !tB) return false;\n if (tA.index !== tB.index || tA.rowStart !== tB.rowStart || tA.rowEnd !== tB.rowEnd) {\n return false;\n }\n }\n const lA = sA[j].listFragment;\n const lB = sB[j].listFragment;\n if (lA || lB) {\n if (!lA || !lB) return false;\n if (lA.index !== lB.index || lA.itemStart !== lB.itemStart || lA.itemEnd !== lB.itemEnd) {\n return false;\n }\n }\n }\n }\n return true;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { MAX_INSERT_WIDTH } from \"./imageConstraints\";\nimport { usePageSettings } from \"./usePageSettings\";\nimport {\n EditorDocument,\n EditorHistory,\n collapsedSelection,\n dedentList as cmdDedentList,\n deleteTable as cmdDeleteTable,\n deleteTableColumn as cmdDeleteColumn,\n deleteTableRow as cmdDeleteRow,\n insertImage as cmdInsertImage,\n mergeDown as cmdMergeDown,\n mergeRight as cmdMergeRight,\n mergeSelection as cmdMergeSelection,\n moveEmbedAnchor as cmdMoveEmbedAnchor,\n setBlockAttrAtIndex as cmdSetBlockAttrAtIndex,\n setColumnBoundary as cmdSetColumnBoundary,\n setImageAttrs as cmdSetImageAttrs,\n setRowHeight as cmdSetRowHeight,\n setRowHeights as cmdSetRowHeights,\n splitCell as cmdSplitCell,\n tableRectSelection,\n getMarksInRange,\n indentList as cmdIndentList,\n insertAnswerLines as cmdInsertAnswerLines,\n insertTable as cmdInsertTable,\n insertTableColumn as cmdInsertColumn,\n insertTableRow as cmdInsertRow,\n isFormulaEmbed,\n isImageEmbed,\n isMarkUniformInRange,\n moveToNextCell as cmdMoveNextCell,\n moveToPrevCell as cmdMovePrevCell,\n orderedRange,\n removeMark as cmdRemoveMark,\n setBlockAttr as cmdSetBlockAttr,\n setCellAttr as cmdSetCellAttr,\n setBlockType as cmdSetBlockType,\n setMark as cmdSetMark,\n setTableWidth as cmdSetTableWidth,\n splitListItem as cmdSplitListItem,\n tableLocationOf,\n toggleList as cmdToggleList,\n toggleMark as cmdToggleMark,\n type AlignValue,\n type BlockAttrs,\n type AnswerLineSpacing,\n type BlockType,\n type TableBorderPreset,\n type CommandContext,\n type DeltaOp,\n type EmbedLoc,\n type ImageEmbed,\n type ListKind,\n type MarkAttrs,\n type MarkName,\n type PageSettings,\n type SerializedDocument,\n type Selection,\n type TableLocation,\n type TableRect,\n} from \"@sofereditor/core\";\n\nexport interface UseEditorOptions {\n document?: EditorDocument;\n /**\n * Quando definido, `insertImageFromFile` chama esta função pra obter a URL\n * final em vez de embutir o arquivo como data URL base64 no Y.Doc.\n *\n * Use isto quando o app tem storage externo (S3, Azure Blob, etc.) e não quer\n * inflar o documento com imagens base64. O componente `<Editor>` propaga o\n * mesmo upload para paste/drop/file picker.\n */\n uploadImage?: (file: File) => Promise<string>;\n}\n\nexport type PendingMarks = Partial<Record<MarkName, MarkAttrs[MarkName] | null>>;\n\nexport interface LinkRequest {\n initialHref: string;\n resolve: (href: string | null) => void;\n /** Model selection captured when the link was requested (bug #6). */\n selection: Selection;\n}\n\nexport interface PageConfigRequest {\n initial: PageSettings;\n resolve: (applied: boolean) => void;\n}\n\nexport type CaptionAlign = \"left\" | \"center\" | \"right\";\n\nexport interface ImageCaptionResult {\n caption: string;\n align: CaptionAlign;\n}\n\nexport interface ImageCaptionRequest {\n initialCaption: string;\n initialAlign: CaptionAlign;\n /**\n * Resolves with the new caption + alignment, or null on cancel. An empty\n * `caption` string means \"remove caption\" (align is ignored in that case).\n */\n resolve: (result: ImageCaptionResult | null) => void;\n}\n\nexport interface FormulaResult {\n latex: string;\n display: boolean;\n}\n\nexport interface FormulaRequest {\n initialLatex: string;\n initialDisplay: boolean;\n /** Resolve com a fórmula, ou null no cancelamento. */\n resolve: (result: FormulaResult | null) => void;\n}\n\nexport interface UseEditorResult {\n doc: EditorDocument;\n history: EditorHistory;\n /** Snapshot of the model, re-computed on every Y.js update. */\n snapshot: SerializedDocument;\n /** Current model selection. Use `setSelection` to move the caret. */\n selection: Selection;\n setSelection: (sel: Selection) => void;\n /** Stable getter — commands read this to know where the caret is. */\n getSelection: () => Selection;\n\n // ---- Mark API (added in Sub-phase 2.1) ----\n isMarkActive: (name: MarkName, value?: MarkAttrs[MarkName]) => boolean;\n getActiveMarks: () => Partial<Record<MarkName, MarkAttrs[MarkName] | \"mixed\">>;\n toggleMark: (name: MarkName, value?: MarkAttrs[MarkName]) => void;\n setMark: (name: MarkName, value: MarkAttrs[MarkName]) => void;\n removeMark: (name: MarkName) => void;\n\n // ---- Block API (added in Sub-phase 2.2) ----\n /** Returns the type of the focus block (or \"mixed\" when the selection covers heterogeneous blocks). */\n getBlockType: () => BlockType | \"mixed\";\n /** Returns one block attribute at the focus block, or \"mixed\" when not uniform across the selection. */\n getBlockAttr: <K extends keyof BlockAttrs>(key: K) => BlockAttrs[K] | \"mixed\" | undefined;\n setBlockType: (type: BlockType, attrs?: BlockAttrs) => void;\n setBlockAttr: <K extends keyof BlockAttrs>(key: K, value: BlockAttrs[K] | null) => void;\n setAlign: (value: AlignValue) => void;\n getAlign: () => AlignValue | \"mixed\" | undefined;\n /** Cor de fundo das células selecionadas (rect) ou da célula focada. `null` remove. No-op fora de tabela. */\n setCellBackground: (color: string | null) => void;\n /** Cor de fundo da célula focada; undefined fora de tabela ou sem cor. */\n getCellBackground: () => string | undefined;\n /** Onde as linhas da grade da tabela focada aparecem. No-op fora de tabela. */\n setTableBorderPreset: (preset: TableBorderPreset) => void;\n /** Preset da tabela focada; \"all\" fora de tabela ou quando ausente. */\n getTableBorderPreset: () => TableBorderPreset;\n /** Cor das linhas da grade da tabela focada. `null` restaura o padrão. No-op fora de tabela. */\n setTableBorderColor: (color: string | null) => void;\n /** Cor da tabela focada; undefined fora de tabela ou quando usa o padrão. */\n getTableBorderColor: () => string | undefined;\n\n // ---- List API (added in Sub-phase 2.3) ----\n isListActive: (kind: ListKind) => boolean;\n toggleList: (kind: ListKind) => void;\n indentList: () => void;\n dedentList: () => void;\n /** Returns true when handled (current block is a listItem); false to fall back to insertParagraph. */\n splitListItem: () => boolean;\n\n /** Marks queued for the next `insertText` (e.g. user pressed Cmd+B with a collapsed caret). */\n pendingMarks: PendingMarks;\n /** Consumed by the input layer right before calling `insertText`. */\n consumePendingMarks: () => PendingMarks | undefined;\n\n // ---- Link dialog (Polish iteration) ----\n linkRequest: LinkRequest | null;\n /** Open the link dialog; resolves with the entered href, empty string to remove, or null on cancel. */\n requestLink: (initialHref?: string) => Promise<string | null>;\n /** Called by the LinkDialog itself when the user submits or cancels. */\n resolveLinkRequest: (href: string | null) => void;\n\n // ---- Page settings (Fase 9.6) ----\n /** Reactive page settings snapshot (re-renders when the Y.Doc docSettings changes). */\n pageSettings: PageSettings;\n /** Merge a partial PageSettings into the Y.Doc. Not undoable. */\n setPageSettings: (partial: Partial<PageSettings>) => void;\n /** Page-config dialog request (null when closed). */\n pageConfigRequest: PageConfigRequest | null;\n /** Open the page-config dialog; resolves true on apply, false on cancel. */\n requestPageConfig: () => Promise<boolean>;\n /** Resolver called by `<PageConfigDialog>` on apply/cancel. */\n resolvePageConfigRequest: (applied: boolean) => void;\n\n // ---- Image caption (Fase 9.6+) ----\n imageCaptionRequest: ImageCaptionRequest | null;\n /** Open the image-caption dialog with the current caption + alignment pre-filled. */\n requestImageCaption: (\n initialCaption?: string,\n initialAlign?: CaptionAlign,\n ) => Promise<ImageCaptionResult | null>;\n /** Resolver called by `<ImageCaptionDialog>` on apply/cancel. */\n resolveImageCaptionRequest: (result: ImageCaptionResult | null) => void;\n\n // ---- Fórmula matemática ----\n formulaRequest: FormulaRequest | null;\n requestFormula: (\n initialLatex?: string,\n initialDisplay?: boolean,\n ) => Promise<FormulaResult | null>;\n resolveFormulaRequest: (result: FormulaResult | null) => void;\n /** Renderiza o LaTeX, gera SVG + PNG e insere como embed na seleção. */\n insertFormula: (latex: string, display: boolean) => Promise<void>;\n\n // ---- Tables (Phase 4) ----\n /** True iff the focus position lies inside a table cell. */\n isInTable: () => boolean;\n /** Returns `{blockIndex, row, col}` for the focus position, or null when outside a table. */\n getTableLocation: () => TableLocation | null;\n /** Insere `count` linhas de resposta pautadas depois do bloco focado. */\n insertAnswerLines: (count: number, spacing: AnswerLineSpacing) => void;\n insertTable: (rows: number, cols: number) => void;\n /** Removes the table at `blockIndex`. Defaults to the current focus table. */\n deleteTable: (blockIndex?: number) => void;\n insertRowAbove: () => void;\n insertRowBelow: () => void;\n deleteCurrentRow: () => void;\n insertColumnLeft: () => void;\n insertColumnRight: () => void;\n deleteCurrentColumn: () => void;\n moveToNextCell: () => void;\n moveToPrevCell: () => void;\n mergeRight: () => boolean;\n mergeDown: () => boolean;\n splitCell: () => boolean;\n /** Move a divisa `boundary` (entre a coluna `boundary` e a `boundary+1`) por `deltaPct` pontos percentuais. */\n setColumnBoundary: (blockIndex: number, boundary: number, deltaPct: number, base?: number[]) => void;\n /** Define a largura total da tabela como percentual da largura útil da página. */\n setTableWidth: (blockIndex: number, pct: number) => void;\n /** Grava a altura de UMA linha, em px. É um mínimo, não fixo. */\n setRowHeight: (blockIndex: number, row: number, px: number) => void;\n /** Grava o array de alturas inteiro numa única transação. */\n setRowHeights: (blockIndex: number, heights: number[]) => void;\n /** Returns the table rectangle if the current selection is a multi-cell range, else null. */\n getTableRect: () => TableRect | null;\n /** True when there's a rectangular table-cell selection. */\n hasTableRectSelection: () => boolean;\n /** Merge every cell in the rectangular selection. Returns true on success. */\n mergeSelection: () => boolean;\n\n // ---- Inline images (Fase 5) ----\n insertImage: (embed: ImageEmbed) => void;\n setImageAttrs: (\n blockIndex: number,\n offset: number,\n partial: Partial<ImageEmbed>,\n cellIndex?: number,\n newOffset?: number,\n ) => void;\n /**\n * Move a floating image embed from one anchor to another (re-anchor across\n * blocks/pages) in a single transaction. No-op for inline embeds.\n */\n moveEmbedAnchor: (\n from: EmbedLoc,\n to: EmbedLoc,\n newOffsetX: number,\n newOffsetY: number,\n ) => void;\n /** Read a File (image) and insert it inline at the caret as a data-URL embed. */\n insertImageFromFile: (file: File) => Promise<void>;\n /**\n * Returns the embed currently selected (single 1-char range over an image),\n * or null when no embed is solely selected.\n */\n getSelectedEmbed: () =>\n | { blockIndex: number; offset: number; cellIndex?: number; embed: ImageEmbed }\n | null;\n /**\n * Passthrough of `UseEditorOptions.uploadImage` — `undefined` when not\n * configured. Exposed so callers with the editor instance but not the\n * original options (the `<Editor>` component's paste handler, notably) can\n * upload an image without going through `insertImageFromFile` (which also\n * inserts it — not always what's wanted, e.g. resolving `data:` embeds\n * already inside a pasted slice before `insertSlice`).\n */\n uploadImage?: (file: File) => Promise<string>;\n}\n\nexport function useEditor(opts: UseEditorOptions = {}): UseEditorResult {\n const doc = useMemo(() => opts.document ?? new EditorDocument(), [opts.document]);\n const history = useMemo(() => new EditorHistory(doc), [doc]);\n\n const [snapshot, setSnapshot] = useState<SerializedDocument>(() => doc.toJSON());\n const [selection, setSelectionState] = useState<Selection>(() =>\n collapsedSelection({ blockIndex: 0, offset: 0 }),\n );\n const selectionRef = useRef(selection);\n selectionRef.current = selection;\n\n // pendingMarks lives on a ref so the beforeinput handler reads the latest value\n // without re-binding listeners on every render. The state copy exists so the\n // Toolbar can render a \"pressed\" hint when a mark is armed at a collapsed caret.\n const pendingMarksRef = useRef<PendingMarks>({});\n const [pendingMarks, setPendingMarksState] = useState<PendingMarks>({});\n\n const setPendingMarks = useCallback((next: PendingMarks) => {\n pendingMarksRef.current = next;\n setPendingMarksState(next);\n }, []);\n\n useEffect(() => {\n // Coalesce snapshot recomputation: `afterTransaction` fires per Y.js\n // transaction, which means once per keystroke for normal typing — but on\n // paste / multi-op commands it can fire N times in a microtask burst.\n // `toJSON()` is expensive for docs with embedded images (each call deep-\n // clones every block's delta, including base64 image src). Coalescing\n // into rAF guarantees at most one recompute per animation frame regardless\n // of how many transactions fire underneath. A pending flag avoids\n // scheduling multiple rAFs.\n let scheduled = false;\n let cancelled = false;\n let frame = 0 as number | ReturnType<typeof requestAnimationFrame>;\n const flush = () => {\n scheduled = false;\n if (cancelled) return;\n setSnapshot(doc.toJSON());\n };\n const schedule = () => {\n if (scheduled) return;\n scheduled = true;\n // Use rAF when available (browser), fall back to microtask in jsdom/Node.\n if (typeof requestAnimationFrame === \"function\") {\n frame = requestAnimationFrame(flush);\n } else {\n queueMicrotask(flush);\n }\n };\n doc.ydoc.on(\"afterTransaction\", schedule);\n return () => {\n cancelled = true;\n if (\n scheduled &&\n typeof requestAnimationFrame === \"function\" &&\n typeof cancelAnimationFrame === \"function\"\n ) {\n cancelAnimationFrame(frame as number);\n }\n doc.ydoc.off(\"afterTransaction\", schedule);\n };\n }, [doc]);\n\n const getSelection = useCallback(() => selectionRef.current, []);\n const setSelection = useCallback(\n (sel: Selection) => {\n const prev = selectionRef.current;\n selectionRef.current = sel;\n setSelectionState(sel);\n // Any caret movement that isn't a direct typing follow-up clears the queue.\n if (\n Object.keys(pendingMarksRef.current).length > 0 &&\n (prev.focus.blockIndex !== sel.focus.blockIndex || prev.focus.offset !== sel.focus.offset)\n ) {\n // Heuristic: typing advances the caret by exactly 1; everything else clears.\n const advancedByOne =\n prev.focus.blockIndex === sel.focus.blockIndex &&\n sel.focus.offset === prev.focus.offset + 1;\n if (!advancedByOne) {\n pendingMarksRef.current = {};\n setPendingMarksState({});\n }\n }\n },\n [],\n );\n\n const ctxRef = useRef<CommandContext>({ doc, getSelection, setSelection });\n ctxRef.current = { doc, getSelection, setSelection };\n\n const consumePendingMarks = useCallback((): PendingMarks | undefined => {\n const current = pendingMarksRef.current;\n if (Object.keys(current).length === 0) return undefined;\n pendingMarksRef.current = {};\n setPendingMarksState({});\n return current;\n }, []);\n\n const isMarkActive = useCallback(\n (name: MarkName, value?: MarkAttrs[MarkName]): boolean => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n if (start.blockIndex === end.blockIndex && start.offset === end.offset) {\n // Collapsed: union of pending and ambient (mark of the char immediately before caret).\n const pending = pendingMarksRef.current[name];\n if (pending !== undefined) {\n if (pending === null) return false;\n return value === undefined ? true : sameValue(pending, value);\n }\n const yText = doc.getBlockText(start.blockIndex);\n if (!yText || start.offset === 0) return false;\n return isMarkUniformInRange(yText, start.offset - 1, start.offset, name, value);\n }\n if (start.blockIndex !== end.blockIndex) {\n // Multi-block: active only if uniformly active on every covered slice.\n for (let i = start.blockIndex; i <= end.blockIndex; i++) {\n const yText = doc.getBlockText(i);\n if (!yText) return false;\n const s = i === start.blockIndex ? start.offset : 0;\n const e = i === end.blockIndex ? end.offset : yText.length;\n if (s >= e) continue;\n if (!isMarkUniformInRange(yText, s, e, name, value)) return false;\n }\n return true;\n }\n const yText = doc.getBlockText(start.blockIndex);\n if (!yText) return false;\n return isMarkUniformInRange(yText, start.offset, end.offset, name, value);\n },\n [doc],\n );\n\n const getActiveMarks = useCallback(() => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n if (start.blockIndex !== end.blockIndex) {\n // For Sub-phase 2.1 we conservatively report empty across multi-block selections.\n return {};\n }\n const yText = doc.getBlockText(start.blockIndex);\n if (!yText) return {};\n if (start.offset === end.offset) {\n if (start.offset === 0) return {};\n return getMarksInRange(yText, start.offset - 1, start.offset);\n }\n return getMarksInRange(yText, start.offset, end.offset);\n }, [doc]);\n\n const toggleMark = useCallback(\n (name: MarkName, value?: MarkAttrs[MarkName]) => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;\n if (isCollapsed) {\n // Queue for next insert.\n const next: PendingMarks = { ...pendingMarksRef.current };\n const currentlyActive = isMarkActive(name, value);\n if (currentlyActive) {\n next[name] = null;\n } else {\n next[name] = value ?? (booleanMarks.has(name) ? true : null);\n }\n setPendingMarks(next);\n return;\n }\n cmdToggleMark(ctxRef.current, name, value);\n },\n [isMarkActive, setPendingMarks],\n );\n\n const setMark = useCallback(\n (name: MarkName, value: MarkAttrs[MarkName]) => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;\n if (isCollapsed) {\n setPendingMarks({ ...pendingMarksRef.current, [name]: value });\n return;\n }\n cmdSetMark(ctxRef.current, name, value);\n },\n [setPendingMarks],\n );\n\n const removeMark = useCallback(\n (name: MarkName) => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;\n if (isCollapsed) {\n setPendingMarks({ ...pendingMarksRef.current, [name]: null });\n return;\n }\n cmdRemoveMark(ctxRef.current, name);\n },\n [setPendingMarks],\n );\n\n const getBlockType = useCallback((): BlockType | \"mixed\" => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const first = doc.getBlockType(start.blockIndex);\n if (first === undefined) return \"paragraph\";\n for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {\n if (doc.getBlockType(i) !== first) return \"mixed\";\n }\n return first;\n }, [doc]);\n\n const getBlockAttr = useCallback(\n <K extends keyof BlockAttrs>(key: K): BlockAttrs[K] | \"mixed\" | undefined => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const first = doc.getBlockAttrs(start.blockIndex)[key];\n for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {\n const next = doc.getBlockAttrs(i)[key];\n if (next !== first) return \"mixed\";\n }\n return first;\n },\n [doc],\n );\n\n const setBlockType = useCallback((type: BlockType, attrs?: BlockAttrs) => {\n cmdSetBlockType(ctxRef.current, type, attrs);\n }, []);\n\n const setBlockAttr = useCallback(\n <K extends keyof BlockAttrs>(key: K, value: BlockAttrs[K] | null) => {\n cmdSetBlockAttr(ctxRef.current, key, value);\n },\n [],\n );\n\n const setAlign = useCallback((value: AlignValue) => {\n const sel = selectionRef.current;\n // Route on focus only: setCellAttr keys off sel.focus.cellIndex internally\n // and returns early unless the focus position is inside a table cell.\n const inCell = sel.focus.cellIndex != null;\n if (inCell) cmdSetCellAttr(ctxRef.current, \"align\", value);\n else cmdSetBlockAttr(ctxRef.current, \"align\", value);\n }, []);\n\n const getAlign = useCallback((): AlignValue | \"mixed\" | undefined => {\n const sel = selectionRef.current;\n if (sel.focus.cellIndex != null && doc.isTable(sel.focus.blockIndex)) {\n // Cell path: single focused-cell read (multi-cell mixed-state is out of scope).\n return doc.getCellAttrs(sel.focus.blockIndex, sel.focus.cellIndex).align;\n }\n // Block path: delegate to getBlockAttr so multi-block selections return \"mixed\".\n return getBlockAttr(\"align\");\n }, [doc, getBlockAttr]);\n\n const setCellBackground = useCallback((color: string | null) => {\n // setCellAttr keys off sel.focus.cellIndex internally: applies to the\n // rect selection (or the focused cell) and no-ops outside a table.\n cmdSetCellAttr(ctxRef.current, \"bgColor\", color);\n }, []);\n\n const getCellBackground = useCallback((): string | undefined => {\n const sel = selectionRef.current;\n if (sel.focus.cellIndex == null || !doc.isTable(sel.focus.blockIndex)) return undefined;\n // Single focused-cell read, like getAlign (multi-cell mixed-state out of scope).\n return doc.getCellAttrs(sel.focus.blockIndex, sel.focus.cellIndex).bgColor;\n }, [doc]);\n\n const setTableBorderPreset = useCallback((preset: TableBorderPreset) => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return;\n // setBlockAttr é inerte com o caret dentro de uma célula — que é sempre o\n // caso aqui. Por isso a escrita é por índice de bloco.\n cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, \"borderPreset\", preset);\n }, [doc]);\n\n const getTableBorderPreset = useCallback((): TableBorderPreset => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return \"all\";\n return doc.getBlockAttrs(sel.focus.blockIndex).borderPreset ?? \"all\";\n }, [doc]);\n\n const setTableBorderColor = useCallback((color: string | null) => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return;\n cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, \"borderColor\", color);\n }, [doc]);\n\n const getTableBorderColor = useCallback((): string | undefined => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return undefined;\n return doc.getBlockAttrs(sel.focus.blockIndex).borderColor;\n }, [doc]);\n\n const isListActive = useCallback(\n (kind: ListKind): boolean => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n for (let i = start.blockIndex; i <= end.blockIndex; i++) {\n if (doc.getBlockType(i) !== \"listItem\") return false;\n if (doc.getBlockAttrs(i).listKind !== kind) return false;\n }\n return true;\n },\n [doc],\n );\n\n const toggleList = useCallback((kind: ListKind) => {\n cmdToggleList(ctxRef.current, kind);\n }, []);\n const indentList = useCallback(() => {\n cmdIndentList(ctxRef.current);\n }, []);\n const dedentList = useCallback(() => {\n cmdDedentList(ctxRef.current);\n }, []);\n const splitListItem = useCallback((): boolean => {\n return cmdSplitListItem(ctxRef.current);\n }, []);\n\n // ---- Tables ----\n const isInTable = useCallback((): boolean => {\n return selectionRef.current.focus.cellIndex != null;\n }, []);\n const getTableLocation = useCallback((): TableLocation | null => {\n return tableLocationOf(doc, selectionRef.current.focus);\n }, [doc]);\n const insertTable = useCallback((rows: number, cols: number) => {\n cmdInsertTable(ctxRef.current, rows, cols);\n }, []);\n const insertAnswerLines = useCallback((count: number, spacing: AnswerLineSpacing) => {\n cmdInsertAnswerLines(ctxRef.current, count, spacing);\n }, []);\n const deleteTable = useCallback((blockIndex?: number) => {\n const idx = blockIndex ?? selectionRef.current.focus.blockIndex;\n cmdDeleteTable(ctxRef.current, idx);\n }, []);\n const insertRowAbove = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertRow(ctxRef.current, loc.blockIndex, loc.row, \"before\");\n }, [doc]);\n const insertRowBelow = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertRow(ctxRef.current, loc.blockIndex, loc.row, \"after\");\n }, [doc]);\n const deleteCurrentRow = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdDeleteRow(ctxRef.current, loc.blockIndex, loc.row);\n }, [doc]);\n const insertColumnLeft = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertColumn(ctxRef.current, loc.blockIndex, loc.col, \"before\");\n }, [doc]);\n const insertColumnRight = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertColumn(ctxRef.current, loc.blockIndex, loc.col, \"after\");\n }, [doc]);\n const deleteCurrentColumn = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdDeleteColumn(ctxRef.current, loc.blockIndex, loc.col);\n }, [doc]);\n const moveToNextCell = useCallback(() => {\n cmdMoveNextCell(ctxRef.current);\n }, []);\n const moveToPrevCell = useCallback(() => {\n cmdMovePrevCell(ctxRef.current);\n }, []);\n const mergeRight = useCallback((): boolean => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return false;\n return cmdMergeRight(ctxRef.current, loc.blockIndex, loc.row, loc.col);\n }, [doc]);\n const mergeDown = useCallback((): boolean => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return false;\n return cmdMergeDown(ctxRef.current, loc.blockIndex, loc.row, loc.col);\n }, [doc]);\n const splitCell = useCallback((): boolean => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return false;\n return cmdSplitCell(ctxRef.current, loc.blockIndex, loc.row, loc.col);\n }, [doc]);\n const setColumnBoundary = useCallback(\n (blockIndex: number, boundary: number, deltaPct: number, base?: number[]) => {\n cmdSetColumnBoundary(ctxRef.current, blockIndex, boundary, deltaPct, base);\n },\n [],\n );\n const setTableWidth = useCallback((blockIndex: number, pct: number) => {\n cmdSetTableWidth(ctxRef.current, blockIndex, pct);\n }, []);\n const setRowHeight = useCallback((blockIndex: number, row: number, px: number) => {\n cmdSetRowHeight(ctxRef.current, blockIndex, row, px);\n }, []);\n const setRowHeights = useCallback((blockIndex: number, heights: number[]) => {\n cmdSetRowHeights(ctxRef.current, blockIndex, heights);\n }, []);\n const getTableRect = useCallback(\n () => tableRectSelection(doc, selectionRef.current),\n [doc],\n );\n const hasTableRectSelection = useCallback((): boolean => {\n return tableRectSelection(doc, selectionRef.current) !== null;\n }, [doc]);\n const mergeSelection = useCallback((): boolean => {\n return cmdMergeSelection(ctxRef.current);\n }, []);\n\n // ---- Inline images ----\n const insertImage = useCallback((embed: ImageEmbed) => {\n cmdInsertImage(ctxRef.current, embed);\n }, []);\n const setImageAttrs = useCallback(\n (\n blockIndex: number,\n offset: number,\n partial: Partial<ImageEmbed>,\n cellIndex?: number,\n newOffset?: number,\n ) => {\n cmdSetImageAttrs(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);\n },\n [],\n );\n const moveEmbedAnchor = useCallback(\n (from: EmbedLoc, to: EmbedLoc, newOffsetX: number, newOffsetY: number) => {\n cmdMoveEmbedAnchor(ctxRef.current, from, to, newOffsetX, newOffsetY);\n },\n [],\n );\n const uploadImageRef = useRef(opts.uploadImage);\n uploadImageRef.current = opts.uploadImage;\n const insertImageFromFile = useCallback(async (file: File): Promise<void> => {\n if (!file.type.startsWith(\"image/\")) return;\n // Lê dimensões a partir de um dataUrl temporário em ambos os caminhos\n // (lê o arquivo só uma vez; o uploadImage decide se persiste essa data\n // ou faz upload pra um storage externo).\n const dataUrl = await readAsDataURL(file);\n const dims = await readImageDimensions(dataUrl);\n let w = dims.width;\n let h = dims.height;\n if (w > MAX_INSERT_WIDTH) {\n h = Math.round((h * MAX_INSERT_WIDTH) / w);\n w = MAX_INSERT_WIDTH;\n }\n const upload = uploadImageRef.current;\n const src = upload ? await upload(file) : dataUrl;\n cmdInsertImage(ctxRef.current, { type: \"image\", src, width: w, height: h });\n }, []);\n const getSelectedEmbed = useCallback(() => {\n const sel = selectionRef.current;\n if (sel.anchor.blockIndex !== sel.focus.blockIndex) return null;\n if ((sel.anchor.cellIndex ?? -1) !== (sel.focus.cellIndex ?? -1)) return null;\n const a = sel.anchor.offset;\n const b = sel.focus.offset;\n if (Math.abs(a - b) !== 1) return null;\n const start = Math.min(a, b);\n const yText = doc.textAt(sel.anchor.blockIndex, sel.anchor.cellIndex);\n if (!yText) return null;\n const delta = yText.toDelta() as DeltaOp[];\n let cursor = 0;\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n cursor += op.insert.length;\n continue;\n }\n if (cursor === start && isImageEmbed(op.insert)) {\n return {\n blockIndex: sel.anchor.blockIndex,\n cellIndex: sel.anchor.cellIndex,\n offset: start,\n embed: op.insert,\n };\n }\n cursor += 1;\n }\n return null;\n }, [doc]);\n\n const [linkRequest, setLinkRequest] = useState<LinkRequest | null>(null);\n const linkRequestRef = useRef<LinkRequest | null>(null);\n const requestLink = useCallback((initialHref = \"\"): Promise<string | null> => {\n return new Promise((resolve) => {\n // Capture the model selection NOW (bug #6): the modal round-trip can\n // collapse it (the editor refocuses on close → selectionchange overwrites\n // the model), and the consumer applies the link mark AFTER the modal —\n // a no-op on a collapsed selection. We restore it on resolve.\n const req: LinkRequest = { initialHref, resolve, selection: selectionRef.current };\n linkRequestRef.current = req;\n setLinkRequest(req);\n });\n }, []);\n const resolveLinkRequest = useCallback(\n (href: string | null) => {\n const req = linkRequestRef.current;\n linkRequestRef.current = null;\n setLinkRequest(null);\n if (req) {\n // Restore the captured selection so the consumer's setMark applies to\n // the originally-selected text even if the modal collapsed it.\n setSelection(req.selection);\n req.resolve(href);\n }\n },\n [setSelection],\n );\n\n // ---- Page settings ----\n const pageSettings = usePageSettings(doc);\n const setPageSettings = useCallback(\n (partial: Partial<PageSettings>) => {\n doc.setPageSettings(partial);\n },\n [doc],\n );\n const [pageConfigRequest, setPageConfigRequest] = useState<PageConfigRequest | null>(null);\n const requestPageConfig = useCallback((): Promise<boolean> => {\n return new Promise((resolve) => {\n setPageConfigRequest({ initial: doc.getPageSettings(), resolve });\n });\n }, [doc]);\n const resolvePageConfigRequest = useCallback((applied: boolean) => {\n setPageConfigRequest((prev) => {\n if (prev) prev.resolve(applied);\n return null;\n });\n }, []);\n\n // ---- Image caption ----\n const [imageCaptionRequest, setImageCaptionRequest] =\n useState<ImageCaptionRequest | null>(null);\n const requestImageCaption = useCallback(\n (initialCaption = \"\", initialAlign: CaptionAlign = \"center\"): Promise<ImageCaptionResult | null> => {\n return new Promise((resolve) => {\n setImageCaptionRequest({ initialCaption, initialAlign, resolve });\n });\n },\n [],\n );\n const resolveImageCaptionRequest = useCallback((result: ImageCaptionResult | null) => {\n setImageCaptionRequest((prev) => {\n if (prev) prev.resolve(result);\n return null;\n });\n }, []);\n\n // ---- Fórmula ----\n const [formulaRequest, setFormulaRequest] = useState<FormulaRequest | null>(null);\n const requestFormula = useCallback(\n (initialLatex = \"\", initialDisplay = false): Promise<FormulaResult | null> => {\n return new Promise((resolve) => {\n setFormulaRequest({ initialLatex, initialDisplay, resolve });\n });\n },\n [],\n );\n const resolveFormulaRequest = useCallback((result: FormulaResult | null) => {\n setFormulaRequest((prev) => {\n if (prev) prev.resolve(result);\n return null;\n });\n }, []);\n\n const insertFormula = useCallback(\n async (latex: string, display: boolean): Promise<void> => {\n // Snapshot da seleção ANTES de qualquer `await`. Esta função tem um\n // `await import(...)` logo abaixo (e outro `await`, no PNG de\n // fallback) NO MEIO do próprio corpo — um gap assíncrono real, não só\n // um round-trip de Promise síncrono. Se o `<dialog>` do modal (que já\n // fechou antes de `insertFormula` ser chamada) devolver o foco ao\n // editor e a seleção do modelo colapsar durante esse gap (bug #6 —\n // mesmo motivo de `LinkRequest.selection`, mas aqui SEM jeito de o\n // chamador segurar a seleção através do `await`, porque ele não vê o\n // await por dentro), `cmdInsertImage` (mais abaixo) leria uma seleção\n // colapsada, `insertImage` (`commands.ts`) pularia o `deleteRange`\n // inteiro (`isCollapsed` verdadeiro) e a fórmula antiga sobreviveria\n // ao lado da nova — duas fórmulas no documento. Capturar aqui e\n // restaurar logo antes de `cmdInsertImage` fecha esse gap pra QUALQUER\n // chamador (toolbar, duplo clique, botão \"Editar fórmula\") num lugar\n // só. Não dá pra resolver espelhando `resolveLinkRequest` (restaurar a\n // seleção ali, no `resolve()`): essa restauração aconteceria ANTES do\n // `await` desta função, não depois — o gap continuaria aberto.\n const selectionAtCall = ctxRef.current.getSelection();\n // Mesma linha de raciocínio do comentário acima: `getSelectedEmbed()`\n // também precisa rodar ANTES do primeiro `await`, lendo a seleção AO\n // VIVO no mesmo instante síncrono de `selectionAtCall` — depois do gap\n // ela pode ter colapsado (mesmo bug #6) e `getSelectedEmbed()` não\n // acharia mais nada para herdar. Usado abaixo, perto de\n // `cmdInsertImage`, só para herdar layout/posição de uma edição —\n // nunca para decidir ONDE inserir (isso é `selectionAtCall`).\n const embedAtCall = getSelectedEmbed();\n // Import DINÂMICO, pelo mesmo motivo do modal: manter o mathjax-full\n // fora do bundle principal. Como esta função já é async, não custa nada.\n let mathModule: typeof import(\"@sofereditor/math\");\n try {\n mathModule = await import(\"@sofereditor/math\");\n } catch {\n // Chunk falhou ao carregar (ex.: deploy do portal trocou o hash dos\n // assets enquanto a aba estava aberta). Sair em silêncio é certo\n // aqui: quem chama `insertFormula` só chega depois de já ter visto\n // o preview funcionar no modal (mesmo import, resolvido lá) — o\n // feedback de erro é responsabilidade do `FormulaDialog`, não desta\n // função.\n return;\n }\n const { renderLatexToSvg, measureExInPx, svgToDataUrl, svgToPngDataUrl } = mathModule;\n const r = renderLatexToSvg(latex, display);\n if (!r.ok) return; // o modal já barra isto; aqui é cinto de segurança\n // `useEditor` não tem ref do elemento raiz — conferido. `.ed-root` é o\n // mesmo seletor que TableFloatingToolbar usa para achar a raiz do editor.\n // Medir no <body> daria o font-size do documento HTML, não o do editor.\n const root = document.querySelector<HTMLElement>(\".ed-root\");\n const exPx = root ? measureExInPx(root) : 8;\n let w = Math.round(r.widthEx * exPx);\n let h = Math.round(r.heightEx * exPx);\n // vAlignEx é relativo à ALTURA renderizada (`r.heightEx`, não clampada).\n // Quando o clamp abaixo encolhe w/h, o `ex` da linha de base tem que\n // encolher pelo MESMO fator — senão a fórmula clampada senta fora da\n // linha de base, deslocada proporcionalmente ao quanto foi clampada.\n // Fator 1 (sem clamp) é o caso comum e não altera nada.\n let vAlignScale = 1;\n if (w > MAX_INSERT_WIDTH) {\n vAlignScale = MAX_INSERT_WIDTH / w;\n // Fórmula de `h` inalterada (mesma ordem de operações de antes do\n // fix) — só a variável `vAlignScale` é nova.\n h = Math.round((h * MAX_INSERT_WIDTH) / w);\n w = MAX_INSERT_WIDTH;\n }\n const src = svgToDataUrl(r.svg);\n let svgFallback: string | undefined;\n try {\n svgFallback = await svgToPngDataUrl(r.svg, w, h);\n } catch {\n // Sem PNG a fórmula ainda entra no documento e sai no PDF; só o DOCX\n // vai pular esse embed, contando em `skipped`. Melhor que não inserir.\n svgFallback = undefined;\n }\n // Restaura a seleção capturada ANTES do primeiro `await` lá em cima —\n // ver o comentário no topo da função. Sem isto, `cmdInsertImage` lê a\n // seleção AO VIVO no instante em que roda (`ctx.getSelection()` dentro\n // de `insertImage`, `commands.ts`), que pode ter colapsado durante o\n // gap assíncrono.\n ctxRef.current.setSelection(selectionAtCall);\n // Herda layout/posição do embed que está sendo SUBSTITUÍDO (edição de\n // uma fórmula existente — duplo clique ou \"Editar fórmula\" na\n // toolbar), nunca de uma imagem comum: `embedAtCall` pode ser uma\n // imagem selecionada quando a fórmula está sendo inserida do zero pela\n // toolbar, e herdar o layout dela seria pior que não herdar nada.\n // `align` NÃO entra aqui de propósito — é derivado de `display` logo\n // abaixo (bloco → \"center\"); herdá-lo reintroduziria a centralização\n // depois de o professor desmarcar \"Fórmula em bloco\". `w`/`h` também\n // não são preservados: o valor salvo é medida+clamp do LaTeX ANTIGO, a\n // razão do resize aplicado pelo usuário não é recuperável sem\n // re-renderizar o LaTeX antigo, e o novo tem tamanho natural diferente\n // — re-medir é o comportamento pretendido, não um bug a \"consertar\".\n const inheritedLayout: Pick<ImageEmbed, \"layout\" | \"offsetX\" | \"offsetY\"> = {};\n if (isFormulaEmbed(embedAtCall?.embed)) {\n // Spreads condicionais por campo, não um objeto fixo com chaves\n // `undefined`: o embed vira um insert de Y.Text (`yText.insert`, em\n // `commands.ts`) que viaja pela rede — melhor não gravar chaves\n // `offsetX`/`offsetY` explicitamente `undefined` (ex.: fórmula\n // antiga em `layout: \"inline\"`, que não tem offsets) quando dá pra\n // simplesmente omiti-las.\n const prev = embedAtCall.embed;\n if (prev.layout !== undefined) inheritedLayout.layout = prev.layout;\n if (prev.offsetX !== undefined) inheritedLayout.offsetX = prev.offsetX;\n if (prev.offsetY !== undefined) inheritedLayout.offsetY = prev.offsetY;\n }\n cmdInsertImage(ctxRef.current, {\n type: \"image\",\n src,\n width: w,\n height: h,\n ...inheritedLayout,\n ...(display ? { align: \"center\" as const } : {}),\n formula: {\n latex,\n display,\n ...(display ? {} : { vAlign: `${r.vAlignEx * vAlignScale}ex` }),\n },\n ...(svgFallback ? { svgFallback } : {}),\n });\n },\n [getSelectedEmbed],\n );\n\n return {\n doc,\n history,\n snapshot,\n selection,\n setSelection,\n getSelection,\n isMarkActive,\n getActiveMarks,\n toggleMark,\n setMark,\n removeMark,\n pendingMarks,\n consumePendingMarks,\n getBlockType,\n getBlockAttr,\n setBlockType,\n setBlockAttr,\n setAlign,\n getAlign,\n setCellBackground,\n getCellBackground,\n setTableBorderPreset,\n getTableBorderPreset,\n setTableBorderColor,\n getTableBorderColor,\n isListActive,\n toggleList,\n indentList,\n dedentList,\n splitListItem,\n linkRequest,\n requestLink,\n resolveLinkRequest,\n isInTable,\n getTableLocation,\n insertAnswerLines,\n insertTable,\n deleteTable,\n insertRowAbove,\n insertRowBelow,\n deleteCurrentRow,\n insertColumnLeft,\n insertColumnRight,\n deleteCurrentColumn,\n moveToNextCell,\n moveToPrevCell,\n mergeRight,\n mergeDown,\n splitCell,\n setColumnBoundary,\n setTableWidth,\n setRowHeight,\n setRowHeights,\n getTableRect,\n hasTableRectSelection,\n mergeSelection,\n insertImage,\n setImageAttrs,\n moveEmbedAnchor,\n insertImageFromFile,\n getSelectedEmbed,\n pageSettings,\n setPageSettings,\n pageConfigRequest,\n requestPageConfig,\n resolvePageConfigRequest,\n imageCaptionRequest,\n requestImageCaption,\n resolveImageCaptionRequest,\n formulaRequest,\n requestFormula,\n resolveFormulaRequest,\n insertFormula,\n uploadImage: opts.uploadImage,\n };\n}\n\nfunction readAsDataURL(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error ?? new Error(\"FileReader error\"));\n reader.onload = () => resolve(reader.result as string);\n reader.readAsDataURL(file);\n });\n}\n\nfunction readImageDimensions(src: string): Promise<{ width: number; height: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onerror = () => reject(new Error(\"image load failed\"));\n img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });\n img.src = src;\n });\n}\n\nconst booleanMarks: Set<MarkName> = new Set([\"bold\", \"italic\", \"underline\", \"strike\"]);\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a === \"object\" && a && b && typeof b === \"object\") {\n const ak = Object.keys(a);\n const bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n return ak.every((k) => (a as Record<string, unknown>)[k] === (b as Record<string, unknown>)[k]);\n }\n return false;\n}\n","import { useCallback, useRef, useSyncExternalStore } from \"react\";\nimport type { EditorDocument, PageSettings } from \"@sofereditor/core\";\n\n/**\n * Reactive snapshot of an `EditorDocument`'s page settings. Re-renders the\n * component when `docSettings` changes (either locally via `setPageSettings`\n * or remotely via Y.js sync).\n *\n * Caches the snapshot reference to satisfy `useSyncExternalStore`'s stable-\n * identity requirement — `getPageSettings` returns a fresh object each call,\n * which would loop the store forever without this caching.\n */\nexport function usePageSettings(doc: EditorDocument): PageSettings {\n const cacheRef = useRef<PageSettings | null>(null);\n\n const subscribe = useCallback(\n (notify: () => void) => doc.observePageSettings(notify),\n [doc],\n );\n\n const getSnapshot = useCallback((): PageSettings => {\n const next = doc.getPageSettings();\n const prev = cacheRef.current;\n if (prev && shallowEqual(prev, next)) return prev;\n cacheRef.current = next;\n return next;\n }, [doc]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nfunction shallowEqual(a: PageSettings, b: PageSettings): boolean {\n return (\n a.width === b.width &&\n a.height === b.height &&\n a.marginTop === b.marginTop &&\n a.marginBottom === b.marginBottom &&\n a.marginLeft === b.marginLeft &&\n a.marginRight === b.marginRight &&\n a.preset === b.preset &&\n a.systemHeaderTitle === b.systemHeaderTitle &&\n a.systemHeaderAuthor === b.systemHeaderAuthor\n );\n}\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type JSX,\n type MouseEvent,\n type ReactNode,\n} from \"react\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { ANSWER_LINE_MAX, isFormulaEmbed, TABLE_BORDER_COLOR } from \"@sofereditor/core\";\nimport { TableBorderPanel, BORDER_MENU_GLYPH } from \"./TableBorderPanel\";\nimport type {\n AlignValue,\n AnswerLineSpacing,\n BlockType,\n DirValue,\n ImageLayout,\n MarkName,\n} from \"@sofereditor/core\";\n\n// School standard: Arial only. The dropdown is kept (instead of removed) so\n// that the surrounding font-size / color UI stays balanced and future font\n// additions are a one-liner.\nconst FONT_FAMILIES = [\"Arial\"];\nconst FONT_SIZES = [\"8pt\", \"10pt\", \"11pt\", \"12pt\", \"14pt\", \"16pt\", \"18pt\", \"24pt\", \"32pt\", \"48pt\"];\n\ninterface BlockOption {\n value: string;\n label: string;\n type: BlockType;\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nconst BLOCK_OPTIONS: BlockOption[] = [\n { value: \"paragraph\", label: \"Parágrafo\", type: \"paragraph\" },\n { value: \"h1\", label: \"Título 1\", type: \"heading\", level: 1 },\n { value: \"h2\", label: \"Título 2\", type: \"heading\", level: 2 },\n { value: \"h3\", label: \"Título 3\", type: \"heading\", level: 3 },\n { value: \"h4\", label: \"Título 4\", type: \"heading\", level: 4 },\n { value: \"h5\", label: \"Título 5\", type: \"heading\", level: 5 },\n { value: \"h6\", label: \"Título 6\", type: \"heading\", level: 6 },\n { value: \"blockquote\", label: \"Citação\", type: \"blockquote\" },\n { value: \"codeBlock\", label: \"Código\", type: \"codeBlock\" },\n];\n\nconst ALIGN_OPTIONS: { value: AlignValue; label: string; title: string }[] = [\n { value: \"left\", label: \"⇤\", title: \"Alinhar à esquerda\" },\n { value: \"center\", label: \"≡\", title: \"Centralizar\" },\n { value: \"right\", label: \"⇥\", title: \"Alinhar à direita\" },\n { value: \"justify\", label: \"☰\", title: \"Justificar\" },\n];\n\nconst IMAGE_ALIGN_VALUES = new Set<AlignValue>([\"left\", \"center\", \"right\"]);\n\n/**\n * Controles nativos que PRECISAM receber o `mousedown` para abrir seu\n * dropdown/picker. Prevenir o mousedown num `<select>` impede o dropdown de\n * abrir — foi exatamente assim que o seletor de bordas da tabela nasceu morto.\n *\n * A regra vivia duplicada em três handlers com três listas diferentes; fica\n * aqui numa fonte só para não voltar a divergir.\n */\nconst NATIVE_CONTROL_TAGS = new Set([\"SELECT\", \"INPUT\", \"OPTION\"]);\n\nexport function isNativeControl(t: EventTarget | null): boolean {\n const el = t as HTMLElement | null;\n return !!el && NATIVE_CONTROL_TAGS.has(el.tagName);\n}\n\n/**\n * Alvos dentro de um popover que mantêm o próprio `mousedown`. Superconjunto de\n * `isNativeControl` mais `BUTTON` — o resto é fundo de popover, onde o\n * `preventDefault` protege a seleção do editor.\n */\nexport function keepsOwnMouseDown(t: EventTarget | null): boolean {\n const el = t as HTMLElement | null;\n return !!el && (NATIVE_CONTROL_TAGS.has(el.tagName) || el.tagName === \"BUTTON\");\n}\n\nconst IMAGE_LAYOUT_OPTIONS: { value: ImageLayout; label: string; title: string }[] = [\n { value: \"inline\", label: \"↔\", title: \"Inline (no fluxo do texto)\" },\n { value: \"wrap-left\", label: \"◧\", title: \"Texto contorna à direita\" },\n { value: \"wrap-right\", label: \"◨\", title: \"Texto contorna à esquerda\" },\n { value: \"behind\", label: \"▢↓\", title: \"Atrás do texto\" },\n { value: \"front\", label: \"▢↑\", title: \"Na frente do texto\" },\n];\n\nexport interface ToolbarProps {\n className?: string;\n}\n\nexport function Toolbar({ className }: ToolbarProps): JSX.Element {\n const editor = useEditorContext();\n const {\n isMarkActive,\n getActiveMarks,\n toggleMark,\n setMark,\n removeMark,\n getBlockType,\n getBlockAttr,\n setBlockType,\n setBlockAttr,\n setAlign,\n getAlign,\n isListActive,\n toggleList,\n indentList,\n dedentList,\n } = editor;\n const active = getActiveMarks();\n const blockType = getBlockType();\n const blockLevel = getBlockAttr(\"level\");\n const blockAlign = getAlign();\n const blockDir = getBlockAttr(\"dir\");\n\n // Preserve contenteditable focus when clicking the toolbar background or buttons,\n // BUT let native form controls (select/input) receive mousedown normally so their\n // dropdowns/pickers can open.\n const stop = (e: MouseEvent) => {\n if (isNativeControl(e.target)) return;\n e.preventDefault();\n };\n\n const onToggle = (name: MarkName) => (e: MouseEvent) => {\n e.preventDefault();\n toggleMark(name);\n };\n\n const onColorChange = (e: ChangeEvent<HTMLInputElement>) => {\n setMark(\"color\", e.target.value);\n };\n\n const onHighlightChange = (e: ChangeEvent<HTMLInputElement>) => {\n setMark(\"highlight\", e.target.value);\n };\n\n const onFontFamilyChange = (e: ChangeEvent<HTMLSelectElement>) => {\n const v = e.target.value;\n if (v === \"\") removeMark(\"fontFamily\");\n else setMark(\"fontFamily\", v);\n };\n\n const onFontSizeChange = (e: ChangeEvent<HTMLSelectElement>) => {\n const v = e.target.value;\n if (v === \"\") removeMark(\"fontSize\");\n else setMark(\"fontSize\", v);\n };\n\n const onBlockChange = (e: ChangeEvent<HTMLSelectElement>) => {\n const opt = BLOCK_OPTIONS.find((o) => o.value === e.target.value);\n if (!opt) return;\n setBlockType(opt.type, opt.level !== undefined ? { level: opt.level } : undefined);\n };\n\n const onAlign = (value: AlignValue) => (e: MouseEvent) => {\n e.preventDefault();\n // When an image embed is selected, the three image-friendly alignments\n // (left/center/right) target the embed itself; otherwise fall through to\n // the block-level align attribute (which also handles \"justify\").\n const selectedEmbed = editor.getSelectedEmbed();\n if (selectedEmbed && IMAGE_ALIGN_VALUES.has(value)) {\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n { align: value as \"left\" | \"center\" | \"right\" },\n selectedEmbed.cellIndex,\n );\n return;\n }\n setAlign(value);\n };\n\n const onInsertImageClick = useCallback(\n (e: MouseEvent) => {\n e.preventDefault();\n imageInputRef.current?.click();\n },\n [],\n );\n const onImageInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) void editor.insertImageFromFile(file);\n e.target.value = \"\";\n },\n [editor],\n );\n const imageInputRef = useRef<HTMLInputElement>(null);\n\n const onToggleDir = (e: MouseEvent) => {\n e.preventDefault();\n const next: DirValue = blockDir === \"rtl\" ? \"ltr\" : \"rtl\";\n setBlockAttr(\"dir\", next);\n };\n\n const linkActive = isMarkActive(\"link\");\n const onLinkClick = useCallback(\n (e: MouseEvent) => {\n e.preventDefault();\n const currentHref =\n active.link && typeof active.link === \"object\" && \"href\" in active.link\n ? (active.link.href as string)\n : \"\";\n void editor.requestLink(currentHref).then((href) => {\n if (href === null) return;\n if (href === \"\") removeMark(\"link\");\n else setMark(\"link\", { href });\n });\n },\n [active.link, editor, removeMark, setMark],\n );\n\n const colorValue = typeof active.color === \"string\" ? active.color : \"#000000\";\n // Amarelo clássico de marca-texto como padrão do picker quando nada está marcado.\n const highlightValue = typeof active.highlight === \"string\" ? active.highlight : \"#fff176\";\n const fontFamilyValue = typeof active.fontFamily === \"string\" ? active.fontFamily : \"\";\n const fontSizeValue = typeof active.fontSize === \"string\" ? active.fontSize : \"\";\n const blockValue = blockSelectValue(blockType, blockLevel);\n\n // The image layout group is only visible when an image embed is selected.\n // We read it fresh every render so the visibility tracks selection changes.\n const selectedEmbed = editor.getSelectedEmbed();\n const currentImageLayout: ImageLayout = selectedEmbed\n ? (selectedEmbed.embed.layout ?? \"inline\")\n : \"inline\";\n\n const onImageLayoutClick = (layout: ImageLayout) => (e: MouseEvent) => {\n e.preventDefault();\n const sel = editor.getSelectedEmbed();\n if (!sel) return;\n // Reset position offsets when switching modes; the previous values would\n // refer to a different coordinate system (margins vs. left/top).\n const reset =\n layout !== (sel.embed.layout ?? \"inline\")\n ? { offsetX: 0, offsetY: 0 }\n : {};\n editor.setImageAttrs(\n sel.blockIndex,\n sel.offset,\n { layout, ...reset },\n sel.cellIndex,\n );\n };\n\n return (\n <div\n className={className ?? \"ed-toolbar\"}\n role=\"toolbar\"\n aria-label=\"Editor toolbar\"\n onMouseDown={stop}\n >\n <Group>\n <select\n className=\"ed-toolbar-select\"\n value={blockValue}\n onChange={onBlockChange}\n onMouseDown={stop}\n aria-label=\"Tipo de bloco\"\n >\n {blockValue === \"\" && <option value=\"\">— (misto)</option>}\n {BLOCK_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>{o.label}</option>\n ))}\n </select>\n </Group>\n\n <Group>\n <MarkButton title=\"Negrito (⌘B)\" pressed={isMarkActive(\"bold\")} onClick={onToggle(\"bold\")}>\n <strong>B</strong>\n </MarkButton>\n <MarkButton title=\"Itálico (⌘I)\" pressed={isMarkActive(\"italic\")} onClick={onToggle(\"italic\")}>\n <em>I</em>\n </MarkButton>\n <MarkButton title=\"Sublinhado (⌘U)\" pressed={isMarkActive(\"underline\")} onClick={onToggle(\"underline\")}>\n <u>U</u>\n </MarkButton>\n <MarkButton title=\"Tachado (⌘⇧X)\" pressed={isMarkActive(\"strike\")} onClick={onToggle(\"strike\")}>\n <s>S</s>\n </MarkButton>\n </Group>\n\n <Group>\n <label className=\"ed-toolbar-label\">\n <span className=\"ed-toolbar-swatch\" aria-hidden style={{ background: colorValue }} />\n <input type=\"color\" value={colorValue} onChange={onColorChange} onMouseDown={stop} aria-label=\"Cor do texto\" />\n </label>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Remover cor\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n removeMark(\"color\");\n }}\n >\n ⌫\n </button>\n <label className=\"ed-toolbar-label\" title=\"Marca-texto\">\n <span\n className=\"ed-toolbar-swatch ed-toolbar-swatch--highlight\"\n aria-hidden\n style={{ background: highlightValue }}\n />\n <input\n type=\"color\"\n value={highlightValue}\n onChange={onHighlightChange}\n onMouseDown={stop}\n aria-label=\"Marca-texto\"\n />\n </label>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Remover marca-texto\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n removeMark(\"highlight\");\n }}\n >\n ⌫\n </button>\n </Group>\n\n <Group>\n <select\n className=\"ed-toolbar-select\"\n value={fontFamilyValue}\n onChange={onFontFamilyChange}\n onMouseDown={stop}\n aria-label=\"Família da fonte\"\n >\n <option value=\"\">Fonte…</option>\n {FONT_FAMILIES.map((f) => (\n <option key={f} value={f}>{f}</option>\n ))}\n </select>\n <select\n className=\"ed-toolbar-select\"\n value={fontSizeValue}\n onChange={onFontSizeChange}\n onMouseDown={stop}\n aria-label=\"Tamanho da fonte\"\n >\n <option value=\"\">Tamanho…</option>\n {FONT_SIZES.map((s) => (\n <option key={s} value={s}>{s}</option>\n ))}\n </select>\n </Group>\n\n <Group>\n {ALIGN_OPTIONS.map((a) => (\n <button\n key={a.value}\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title={a.title}\n aria-pressed={blockAlign === a.value}\n onMouseDown={stop}\n onClick={onAlign(a.value)}\n >\n {a.label}\n </button>\n ))}\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={blockDir === \"rtl\"}\n title={blockDir === \"rtl\" ? \"Mudar para LTR\" : \"Mudar para RTL (hebraico)\"}\n onMouseDown={stop}\n onClick={onToggleDir}\n >\n {blockDir === \"rtl\" ? \"RTL\" : \"LTR\"}\n </button>\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={isListActive(\"bullet\")}\n title=\"Lista com marcadores (⌘⇧8)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n toggleList(\"bullet\");\n }}\n >\n •\n </button>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={isListActive(\"ordered\")}\n title=\"Lista numerada (⌘⇧7)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n toggleList(\"ordered\");\n }}\n >\n 1.\n </button>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Diminuir recuo (⇧Tab)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n dedentList();\n }}\n >\n ⇤\n </button>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Aumentar recuo (Tab)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n indentList();\n }}\n >\n ⇥\n </button>\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={linkActive}\n title={linkActive ? \"Remover link (⌘K)\" : \"Adicionar link (⌘K)\"}\n onMouseDown={stop}\n onClick={onLinkClick}\n >\n 🔗\n </button>\n </Group>\n\n <Group>\n <TableMenu />\n <AnswerLinesMenu />\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Inserir imagem\"\n onMouseDown={stop}\n onClick={onInsertImageClick}\n >\n 🖼\n </button>\n <input\n ref={imageInputRef}\n type=\"file\"\n accept=\"image/*\"\n hidden\n onChange={onImageInputChange}\n />\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Inserir fórmula\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n // `insertFormula` retornada (não disparada solta) para cair na\n // MESMA cadeia: um só `.catch` cobre tanto `requestFormula`\n // quanto `insertFormula`. Nenhuma das duas deveria rejeitar hoje\n // (`insertFormula` já protege o import dinâmico com try/catch),\n // mas sem este `.catch` uma rejeição inesperada vira unhandled\n // rejection — ruído em qualquer monitoramento de erro.\n void editor\n .requestFormula()\n .then((r) => (r ? editor.insertFormula(r.latex, r.display) : undefined))\n .catch((err) => {\n console.error(\"Falha ao inserir fórmula:\", err);\n });\n }}\n >\n √x\n </button>\n </Group>\n\n {selectedEmbed && (\n <Group>\n {IMAGE_LAYOUT_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title={opt.title}\n aria-pressed={currentImageLayout === opt.value}\n onMouseDown={stop}\n onClick={onImageLayoutClick(opt.value)}\n >\n {opt.label}\n </button>\n ))}\n {isFormulaEmbed(selectedEmbed.embed) ? (\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Editar fórmula\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n // NÃO é a mesma checagem \"morta\" de antes: o narrowing de\n // `isFormulaEmbed` no ternário (linha acima) não atravessa a\n // fronteira deste closure — `onClick` é uma função separada,\n // e dentro dela `selectedEmbed.embed.formula` volta a ser\n // `FormulaMeta | undefined` pro TS (`tsc --noEmit` reprova\n // sem este re-check; testado). Revalidar aqui é barato e\n // idempotente — nada muda entre o render e o clique.\n if (!isFormulaEmbed(selectedEmbed.embed)) return;\n const f = selectedEmbed.embed.formula;\n // A restauração da seleção do embed através do round-trip do\n // modal (bug #6) mora dentro de `insertFormula`\n // (`useEditor.ts`) — é a única com o `await` no meio, então é\n // a única que consegue segurar a seleção através do gap.\n void editor.requestFormula(f.latex, f.display).then((r) => {\n if (!r) return;\n void editor.insertFormula(r.latex, r.display);\n });\n }}\n >\n Editar fórmula\n </button>\n ) : (\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Legenda da imagem\"\n aria-pressed={!!selectedEmbed.embed.caption}\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n const initialCaption = selectedEmbed.embed.caption ?? \"\";\n const initialAlign = selectedEmbed.embed.captionAlign ?? \"center\";\n void editor.requestImageCaption(initialCaption, initialAlign).then((result) => {\n if (result === null) return; // cancelled\n const empty = result.caption.length === 0;\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n {\n caption: empty ? undefined : result.caption,\n captionAlign: empty ? undefined : result.align,\n },\n selectedEmbed.cellIndex,\n );\n });\n }}\n >\n Legenda\n </button>\n )}\n </Group>\n )}\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Configurar página (tamanho, margens)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n void editor.requestPageConfig();\n }}\n >\n ⚙\n </button>\n </Group>\n </div>\n );\n}\n\n// ---------- Table menu ----------\n\n/**\n * Linhas de resposta: parágrafos pautados para o aluno escrever.\n *\n * Um botão em vez da digitação de underlines porque a régua precisa acompanhar\n * a margem — uma fileira de `_` quebra ao mudar tamanho de página ou fonte.\n */\nfunction AnswerLinesMenu(): JSX.Element {\n const editor = useEditorContext();\n const [open, setOpen] = useState(false);\n const [count, setCount] = useState(5);\n const [spacing, setSpacing] = useState<AnswerLineSpacing>(1.5);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onDown = (e: globalThis.MouseEvent) => {\n if (!rootRef.current) return;\n if (rootRef.current.contains(e.target as Node)) return;\n setOpen(false);\n };\n document.addEventListener(\"mousedown\", onDown);\n return () => document.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n const clamped = Math.max(1, Math.min(ANSWER_LINE_MAX, Math.trunc(count) || 1));\n\n return (\n <div ref={rootRef} className=\"ed-toolbar-tablemenu\">\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Linhas de resposta\"\n aria-pressed={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.preventDefault();\n setOpen((v) => !v);\n }}\n >\n ☰\n </button>\n {open && (\n <div\n className=\"ed-table-popover ed-answerlines-popover\"\n role=\"menu\"\n onMouseDown={(e) => {\n if (!keepsOwnMouseDown(e.target)) e.preventDefault();\n }}\n >\n <label className=\"ed-toolbar-label\">\n Quantidade\n <input\n type=\"number\"\n min={1}\n max={ANSWER_LINE_MAX}\n value={count}\n onChange={(e) => setCount(Number(e.target.value))}\n aria-label=\"Quantidade de linhas\"\n />\n </label>\n <label className=\"ed-toolbar-label\">\n Entrelinha\n <select\n value={String(spacing)}\n onChange={(e) => setSpacing(Number(e.target.value) as AnswerLineSpacing)}\n aria-label=\"Entrelinha\"\n >\n <option value=\"1\">Simples</option>\n <option value=\"1.5\">1,5</option>\n <option value=\"2\">Duplo</option>\n </select>\n </label>\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false);\n editor.insertAnswerLines(clamped, spacing);\n }}\n >\n Inserir {clamped} linha{clamped > 1 ? \"s\" : \"\"}\n </button>\n </div>\n )}\n </div>\n );\n}\n\nfunction TableMenu(): JSX.Element {\n const editor = useEditorContext();\n const inTable = editor.isInTable();\n const [open, setOpen] = useState(false);\n const [bordersOpen, setBordersOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onDown = (e: globalThis.MouseEvent) => {\n if (!rootRef.current) return;\n if (rootRef.current.contains(e.target as Node)) return;\n setOpen(false);\n };\n document.addEventListener(\"mousedown\", onDown);\n return () => document.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n // Fechar o menu ▦ recolhe o painel: reabrir sempre começa do estado neutro.\n useEffect(() => {\n if (!open) setBordersOpen(false);\n }, [open]);\n\n return (\n <div ref={rootRef} className=\"ed-toolbar-tablemenu\">\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Tabela\"\n aria-pressed={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.preventDefault();\n setOpen((v) => !v);\n }}\n >\n ▦\n </button>\n {open && (\n <div\n className=\"ed-table-popover\"\n role=\"menu\"\n onMouseDown={(e) => {\n // Do not let mousedown clear the editor selection.\n if (!keepsOwnMouseDown(e.target)) e.preventDefault();\n }}\n >\n <TableSizePicker\n onPick={(rows, cols) => {\n setOpen(false);\n editor.insertTable(rows, cols);\n }}\n />\n {inTable && (\n <div className=\"ed-table-actions\">\n <hr />\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertRowAbove(); }}>\n Inserir linha acima\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertRowBelow(); }}>\n Inserir linha abaixo\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertColumnLeft(); }}>\n Inserir coluna à esquerda\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertColumnRight(); }}>\n Inserir coluna à direita\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.deleteCurrentRow(); }}>\n Excluir linha\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.deleteCurrentColumn(); }}>\n Excluir coluna\n </button>\n <hr />\n {editor.hasTableRectSelection() && (\n <button\n type=\"button\"\n onClick={() => { setOpen(false); editor.mergeSelection(); }}\n >\n Mesclar células selecionadas\n </button>\n )}\n <button type=\"button\" onClick={() => { setOpen(false); editor.mergeRight(); }}>\n Mesclar com célula à direita\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.mergeDown(); }}>\n Mesclar com célula abaixo\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.splitCell(); }}>\n Dividir células mescladas\n </button>\n <hr />\n <label className=\"ed-toolbar-label ed-table-bgcolor\">\n <span\n className=\"ed-toolbar-swatch\"\n aria-hidden\n style={{ background: editor.getCellBackground() ?? \"#ffffff\" }}\n />\n Cor de fundo da célula\n <input\n type=\"color\"\n value={editor.getCellBackground() ?? \"#ffffff\"}\n onChange={(e) => editor.setCellBackground(e.target.value)}\n aria-label=\"Cor de fundo da célula\"\n />\n </label>\n <button type=\"button\" onClick={() => { setOpen(false); editor.setCellBackground(null); }}>\n Remover cor de fundo\n </button>\n <hr />\n <button\n type=\"button\"\n className=\"ed-border-toggle\"\n aria-pressed={bordersOpen}\n aria-expanded={bordersOpen}\n title=\"Bordas da tabela\"\n aria-label=\"Bordas da tabela\"\n onClick={(e) => {\n e.preventDefault();\n setBordersOpen((v) => !v);\n }}\n >\n {BORDER_MENU_GLYPH}\n </button>\n {bordersOpen && (\n <TableBorderPanel\n preset={editor.getTableBorderPreset()}\n color={editor.getTableBorderColor() ?? TABLE_BORDER_COLOR}\n onPreset={(p) => editor.setTableBorderPreset(p)}\n onColor={(c) => editor.setTableBorderColor(c)}\n onResetColor={() => editor.setTableBorderColor(null)}\n />\n )}\n <hr />\n <button\n type=\"button\"\n className=\"ed-table-delete\"\n onClick={() => { setOpen(false); editor.deleteTable(); }}\n >\n Excluir tabela\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nconst PICKER_MAX_ROWS = 8;\nconst PICKER_MAX_COLS = 10;\n\ninterface TableSizePickerProps {\n onPick: (rows: number, cols: number) => void;\n}\n\nfunction TableSizePicker({ onPick }: TableSizePickerProps): JSX.Element {\n const [hover, setHover] = useState<{ r: number; c: number } | null>(null);\n const label = hover ? `${hover.r + 1} × ${hover.c + 1}` : \"Inserir tabela\";\n return (\n <div className=\"ed-table-picker\">\n <div className=\"ed-table-picker-label\">{label}</div>\n <div\n className=\"ed-table-picker-grid\"\n style={{\n gridTemplateColumns: `repeat(${PICKER_MAX_COLS}, 18px)`,\n gridTemplateRows: `repeat(${PICKER_MAX_ROWS}, 18px)`,\n }}\n onMouseLeave={() => setHover(null)}\n >\n {Array.from({ length: PICKER_MAX_ROWS }, (_, r) =>\n Array.from({ length: PICKER_MAX_COLS }, (_, c) => {\n const filled = hover != null && r <= hover.r && c <= hover.c;\n return (\n <button\n key={`${r}-${c}`}\n type=\"button\"\n className={`ed-table-picker-cell${filled ? \" filled\" : \"\"}`}\n onMouseEnter={() => setHover({ r, c })}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.preventDefault();\n onPick(r + 1, c + 1);\n }}\n aria-label={`${r + 1} × ${c + 1}`}\n />\n );\n }),\n )}\n </div>\n </div>\n );\n}\n\nfunction blockSelectValue(\n type: BlockType | \"mixed\",\n level: unknown,\n): string {\n if (type === \"mixed\") return \"\";\n if (type === \"heading\") {\n if (typeof level === \"number\" && level >= 1 && level <= 6) return `h${level}`;\n return \"h1\";\n }\n return type;\n}\n\nfunction Group({ children }: { children: ReactNode }): JSX.Element {\n return <div className=\"ed-toolbar-group\">{children}</div>;\n}\n\ninterface MarkButtonProps {\n title: string;\n pressed: boolean;\n onClick: (e: MouseEvent) => void;\n children: ReactNode;\n}\n\nfunction MarkButton({ title, pressed, onClick, children }: MarkButtonProps): JSX.Element {\n return (\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={pressed}\n title={title}\n onMouseDown={(e) => e.preventDefault()}\n onClick={onClick}\n >\n {children}\n </button>\n );\n}\n","import type { JSX, MouseEvent } from \"react\";\nimport type { TableBorderPreset } from \"@sofereditor/core\";\n\n/**\n * Vocabulário do `w:tblBorders` do Word. Glifos de texto em vez de biblioteca\n * de ícones para manter o idioma do `Toolbar.tsx` (⇤ ≡ ⇥ ☰ • 🔗 ▦ 🖼 ⚙) e não\n * pendurar uma dependência de ícones num pacote publicado.\n *\n * `⊞` (U+229E) é o quadrado dividido em quatro — o ícone de bordas do Word e do\n * Docs. NÃO trocar por `▦`: é grade fina, e já é o glifo do botão de tabela.\n */\nexport const BORDER_PRESETS: readonly {\n key: TableBorderPreset;\n label: string;\n glyph: string;\n}[] = [\n { key: \"all\", label: \"Todas\", glyph: \"⊞\" },\n { key: \"outer\", label: \"Só externas\", glyph: \"□\" },\n { key: \"horizontal\", label: \"Só horizontais\", glyph: \"▤\" },\n { key: \"vertical\", label: \"Só verticais\", glyph: \"▥\" },\n { key: \"none\", label: \"Nenhuma\", glyph: \"⬚\" },\n];\n\n/** Glifo do gatilho. Mesmo desenho do preset \"all\", como no Word. */\nexport const BORDER_MENU_GLYPH = \"⊞\";\n\nexport interface TableBorderPanelProps {\n preset: TableBorderPreset;\n /** Cor atual da linha, já resolvida (nunca `null`). */\n color: string;\n onPreset: (p: TableBorderPreset) => void;\n onColor: (c: string) => void;\n onResetColor: () => void;\n}\n\n/**\n * Conteúdo do painel de bordas: os cinco presets e a cor da linha.\n *\n * Puro de propósito — sem contexto do editor e sem estado. Quem hospeda decide\n * onde ele aparece e de onde vêm os valores; assim dá para testá-lo por render\n * estática, que é a única forma de teste disponível neste pacote.\n */\nexport function TableBorderPanel({\n preset,\n color,\n onPreset,\n onColor,\n onResetColor,\n}: TableBorderPanelProps): JSX.Element {\n return (\n <div className=\"ed-border-panel\">\n <div className=\"ed-border-presets\">\n {BORDER_PRESETS.map((p) => (\n <button\n key={p.key}\n type=\"button\"\n className=\"ed-border-preset\"\n aria-pressed={preset === p.key}\n title={p.label}\n onClick={(e: MouseEvent) => {\n e.preventDefault();\n onPreset(p.key);\n }}\n >\n <span className=\"ed-border-glyph\" aria-hidden>\n {p.glyph}\n </span>\n {p.label}\n </button>\n ))}\n </div>\n <hr />\n <label className=\"ed-toolbar-label ed-border-color\">\n <span className=\"ed-toolbar-swatch\" aria-hidden style={{ background: color }} />\n Cor da linha\n <input\n type=\"color\"\n value={color}\n onChange={(e) => onColor(e.target.value)}\n aria-label=\"Cor da linha da borda\"\n />\n </label>\n <button\n type=\"button\"\n className=\"ed-border-reset\"\n onClick={(e: MouseEvent) => {\n e.preventDefault();\n onResetColor();\n }}\n >\n Padrão\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,iBAWO;AACP,IAAAC,gBAkBO;;;ACZP,SAAS,2BAA2B,MAAY,MAAuC;AACrF,MAAI,MAAmB;AACvB,SAAO,OAAO,QAAQ,MAAM;AAC1B,QAAI,IAAI,aAAa,KAAK,cAAc;AACtC,YAAM,KAAK;AACX,UAAI,GAAG,QAAQ,cAAc,KAAM,QAAO;AAAA,IAC5C;AACA,UAAM,IAAI;AAAA,EACZ;AACA,SAAO;AACT;AAGA,SAAS,0BACP,MACA,SACoB;AACpB,MAAI,MAAmB;AACvB,SAAO,OAAO,QAAQ,SAAS;AAC7B,QAAI,IAAI,aAAa,KAAK,cAAc;AACtC,YAAM,KAAK;AACX,UAAI,GAAG,QAAQ,aAAa,KAAM,QAAO;AAAA,IAC3C;AACA,UAAM,IAAI;AAAA,EACZ;AACA,SAAO;AACT;AAEO,SAAS,eAAe,MAAY,MAAkC;AAC3E,QAAM,KAAK,2BAA2B,MAAM,IAAI;AAChD,MAAI,CAAC,GAAI,QAAO;AAChB,SAAO,OAAO,SAAS,GAAG,QAAQ,YAAa,EAAE;AACnD;AAOO,SAAS,gBAAgB,MAAmB,YAAwC;AACzF,SAAO,KAAK,cAA2B,sBAAsB,UAAU,IAAI;AAC7E;AAEA,SAAS,kBAAkB,IAAyB;AAClD,QAAM,IAAI,GAAG,QAAQ;AACrB,MAAI,KAAK,KAAM,QAAO;AACtB,QAAM,IAAI,OAAO,SAAS,GAAG,EAAE;AAC/B,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAEA,SAAS,gBAAgB,IAAqC;AAC5D,QAAM,IAAI,GAAG,QAAQ;AACrB,MAAI,KAAK,KAAM,QAAO;AACtB,QAAM,IAAI,OAAO,SAAS,GAAG,EAAE;AAC/B,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAQO,SAAS,qBACd,MACA,YACA,QACoB;AACpB,QAAM,aAAa,KAAK,iBAA8B,sBAAsB,UAAU,IAAI;AAC1F,MAAI,WAAW,WAAW,EAAG,QAAO;AACpC,MAAI,WAAW,WAAW,EAAG,QAAO,WAAW,CAAC;AAChD,MAAI,OAA2B;AAC/B,aAAW,MAAM,YAAY;AAC3B,WAAO;AACP,UAAM,QAAQ,kBAAkB,EAAE;AAClC,UAAM,MAAM,gBAAgB,EAAE;AAC9B,QAAI,OAAO,KAAM,QAAO;AACxB,QAAI,UAAU,SAAS,SAAS,IAAK,QAAO;AAAA,EAC9C;AACA,SAAO;AACT;AAMO,SAAS,eACd,MACA,YACA,WACoB;AACpB,QAAM,UAAU,gBAAgB,MAAM,UAAU;AAChD,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,cAA2B,qBAAqB,SAAS,IAAI;AAC9E;AAQA,SAAS,WAAW,GAAkB;AACpC,SACE,EAAE,aAAa,KAAK,gBACnB,EAAkB,QAAQ,UAAU;AAEzC;AAQA,SAAS,gBAAgB,GAAkB;AACzC,SACE,EAAE,aAAa,KAAK,gBACnB,EAAkB,QAAQ,eAAe;AAE9C;AAOA,SAAS,eAAe,GAAkB;AACxC,SACE,EAAE,aAAa,KAAK,gBACnB,EAAkB,QAAQ,iBAAiB;AAEhD;AAGA,SAAS,cAAc,GAAiB;AACtC,QAAM,SAAS,EAAE;AACjB,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,IAAI;AACR,aAAW,KAAK,MAAM,KAAK,OAAO,UAAU,GAAG;AAC7C,QAAI,MAAM,EAAG,QAAO;AACpB;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,kBAAkB,IAAiB,WAAiC;AAC3E,MAAI,OAAO,UAAW,QAAO;AAC7B,MAAI,GAAG,QAAQ,cAAc,KAAM,QAAO;AAC1C,MAAI,UAAU,QAAQ,aAAa,QAAQ,GAAG,QAAQ,aAAa,KAAM,QAAO;AAChF,SAAO;AACT;AAOO,SAAS,iBACd,WACA,MACA,cACQ;AAER,MAAI,SAAe;AACnB,MAAI,YAAY;AAChB,MAAI,WAAW,MAAM,KAAK,OAAO,YAAY;AAC3C,gBAAY,cAAc,MAAM;AAChC,aAAS,OAAO;AAAA,EAClB;AAEA,MAAI,SAAS;AACb,MAAI,OAAO;AAEX,WAAS,MAAM,GAAe;AAC5B,QAAI,KAAM;AACV,QAAI,MAAM,QAAQ;AAChB,UAAI,EAAE,aAAa,KAAK,WAAW;AACjC,kBAAU;AACV,eAAO;AACP;AAAA,MACF;AAEA,YAAM,WAAW,EAAE;AACnB,eAAS,IAAI,GAAG,IAAI,aAAa,IAAI,SAAS,QAAQ,KAAK;AACzD,cAAM,SAAS,CAAC,CAAC;AACjB,YAAI,KAAM;AAAA,MACZ;AACA,aAAO;AACP;AAAA,IACF;AACA,QAAI,EAAE,aAAa,KAAK,WAAW;AACjC,iBAAW,EAAE,eAAe,IAAI;AAChC;AAAA,IACF;AACA,QAAI,EAAE,aAAa,KAAK,aAAc;AACtC,UAAM,KAAK;AACX,QAAI,kBAAkB,IAAI,SAAS,EAAG;AAGtC,QAAI,WAAW,EAAE,KAAK,gBAAgB,EAAE,EAAG;AAC3C,QAAI,WAAW,EAAE,KAAK,eAAe,EAAE,GAAG;AACxC,gBAAU;AACV;AAAA,IACF;AACA,eAAW,SAAS,MAAM,KAAK,GAAG,UAAU,GAAG;AAC7C,YAAM,KAAK;AACX,UAAI,KAAM;AAAA,IACZ;AAAA,EACF;AAEA,QAAM,SAAS;AACf,SAAO;AACT;AAWA,SAAS,qBACP,MACA,MACuB;AACvB,QAAM,UAAU,2BAA2B,MAAM,IAAI;AACrD,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,WAAW,OAAO,SAAS,QAAQ,QAAQ,YAAa,EAAE;AAChE,QAAM,YAAY,QAAQ,QAAQ;AAClC,MAAI,cAAc,SAAS;AACzB,UAAM,SAAS,0BAA0B,MAAM,OAAO;AACtD,QAAI,CAAC,QAAQ;AAEX,YAAM,YAAY,QAAQ,cAA2B,mBAAmB;AACxE,UAAI,CAAC,UAAW,QAAO;AACvB,aAAO;AAAA,QACL;AAAA,QACA,SAAS;AAAA,QACT,WAAW;AAAA,QACX,gBAAgB;AAAA,MAClB;AAAA,IACF;AACA,UAAM,UAAU,OAAO,SAAS,OAAO,QAAQ,WAAY,EAAE;AAC7D,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,gBAAgB;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,IACT,WAAW;AAAA,IACX,gBAAgB,kBAAkB,OAAO;AAAA,EAC3C;AACF;AAEO,SAAS,iBAAiB,MAAqC;AACpE,QAAM,MAAM,OAAO,aAAa;AAChC,MAAI,CAAC,OAAO,IAAI,eAAe,EAAG,QAAO;AACzC,MAAI,CAAC,IAAI,cAAc,CAAC,IAAI,UAAW,QAAO;AAC9C,MAAI,CAAC,KAAK,SAAS,IAAI,UAAU,KAAK,CAAC,KAAK,SAAS,IAAI,SAAS,EAAG,QAAO;AAE5E,QAAM,IAAI,qBAAqB,IAAI,YAAY,IAAI;AACnD,QAAM,IAAI,qBAAqB,IAAI,WAAW,IAAI;AAClD,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AAErB,QAAM,SAAmB;AAAA,IACvB,YAAY,EAAE;AAAA,IACd,WAAW,EAAE;AAAA,IACb,QAAQ,EAAE,iBAAiB,iBAAiB,EAAE,WAAW,IAAI,YAAY,IAAI,YAAY;AAAA,EAC3F;AACA,QAAM,QAAkB;AAAA,IACtB,YAAY,EAAE;AAAA,IACd,WAAW,EAAE;AAAA,IACb,QAAQ,EAAE,iBAAiB,iBAAiB,EAAE,WAAW,IAAI,WAAW,IAAI,WAAW;AAAA,EACzF;AACA,SAAO,EAAE,QAAQ,MAAM;AACzB;AAOO,SAAS,YAAY,MAAmB,KAAgC;AAC7E,MAAI;AACJ,MAAI,iBAAiB;AAErB,MAAI,IAAI,aAAa,MAAM;AACzB,gBAAY,eAAe,MAAM,IAAI,YAAY,IAAI,SAAS;AAC9D,QAAI,CAAC,UAAW,QAAO;AAAA,EACzB,OAAO;AACL,gBAAY,qBAAqB,MAAM,IAAI,YAAY,IAAI,MAAM;AACjE,QAAI,CAAC,UAAW,QAAO;AACvB,qBAAiB,kBAAkB,SAAS;AAAA,EAC9C;AAEA,MAAI,YAAY,KAAK,IAAI,GAAG,IAAI,SAAS,cAAc;AACvD,MAAI,SAA0B;AAC9B,MAAI,UAA8B;AAClC,MAAI,WAAwB;AAE5B,WAAS,MAAM,GAAe;AAC5B,QAAI,OAAQ;AACZ,QAAI,EAAE,aAAa,KAAK,WAAW;AACjC,YAAM,OAAO,EAAE,eAAe,IAAI;AAClC,UAAI,aAAa,KAAK;AACpB,iBAAS,EAAE,MAAM,GAAG,QAAQ,UAAU;AACtC;AAAA,MACF;AACA,mBAAa;AACb,gBAAU;AACV,iBAAW;AACX;AAAA,IACF;AACA,QAAI,EAAE,aAAa,KAAK,aAAc;AACtC,UAAM,KAAK;AACX,QAAI,kBAAkB,IAAI,SAAU,EAAG;AAEvC,QAAI,WAAW,EAAE,KAAK,gBAAgB,EAAE,EAAG;AAC3C,QAAI,WAAW,EAAE,KAAK,eAAe,EAAE,GAAG;AACxC,UAAI,cAAc,GAAG;AACnB,cAAM,SAAS,GAAG;AAClB,YAAI,QAAQ;AACV,mBAAS,EAAE,MAAM,QAAQ,QAAQ,cAAc,EAAE,EAAE;AACnD;AAAA,QACF;AAAA,MACF;AACA,mBAAa;AACb,gBAAU;AACV;AAAA,IACF;AACA,eAAW,SAAS,MAAM,KAAK,GAAG,UAAU,GAAG;AAC7C,YAAM,KAAK;AACX,UAAI,OAAQ;AAAA,IACd;AAAA,EACF;AAEA,QAAM,SAAS;AACf,MAAI,OAAQ,QAAO;AAInB,MAAI,WAAW,cAAc,GAAG;AAC9B,UAAM,SAAU,QAAwB;AACxC,QAAI,OAAQ,QAAO,EAAE,MAAM,QAAQ,QAAQ,cAAc,OAAO,IAAI,EAAE;AAAA,EACxE;AAOA,MAAI,UAAU;AACZ,UAAM,IAAI;AACV,WAAO,EAAE,MAAM,GAAG,SAAS,EAAE,eAAe,IAAI,OAAO;AAAA,EACzD;AACA,SAAO,EAAE,MAAM,WAAW,QAAQ,EAAE;AACtC;AAEO,SAAS,gBAAgB,GAAc,GAAuB;AACnE,SACE,EAAE,OAAO,eAAe,EAAE,OAAO,eAChC,EAAE,OAAO,aAAa,SAAS,EAAE,OAAO,aAAa,OACtD,EAAE,OAAO,WAAW,EAAE,OAAO,UAC7B,EAAE,MAAM,eAAe,EAAE,MAAM,eAC9B,EAAE,MAAM,aAAa,SAAS,EAAE,MAAM,aAAa,OACpD,EAAE,MAAM,WAAW,EAAE,MAAM;AAE/B;AASO,SAAS,qBAAqB,KAAyB;AAC5D,MAAI,IAAI,OAAO,eAAe,IAAI,MAAM,WAAY,QAAO;AAC3D,MAAI,IAAI,OAAO,aAAa,QAAQ,IAAI,MAAM,aAAa,KAAM,QAAO;AACxE,SAAO,IAAI,OAAO,cAAc,IAAI,MAAM;AAC5C;AAEO,SAAS,kBAAkB,MAAmB,UAA2B;AAC9E,QAAM,SAAS,OAAO,aAAa;AACnC,MAAI,CAAC,OAAQ;AACb,MAAI,qBAAqB,QAAQ,GAAG;AAElC,WAAO,gBAAgB;AACvB;AAAA,EACF;AACA,QAAM,WAAW,YAAY,MAAM,SAAS,MAAM;AAClD,QAAM,UAAU,YAAY,MAAM,SAAS,KAAK;AAChD,MAAI,CAAC,YAAY,CAAC,QAAS;AAO3B,MACE,OAAO,eAAe,SAAS,QAC/B,OAAO,iBAAiB,SAAS,UACjC,OAAO,cAAc,QAAQ,QAC7B,OAAO,gBAAgB,QAAQ,QAC/B;AACA;AAAA,EACF;AAEA,MAAI;AACF,WAAO,gBAAgB;AACvB,WAAO,iBAAiB,SAAS,MAAM,SAAS,QAAQ,QAAQ,MAAM,QAAQ,MAAM;AAAA,EACtF,QAAQ;AAAA,EAER;AACF;;;AC3bA,IAAAC,eAA2B;;;ACS3B,kBAA6C;;;ACGtC,IAAM,mBAAmB;;;ADgCzB,SAAS,YAAY,MAAqC;AAC/D,MAAI,CAAC,QAAQ,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAC9C,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,UAAU,EAAE,gBAAgB,MAAM,WAAW;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,SAA4B,CAAC;AACnC,MAAI;AACF,mBAAe,MAAM,CAAC,GAAG,MAAM;AAAA,EACjC,QAAQ;AAON,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,EAAG,QAAO;AAUhC,sBAAoB,MAAM;AAmB1B,QAAM,cAAc,OAAO;AAAA,IACzB,CAAC,MAAM,EAAE,KAAK,KAAK,EAAE,SAAS,KAAK,EAAE,MAAM,KAAK,CAAC,WAAO,0BAAa,GAAG,MAAM,CAAC;AAAA,EACjF;AACA,MAAI,CAAC,YAAa,QAAO;AAmBzB,QAAM,aAAa,OAAO,SAAS,KAAK,OAAO,CAAC,EAAE,SAAS;AAC3D,SAAO,EAAE,QAAQ,WAAW,OAAO,SAAS,OAAO,GAAI,aAAa,EAAE,YAAY,KAAc,IAAI,CAAC,EAAG;AAC1G;AAQA,IAAM,eAAe,oBAAI,IAAI,CAAC,UAAU,SAAS,QAAQ,QAAQ,QAAQ,SAAS,KAAK,CAAC;AAIxF,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKD,IAAM,qBAAqB,oBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,gBAAgB,KAAsB;AAC7C,SAAO,IAAI,SAAS,GAAG;AACzB;AAEA,SAAS,mBAAmB,IAAsB;AAChD,QAAM,MAAM,GAAG,iBAAiB,GAAG;AACnC,aAAW,KAAK,MAAM,KAAK,GAAG,GAAG;AAC/B,QAAI,mBAAmB,IAAI,EAAE,QAAQ,YAAY,CAAC,EAAG,QAAO;AAAA,EAC9D;AACA,SAAO;AACT;AAMA,SAAS,eAAe,WAAoB,OAAkB,QAAiC;AAC7F,MAAI,QAAqB,CAAC;AAC1B,QAAM,aAAa,MAAM;AACvB,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,QAAQ;AACd,YAAQ,CAAC;AACT,UAAM,QAAQ,qBAAqB,OAAO,KAAK;AAC/C,QAAI,MAAM,SAAS,EAAG,QAAO,KAAK,UAAU,aAAa,OAAO,CAAC,CAAC,CAAC;AAAA,EACrE;AAEA,aAAW,QAAQ,MAAM,KAAK,UAAU,UAAU,GAAG;AACnD,QAAI,KAAK,aAAa,KAAK,aAAc;AACzC,QAAI,KAAK,aAAa,KAAK,WAAW;AACpC,YAAM,KAAK,IAAI;AACf;AAAA,IACF;AACA,QAAI,KAAK,aAAa,KAAK,aAAc;AACzC,UAAM,KAAK;AACX,UAAM,MAAM,GAAG,QAAQ,YAAY;AACnC,QAAI,aAAa,IAAI,GAAG,KAAK,gBAAgB,GAAG,EAAG;AAEnD,YAAQ,KAAK;AAAA,MACX,KAAK;AAAA,MACL,KAAK,MAAM;AACT,mBAAW;AACX,mBAAW,IAAI,QAAQ,OAAO,YAAY,UAAU,GAAG,OAAO,MAAM;AACpE;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,MAAM;AACT,mBAAW;AACX,cAAM,QAAQ,OAAO,IAAI,CAAC,CAAC;AAC3B,cAAM,QAAoB,EAAE,MAAM;AAClC,cAAM,QAAQ,UAAU,EAAE;AAC1B,YAAI,MAAO,OAAM,QAAQ;AACzB,cAAM,QAAQ,qBAAqB,MAAM,KAAK,GAAG,UAAU,GAAG,KAAK;AACnE,eAAO,KAAK,UAAU,WAAW,OAAO,KAAK,CAAC;AAC9C;AAAA,MACF;AAAA,MACA,KAAK,cAAc;AACjB,mBAAW;AACX,yBAAiB,IAAI,OAAO,MAAM;AAClC;AAAA,MACF;AAAA,MACA,KAAK,SAAS;AACZ,mBAAW;AACX,oBAAY,IAAI,OAAO,MAAM;AAC7B;AAAA,MACF;AAAA,MACA,KAAK,MAAM;AAKT,mBAAW;AACX,cAAM,cAAc,wBAAwB,EAAE;AAC9C,cAAM,QAAQ,GAAG,UAAU,IAAI;AAC/B,mBAAW,KAAK,wBAAwB,KAAK,EAAG,GAAE,OAAO;AACzD,cAAM,QAAQ,qBAAqB,MAAM,KAAK,MAAM,UAAU,GAAG,KAAK;AACtE,eAAO,KAAK,UAAU,YAAY,OAAO,EAAE,UAAU,UAAU,WAAW,EAAE,CAAC,CAAC;AAC9E,mBAAW,UAAU,aAAa;AAChC,gBAAM,aAAuB,OAAO,QAAQ,YAAY,MAAM,OAAO,YAAY;AACjF,qBAAW,QAAQ,YAAY,GAAG,OAAO,MAAM;AAAA,QACjD;AACA;AAAA,MACF;AAAA,MACA,KAAK;AAMH,cAAM,KAAK,IAAI;AACf;AAAA,MACF,KAAK;AACH,cAAM,KAAK,IAAI;AACf;AAAA,MACF,SAAS;AACP,cAAM,YAAY,GAAG,aAAa,OAAO,KAAK;AAC9C,cAAM,gBAAgB,mBAAmB,SAAS;AAClD,YAAI,iBAAiB,QAAQ,CAAC,mBAAmB,EAAE,GAAG;AACpD,qBAAW;AACX,gBAAM,SAAS,mBAAmB,EAAE;AACpC,gBAAM,WAAqB,SAAS,mBAAmB,MAAM,IAAI;AACjE,gBAAM,QAAoB,EAAE,UAAU,WAAW,cAAc;AAC/D,gBAAM,QAAQ,UAAU,EAAE;AAC1B,cAAI,MAAO,OAAM,QAAQ;AACzB,gBAAM,QAAQ,qBAAqB,MAAM,KAAK,GAAG,UAAU,GAAG,KAAK;AACnE,iBAAO,KAAK,UAAU,YAAY,OAAO,KAAK,CAAC;AAC/C;AAAA,QACF;AACA,YAAI,mBAAmB,EAAE,GAAG;AAK1B,qBAAW;AACX,cAAI,YAAY,cAAc,KAAK,KAAK;AACxC,sBAAY,gBAAgB,GAAG,aAAa,OAAO,GAAG,SAAS;AAC/D,yBAAe,IAAI,WAAW,MAAM;AACpC;AAAA,QACF;AACA,YAAI,iBAAiB,IAAI,GAAG,GAAG;AAC7B,qBAAW;AACX,gBAAM,QAAoB,CAAC;AAC3B,gBAAM,QAAQ,UAAU,EAAE;AAC1B,cAAI,MAAO,OAAM,QAAQ;AACzB,gBAAM,QAAQ,qBAAqB,MAAM,KAAK,GAAG,UAAU,GAAG,KAAK;AACnE,iBAAO,KAAK,UAAU,aAAa,OAAO,KAAK,CAAC;AAChD;AAAA,QACF;AAGA,cAAM,KAAK,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACA,aAAW;AACb;AAcA,SAAS,WACP,QACA,MACA,OACA,OACA,QACM;AACN,aAAW,SAAS,MAAM,KAAK,OAAO,QAAQ,GAAG;AAC/C,UAAM,MAAM,MAAM,QAAQ,YAAY;AACtC,QAAI,QAAQ,QAAQ,QAAQ,MAAM;AAIhC,YAAM,cAAwB,QAAQ,OAAO,YAAY;AACzD,iBAAW,OAAO,aAAa,QAAQ,GAAG,OAAO,MAAM;AACvD;AAAA,IACF;AACA,QAAI,QAAQ,KAAM;AAQlB,UAAM,cAAc,wBAAwB,KAAK;AAMjD,UAAM,QAAQ,MAAM,UAAU,IAAI;AAClC,eAAW,KAAK,wBAAwB,KAAK,EAAG,GAAE,OAAO;AACzD,UAAM,QAAQ,qBAAqB,MAAM,KAAK,MAAM,UAAU,GAAG,KAAK;AAEtE,UAAM,QAAoB,EAAE,UAAU,MAAM,WAAW,WAAW,KAAK,EAAE;AACzE,UAAM,QAAQ,UAAU,KAAK;AAC7B,QAAI,MAAO,OAAM,QAAQ;AACzB,WAAO,KAAK,UAAU,YAAY,OAAO,KAAK,CAAC;AAC/C,eAAW,UAAU,aAAa;AAChC,YAAM,aAAuB,OAAO,QAAQ,YAAY,MAAM,OAAO,YAAY;AACjF,iBAAW,QAAQ,YAAY,QAAQ,GAAG,OAAO,MAAM;AAAA,IACzD;AAAA,EACF;AACF;AAWA,SAAS,wBAAwB,MAA0B;AACzD,QAAM,MAAM,MAAM,KAAK,KAAK,iBAAiB,QAAQ,CAAC;AACtD,SAAO,IAAI,OAAO,CAAC,OAAO;AACxB,QAAI,IAAI,GAAG;AACX,WAAO,KAAK,MAAM,MAAM;AACtB,YAAM,IAAI,EAAE,QAAQ,YAAY;AAChC,UAAI,MAAM,QAAQ,MAAM,QAAQ,MAAM,WAAW,MAAM,QAAQ,MAAM,KAAM,QAAO;AAClF,UAAI,EAAE;AAAA,IACR;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAKA,SAAS,iBAAiB,IAAa,OAAkB,QAAiC;AACxF,MAAI,CAAC,mBAAmB,EAAE,GAAG;AAC3B,UAAM,QAAoB,CAAC;AAC3B,UAAM,QAAQ,UAAU,EAAE;AAC1B,QAAI,MAAO,OAAM,QAAQ;AACzB,UAAM,QAAQ,qBAAqB,MAAM,KAAK,GAAG,UAAU,GAAG,KAAK;AACnE,WAAO,KAAK,UAAU,cAAc,OAAO,KAAK,CAAC;AACjD;AAAA,EACF;AACA,QAAM,SAA4B,CAAC;AACnC,iBAAe,IAAI,OAAO,MAAM;AAChC,aAAW,KAAK,QAAQ;AACtB,WAAO,KAAK,EAAE,SAAS,cAAc,EAAE,GAAG,GAAG,MAAM,aAAa,IAAI,CAAC;AAAA,EACvE;AACF;AAKA,SAAS,YAAY,OAAgB,OAAkB,QAAiC;AACtF,aAAW,OAAO,WAAW,KAAK,GAAG;AACnC,UAAM,QAAQ,MAAM,KAAK,IAAI,QAAQ,EAAE;AAAA,MAAO,CAAC,MAC7C,CAAC,MAAM,IAAI,EAAE,SAAS,EAAE,QAAQ,YAAY,CAAC;AAAA,IAC/C;AACA,eAAW,QAAQ,OAAO;AACxB,YAAM,aAAgC,CAAC;AACvC,qBAAe,MAAM,OAAO,UAAU;AACtC,iBAAW,KAAK,YAAY;AAC1B,cAAM,QAAoB,CAAC;AAC3B,YAAI,EAAE,MAAM,MAAO,OAAM,QAAQ,EAAE,MAAM;AACzC,eAAO,KAAK,UAAU,aAAa,EAAE,OAAO,KAAK,CAAC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,WAAW,OAA2B;AAC7C,QAAM,OAAkB,CAAC;AACzB,aAAW,SAAS,MAAM,KAAK,MAAM,QAAQ,GAAG;AAC9C,UAAM,MAAM,MAAM,QAAQ,YAAY;AACtC,QAAI,QAAQ,MAAM;AAChB,WAAK,KAAK,KAAK;AAAA,IACjB,WAAW,QAAQ,WAAW,QAAQ,WAAW,QAAQ,SAAS;AAChE,iBAAW,KAAK,MAAM,KAAK,MAAM,QAAQ,GAAG;AAC1C,YAAI,EAAE,QAAQ,YAAY,MAAM,KAAM,MAAK,KAAK,CAAC;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAiBA,SAAS,mBAAmB,WAAkC;AAC5D,QAAM,IAAI,oCAAoC,KAAK,SAAS;AAC5D,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,OAAO,SAAS,EAAE,CAAC,GAAG,EAAE;AAClC,QAAM,aAAa,OAAO,SAAS,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC,IAAI;AAC7D,QAAM,aAAa,4BAA4B,aAAa,SAAS,CAAC;AACtE,SAAO,WAAW,KAAK,IAAI,YAAY,UAAU,CAAC;AACpD;AAOA,SAAS,aAAa,WAA2B;AAC/C,QAAM,MAAM,WAAW,SAAS,EAAE,aAAa;AAC/C,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,IAAI,gCAAgC,KAAK,IAAI,KAAK,CAAC;AACzD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,MAAM,OAAO,WAAW,EAAE,CAAC,CAAC;AAClC,MAAI,CAAC,OAAO,SAAS,GAAG,KAAK,MAAM,EAAG,QAAO;AAC7C,UAAQ,EAAE,CAAC,EAAE,YAAY,GAAG;AAAA,IAC1B,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf;AACE,aAAO;AAAA,EACX;AACF;AAIA,SAAS,4BAA4B,IAAoB;AACvD,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,EAAE,IAAI,CAAC;AAC5C;AAIA,SAAS,mBAAmB,IAA6B;AACvD,QAAM,MAAM,GAAG,iBAAiB,GAAG;AACnC,aAAW,KAAK,MAAM,KAAK,GAAG,GAAG;AAC/B,UAAM,SAAS,EAAE,aAAa,OAAO,KAAK,IAAI,YAAY,EAAE,QAAQ,QAAQ,EAAE;AAC9E,QAAI,MAAM,SAAS,iBAAiB,EAAG,QAAO;AAAA,EAChD;AACA,SAAO;AACT;AAIA,SAAS,mBAAmB,UAA6B;AACvD,QAAM,MAAM,CAAC,UAAU,GAAG,MAAM,KAAK,SAAS,iBAAiB,GAAG,CAAC,CAAC;AACpE,aAAW,KAAK,KAAK;AACnB,UAAM,SAAS,EAAE,aAAa,OAAO,KAAK,IAAI,YAAY;AAC1D,QAAI,MAAM,SAAS,QAAQ,KAAK,MAAM,SAAS,WAAW,EAAG,QAAO;AAAA,EACtE;AACA,QAAM,QAAQ,SAAS,eAAe,IAAI,QAAQ,MAAM,GAAG,EAAE,KAAK;AAClE,MAAI,cAAc,KAAK,IAAI,EAAG,QAAO;AACrC,MAAI,gBAAgB,KAAK,IAAI,EAAG,QAAO;AACvC,SAAO;AACT;AAMA,SAAS,qBAAqB,OAAoB,OAA6B;AAC7E,QAAM,MAAiB,CAAC;AACxB,aAAW,KAAK,MAAO,gBAAe,GAAG,OAAO,GAAG;AACnD,SAAO,mBAAmB,uBAAuB,iBAAiB,GAAG,CAAC,CAAC;AACzE;AAKA,SAAS,uBAAuB,KAA2B;AACzD,SAAO,IAAI;AAAA,IAAI,CAAC,OACd,OAAO,GAAG,WAAW,WAAW,EAAE,GAAG,IAAI,QAAQ,GAAG,OAAO,QAAQ,UAAU,GAAG,EAAE,IAAI;AAAA,EACxF;AACF;AAEA,SAAS,eAAe,MAAiB,OAAkB,KAAsB;AAC/E,MAAI,KAAK,aAAa,KAAK,aAAc;AACzC,MAAI,KAAK,aAAa,KAAK,WAAW;AACpC,UAAM,OAAO,oBAAoB,KAAK,eAAe,EAAE;AACvD,QAAI,KAAK,SAAS,EAAG,QAAO,KAAK,MAAM,KAAK;AAC5C;AAAA,EACF;AACA,MAAI,KAAK,aAAa,KAAK,aAAc;AACzC,QAAM,KAAK;AACX,QAAM,MAAM,GAAG,QAAQ,YAAY;AAEnC,MAAI,aAAa,IAAI,GAAG,KAAK,gBAAgB,GAAG,EAAG;AACnD,MAAI,QAAQ,OAAO;AACjB,UAAM,KAAK,sBAAsB,EAAE;AACnC,QAAI,GAAI,KAAI,KAAK,EAAE;AACnB;AAAA,EACF;AACA,MAAI,QAAQ,MAAM;AAShB,QAAI,KAAK,EAAE,QAAQ,IAAI,CAAC;AACxB;AAAA,EACF;AAEA,QAAM,aAAa,GAAG,aAAa,OAAO,KAAK,IAAI,YAAY,EAAE,QAAQ,QAAQ,EAAE;AACnF,MAAI,UAAU,SAAS,iBAAiB,EAAG;AAE3C,MAAI,YAAY,cAAc,KAAK,KAAK;AACxC,cAAY,gBAAgB,GAAG,aAAa,OAAO,GAAG,SAAS;AAC/D,MAAI,QAAQ,KAAK;AACf,UAAM,OAAO,GAAG,aAAa,MAAM;AACnC,QAAI,KAAM,aAAY,EAAE,GAAG,WAAW,MAAM,EAAE,KAAK,EAAE;AAAA,EACvD;AAEA,aAAW,SAAS,MAAM,KAAK,GAAG,UAAU,EAAG,gBAAe,OAAO,WAAW,GAAG;AACrF;AAwBA,IAAM,kBAAkB;AASxB,SAAS,yBAAyB,SAAyB;AACzD,QAAM,aAAa,QAAQ,QAAQ,GAAG;AACtC,MAAI,eAAe,GAAI,QAAO;AAC9B,QAAM,OAAO,QAAQ,MAAM,GAAG,UAAU;AACxC,QAAM,UAAU,QAAQ,MAAM,aAAa,CAAC;AAC5C,MAAI,YAAY,KAAK,IAAI,GAAG;AAC1B,UAAM,UAAU,QAAQ,SAAS,IAAI,IAAI,IAAI,QAAQ,SAAS,GAAG,IAAI,IAAI;AACzE,WAAO,KAAK,IAAI,GAAG,KAAK,MAAO,QAAQ,SAAS,IAAK,CAAC,IAAI,OAAO;AAAA,EACnE;AAIA,MAAI;AACF,WAAO,mBAAmB,OAAO,EAAE;AAAA,EACrC,QAAQ;AACN,WAAO,QAAQ;AAAA,EACjB;AACF;AAEA,SAAS,sBAAsB,IAA6B;AAC1D,QAAM,MAAM,GAAG,aAAa,KAAK;AACjC,MAAI,CAAC,OAAO,CAAC,IAAI,WAAW,aAAa,EAAG,QAAO;AAWnD,MAAI,yBAAyB,GAAG,IAAI,gBAAiB,QAAO;AAC5D,QAAM,QAAQ,WAAW,GAAG,aAAa,OAAO,CAAC;AACjD,QAAM,QAAQ,mBAAmB,IAAI,SAAS,KAAK;AACnD,QAAM,SAAS,mBAAmB,IAAI,UAAU,KAAK;AACrD,MAAI,SAAS,QAAQ,UAAU,KAAM,QAAO;AAM5C,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI,kBAAkB;AACxB,QAAK,IAAI,mBAAoB;AAC7B,QAAI;AAAA,EACN;AACA,SAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,KAAK,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ,KAAK,MAAM,CAAC,EAAE,EAAE;AACvF;AAUA,SAAS,mBACP,IACA,MACA,OACoB;AACpB,QAAM,MAAM,GAAG,aAAa,IAAI;AAChC,MAAI,OAAO,gBAAgB,KAAK,IAAI,KAAK,CAAC,GAAG;AAC3C,UAAM,IAAI,OAAO,WAAW,GAAG;AAC/B,QAAI,OAAO,SAAS,CAAC,KAAK,IAAI,EAAG,QAAO;AAAA,EAC1C;AACA,QAAM,YAAY,MAAM,IAAI;AAC5B,MAAI,WAAW;AACb,UAAM,IAAI,cAAc,SAAS;AACjC,QAAI,KAAK,KAAM,QAAO;AAAA,EACxB;AACA,SAAO;AACT;AAMA,SAAS,cAAc,KAAiC;AACtD,QAAM,IAAI,IAAI,KAAK;AACnB,QAAM,IAAI,oCAAoC,KAAK,CAAC;AACpD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,MAAM,OAAO,WAAW,EAAE,CAAC,CAAC;AAClC,MAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,EAAG,QAAO;AAC9C,QAAM,QAAQ,EAAE,CAAC,KAAK,MAAM,YAAY;AACxC,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,OAAO,KAAK;AAAA,IACrB,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,OAAO,KAAK;AAAA,IACrB,KAAK;AACH,aAAO,OAAO,KAAK;AAAA,IACrB;AACE,aAAO;AAAA,EACX;AACF;AAIA,SAAS,cAAc,KAAa,OAA6B;AAC/D,UAAQ,KAAK;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,MAAM,KAAK;AAAA,IAChC,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,KAAK;AAAA,IAClC,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,WAAW,KAAK;AAAA,IACrC,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,KAAK;AAAA,IAClC;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,WAAW,WAAkD;AACpE,QAAM,MAA8B,CAAC;AACrC,MAAI,CAAC,UAAW,QAAO;AACvB,aAAW,QAAQ,UAAU,MAAM,GAAG,GAAG;AACvC,UAAM,MAAM,KAAK,QAAQ,GAAG;AAC5B,QAAI,QAAQ,GAAI;AAChB,UAAM,OAAO,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK,EAAE,YAAY;AACnD,UAAM,MAAM,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK;AACrC,QAAI,KAAM,KAAI,IAAI,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAaA,SAAS,gBAAgB,WAA0B,OAA6B;AAC9E,QAAM,QAAQ,WAAW,SAAS;AAClC,MAAI,OAAO,KAAK,KAAK,EAAE,WAAW,EAAG,QAAO;AAC5C,QAAM,OAAkB,EAAE,GAAG,MAAM;AAEnC,MAAI,iBAAiB,OAAO;AAC1B,UAAM,IAAI,MAAM,aAAa,EAAE,YAAY;AAC3C,QAAI,MAAM,UAAU,YAAY,KAAK,CAAC,EAAG,MAAK,OAAO;AAAA,aAC5C,MAAM,YAAY,YAAY,KAAK,CAAC,EAAG,QAAO,KAAK;AAAA,EAC9D;AAEA,MAAI,gBAAgB,OAAO;AACzB,UAAM,IAAI,MAAM,YAAY,EAAE,YAAY;AAC1C,QAAI,MAAM,YAAY,MAAM,UAAW,MAAK,SAAS;AAAA,aAC5C,MAAM,SAAU,QAAO,KAAK;AAAA,EACvC;AAEA,QAAM,OAAO,MAAM,sBAAsB,KAAK,MAAM,iBAAiB;AACrE,MAAI,SAAS,QAAW;AACtB,UAAM,IAAI,KAAK,YAAY;AAC3B,WAAO,KAAK;AACZ,WAAO,KAAK;AACZ,QAAI,EAAE,SAAS,WAAW,EAAG,MAAK,YAAY;AAC9C,QAAI,EAAE,SAAS,cAAc,EAAG,MAAK,SAAS;AAAA,EAChD;AAEA,MAAI,WAAW,OAAO;AACpB,UAAM,MAAM,kBAAkB,MAAM,KAAK;AACzC,QAAI,OAAO,CAAC,QAAQ,GAAG,EAAG,MAAK,QAAQ;AAAA,QAClC,QAAO,KAAK;AAAA,EACnB;AAEA,QAAM,KAAK,MAAM,kBAAkB,KAAK,MAAM;AAC9C,MAAI,OAAO,QAAW;AACpB,UAAM,MAAM,kBAAkB,EAAE;AAChC,QAAI,OAAO,QAAQ,UAAW,MAAK,YAAY;AAAA,QAC1C,QAAO,KAAK;AAAA,EACnB;AAEA,MAAI,eAAe,OAAO;AACxB,UAAM,KAAK,gBAAgB,MAAM,WAAW,CAAC;AAS7C,QAAI,MAAM,KAAM,MAAK,WAAW,iBAAiB,EAAE;AAAA,EACrD;AAEA,SAAO;AACT;AAQA,SAAS,gBAAgB,KAAiC;AACxD,QAAM,IAAI,IAAI,KAAK,EAAE,YAAY;AACjC,QAAM,IAAI,4BAA4B,KAAK,CAAC;AAC5C,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,MAAM,OAAO,WAAW,EAAE,CAAC,CAAC;AAClC,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAClC,MAAI;AACJ,UAAQ,EAAE,CAAC,GAAG;AAAA,IACZ,KAAK;AACH,WAAK;AACL;AAAA,IACF,KAAK;AACH,WAAK,MAAM;AACX;AAAA,IACF,KAAK;AACH,WAAK,MAAM;AACX;AAAA,IACF;AACE,aAAO;AAAA,EACX;AAKA,QAAM,UAAU,KAAK,MAAM,KAAK,EAAE,IAAI;AACtC,MAAI,WAAW,KAAK,UAAU,IAAK,QAAO;AAC1C,SAAO;AACT;AAIA,SAAS,iBAAiB,IAAoB;AAC5C,QAAM,UAAU,KAAK,MAAM,KAAK,EAAE,IAAI;AACtC,QAAM,MAAM,OAAO,UAAU,OAAO,IAAI,OAAO,OAAO,IAAI,QAAQ,QAAQ,CAAC;AAC3E,SAAO,GAAG,GAAG;AACf;AAEA,IAAM,eAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AACV;AAIA,SAAS,kBAAkB,KAA6C;AACtE,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,IAAI,IAAI,KAAK,EAAE,YAAY;AACjC,MAAI,CAAC,KAAK,MAAM,iBAAiB,MAAM,UAAU,MAAM,aAAa,MAAM,UAAW,QAAO;AAC5F,MAAI,EAAE,WAAW,GAAG,GAAG;AACrB,QAAI,MAAM,EAAE,MAAM,CAAC;AACnB,QAAI,IAAI,WAAW,EAAG,OAAM,IAAI,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE;AACnE,WAAO,gBAAgB,KAAK,GAAG,IAAI,IAAI,GAAG,KAAK;AAAA,EACjD;AACA,QAAM,WAAW,qBAAqB,KAAK,CAAC;AAC5C,MAAI,UAAU;AACZ,UAAM,QAAQ,SAAS,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACxD,QAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,UAAM,QAAQ,MAAM,CAAC,MAAM,SAAY,OAAO,WAAW,MAAM,CAAC,CAAC,IAAI;AACrE,QAAI,UAAU,EAAG,QAAO;AACxB,UAAM,QAAQ,CAAC,MAAc;AAC3B,YAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,OAAO,SAAS,GAAG,EAAE,KAAK,CAAC,CAAC;AAClE,aAAO,IAAI,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,IACzC;AACA,WAAO,IAAI,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC;AAAA,EAChE;AACA,SAAO,aAAa,CAAC;AACvB;AAEA,SAAS,QAAQ,KAAsB;AACrC,SAAO,IAAI,YAAY,MAAM;AAC/B;AAEA,SAAS,UAAU,IAAqC;AACtD,QAAM,QAAQ,WAAW,GAAG,aAAa,OAAO,CAAC;AACjD,QAAM,IAAI,MAAM,YAAY;AAC5B,MAAI,CAAC,EAAG,QAAO;AACf,UAAQ,EAAE,YAAY,GAAG;AAAA,IACvB,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,WAAW,GAAmB;AACrC,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,4BAAgB,KAAK,MAAM,CAAC,CAAC,CAAC;AAC5D;AAmBA,SAAS,oBAAoB,QAAiC;AAC5D,MAAI,IAAI;AACR,SAAO,IAAI,OAAO,QAAQ;AACxB,QAAI,OAAO,CAAC,EAAE,SAAS,YAAY;AACjC;AACA;AAAA,IACF;AACA,QAAI,IAAI,IAAI;AACZ,WAAO,IAAI,OAAO,UAAU,OAAO,CAAC,EAAE,SAAS,WAAY;AAC3D,QAAI,IAAI,IAAI,GAAG;AACb,UAAI,MAAM,OAAO;AACjB,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,cAAM,MAAO,OAAO,CAAC,EAAE,MAAM,aAAoC;AACjE,YAAI,MAAM,IAAK,OAAM;AAAA,MACvB;AACA,UAAI,MAAM,GAAG;AACX,iBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,gBAAM,MAAO,OAAO,CAAC,EAAE,MAAM,aAAoC;AACjE,iBAAO,CAAC,EAAE,MAAM,YAAY,WAAW,MAAM,GAAG;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AACA,QAAI;AAAA,EACN;AACF;AAMA,SAAS,OAAO,KAAgB,MAAc,OAAwB;AACpE,QAAM,WAAW,OAAO,KAAK,KAAK,EAAE,SAAS;AAC7C,MAAI,KAAK,WAAW,EAAE,QAAQ,MAAM,YAAY,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,CAAC;AACnF;AAWA,SAAS,oBAAoB,KAAqB;AAChD,SAAO,IAAI,QAAQ,eAAe,GAAG;AACvC;AAIA,SAAS,mBAAmB,KAA2B;AACrD,MAAI,MAAM;AACV,MAAI,IAAI,SAAS,KAAK,OAAO,IAAI,CAAC,EAAE,WAAW,UAAU;AACvD,UAAM,UAAU,IAAI,CAAC,EAAE,OAAO,QAAQ,OAAO,EAAE;AAC/C,QAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,MAAM,CAAC;AAAA,aAClC,YAAY,IAAI,CAAC,EAAE,OAAQ,OAAM,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,QAAQ,GAAG,GAAG,IAAI,MAAM,CAAC,CAAC;AAAA,EAC5F;AACA,MAAI,IAAI,SAAS,KAAK,OAAO,IAAI,IAAI,SAAS,CAAC,EAAE,WAAW,UAAU;AACpE,UAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,UAAM,UAAW,KAAK,OAAkB,QAAQ,OAAO,EAAE;AACzD,QAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,MAAM,GAAG,EAAE;AAAA,aACtC,YAAY,KAAK,OAAQ,OAAM,CAAC,GAAG,IAAI,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,QAAQ,QAAQ,CAAC;AAAA,EAC5F;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,KAA2B;AACnD,QAAM,MAAiB,CAAC;AACxB,aAAW,MAAM,KAAK;AACpB,UAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,QACE,QACA,OAAO,KAAK,WAAW,YACvB,OAAO,GAAG,WAAW,YACrB,UAAU,KAAK,YAAY,GAAG,UAAU,GACxC;AACA,WAAK,SAAS,KAAK,SAAS,GAAG;AAC/B;AAAA,IACF;AACA,QAAI,KAAK,EAAE,GAAG,GAAG,CAAC;AAAA,EACpB;AACA,SAAO;AACT;AAEA,SAAS,UAAU,GAA0B,GAAmC;AAC9E,QAAM,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AACxC,QAAM,KAAK,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AACxC,MAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,WAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,QAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAG,QAAO;AAC5B,UAAM,KAAM,EAA8B,GAAG,CAAC,CAAC;AAC/C,UAAM,KAAM,EAA8B,GAAG,CAAC,CAAC;AAC/C,QAAI,OAAO,OAAO,YAAY,OAAO,QAAQ,OAAO,OAAO,YAAY,OAAO,MAAM;AAClF,UAAI,KAAK,UAAU,EAAE,MAAM,KAAK,UAAU,EAAE,EAAG,QAAO;AAAA,IACxD,WAAW,OAAO,IAAI;AACpB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAA0B;AAC7C,MAAI,IAAI;AACR,aAAW,MAAM,MAAO,KAAI,OAAO,GAAG,WAAW,SAAU,MAAK,GAAG;AACnE,SAAO;AACT;AAEA,SAAS,UAAU,MAAiB,OAAkB,OAAoC;AACxF,SAAO,EAAE,MAAM,MAAM,YAAY,KAAK,GAAG,OAAO,MAAM;AACxD;;;ADhgCO,SAAS,UAAU,IAA6B;AAErD,QAAM,MAAM,GAAG,QAAQ,uBAAU;AACjC,MAAI,KAAK;AACP,QAAI;AACF,YAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,UAAI,SAAS,MAAM,QAAQ,MAAM,MAAM,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM;AAAA,IAC1E,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,OAAO,GAAG,QAAQ,WAAW;AACnC,MAAI,MAAM;AACR,UAAM,QAAQ,YAAY,IAAI;AAC9B,QAAI,MAAO,QAAO,EAAE,MAAM,QAAQ,MAAM;AAAA,EAC1C;AAoBA,QAAM,QAAQ,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,QAAQ,CAAC;AAClF,MAAI,MAAM,SAAS,EAAG,QAAO,EAAE,MAAM,UAAU,MAAM;AAGrD,QAAM,OAAO,GAAG,QAAQ,YAAY;AACpC,MAAI,KAAK,SAAS,EAAG,QAAO,EAAE,MAAM,QAAQ,KAAK;AAEjD,SAAO,EAAE,MAAM,OAAO;AACxB;;;AGlFA,IAAAC,eAAgE;AASzD,SAAS,wBAAwB,OAAgC;AACtE,SAAO,MAAM,OAAO;AAAA,IAAK,CAAC,MACxB,EAAE,MAAM,KAAK,CAAC,WAAO,2BAAa,GAAG,MAAM,KAAK,GAAG,OAAO,IAAI,WAAW,aAAa,CAAC;AAAA,EACzF;AACF;AAmBA,eAAsB,0BACpB,OACA,QACyB;AACzB,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,MAAM,OAAO,IAAI,OAAO,UAAU;AAChC,UAAI,UAAU;AACd,YAAM,QAAmB,MAAM,QAAQ;AAAA,QACrC,MAAM,MAAM,IAAI,OAAO,OAAyB;AAC9C,cAAI,KAAC,2BAAa,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,IAAI,WAAW,aAAa,EAAG,QAAO;AACjF,cAAI;AACF,kBAAM,OAAO,MAAM,cAAc,GAAG,OAAO,GAAG;AAC9C,kBAAM,SAAS,MAAM,OAAO,IAAI;AAChC,sBAAU;AACV,mBAAO,EAAE,GAAG,IAAI,QAAQ,EAAE,GAAG,GAAG,QAAQ,KAAK,OAAO,EAAE;AAAA,UACxD,SAAS,KAAK;AACZ,oBAAQ;AAAA,cACN;AAAA,cACA;AAAA,YACF;AACA,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AACA,aAAO,UAAU,EAAE,GAAG,OAAO,MAAM,IAAI;AAAA,IACzC,CAAC;AAAA,EACH;AACA,SAAO,EAAE,GAAG,OAAO,OAAO;AAC5B;AAsBA,eAAsB,cAAc,SAAgC;AAClE,QAAM,OAAO,MAAM,MAAM,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC;AACtD,QAAM,OAAO,KAAK;AAClB,SAAO,IAAI,KAAK,CAAC,IAAI,GAAG,iBAAiB,KAAK,IAAI,CAAC,IAAI,iBAAiB,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC;AACjG;AAEA,IAAM,kBAA0C;AAAA,EAC9C,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,iBAAiB;AACnB;AAEA,SAAS,iBAAiB,MAAsB;AAC9C,SAAO,gBAAgB,KAAK,YAAY,CAAC,KAAK;AAChD;;;ACrGA,mBASO;AA6JH;AA1IJ,IAAM,SAAS;AAWR,SAAS,4BAA4B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyD;AACvD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAsB,IAAI;AAElD,QAAM,kBAAc,0BAAY,MAA+B;AAC7D,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,YAAgC;AACpC,QAAI,aAAa,MAAM;AACrB,kBAAY,KAAK;AAAA,QACf,sBAAsB,UAAU,wBAAwB,SAAS;AAAA,MACnE;AAAA,IACF,OAAO;AACL,kBAAY,qBAAqB,MAAM,YAAY,MAAM;AAAA,IAC3D;AACA,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,gBAAgB,UAAU,QAAQ,gBACpC,OAAO,SAAS,UAAU,QAAQ,eAAe,EAAE,KAAK,IACxD;AACJ,UAAM,cAAc,SAAS;AAC7B,WAAO,UAAU;AAAA,MACf,8CAA8C,WAAW;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,SAAS,YAAY,QAAQ,SAAS,CAAC;AAE3C,QAAM,cAAU,0BAAY,MAAM;AAChC,UAAM,MAAM,YAAY;AACxB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,OAAO,CAAC,MAAM;AACjB,cAAQ,CAAC,SAAU,SAAS,OAAO,OAAO,IAAK;AAC/C;AAAA,IACF;AACA,UAAM,IAAI,IAAI,sBAAsB;AACpC,UAAM,KAAK,KAAK,sBAAsB;AACtC,UAAM,OAAa;AAAA,MACjB,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK;AAAA,MAC3B,MAAM,EAAE,OAAO,GAAG,OAAO,KAAK;AAAA,MAC9B,OAAO,EAAE;AAAA,MACT,QAAQ,EAAE;AAAA,IACZ;AACA;AAAA,MAAQ,CAAC,SACP,QACA,KAAK,QAAQ,KAAK,OAClB,KAAK,SAAS,KAAK,QACnB,KAAK,UAAU,KAAK,SACpB,KAAK,WAAW,KAAK,SACjB,OACA;AAAA,IACN;AAAA,EACF,GAAG,CAAC,aAAa,OAAO,CAAC;AAEzB,oCAAgB,MAAM;AACpB,YAAQ;AACR,QAAI,YAAY;AAChB,UAAM,YAAY,MAAM;AACtB,UAAI,CAAC,UAAW,SAAQ;AAAA,IAC1B;AACA,UAAM,KACJ,OAAO,0BAA0B,aAC7B,sBAAsB,SAAS,KAC9B,eAAe,SAAS,GAAG;AAClC,WAAO,MAAM;AACX,kBAAY;AACZ,UAAI,OAAO,yBAAyB,cAAc,OAAO,OAAO,YAAY,KAAK,GAAG;AAClF,6BAAqB,EAAE;AAAA,MACzB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,8BAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,WAAW,MAAM,QAAQ;AAC/B,WAAO,iBAAiB,UAAU,QAAQ;AAC1C,SAAK,iBAAiB,UAAU,QAAQ;AACxC,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,QAAQ;AAC7C,WAAK,oBAAoB,UAAU,QAAQ;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,SAAS,OAAO,CAAC;AAErB,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,eAA8B;AAAA,IAClC,UAAU;AAAA,IACV,KAAK,KAAK;AAAA,IACV,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,QAAQ,gBAAgB,MAAM;AAAA,IAC9B,cAAc;AAAA,IACd,WAAW;AAAA,IACX,eAAe;AAAA,IACf,QAAQ;AAAA,EACV;AAEA,QAAM,aAA4B;AAAA,IAChC,UAAU;AAAA,IACV,KAAK,KAAK,MAAM;AAAA,IAChB,MAAM,KAAK,OAAO;AAAA,IAClB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAEA,QAAM,cAAc,CAAC,MAAyC;AAC5D,QAAI,EAAE,WAAW,EAAG;AACpB,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,aAAS;AAAA,EACX;AAEA,SACE,4EACE;AAAA,gDAAC,SAAI,OAAO,cAAc,iBAAiB,OAAO,eAAW,MAAC;AAAA,IAC9D;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,iBAAiB;AAAA,QACjB,eAAe;AAAA,QACf,MAAK;AAAA,QACL,cAAW;AAAA,QACX,OAAM;AAAA,QAEN;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,QAAO;AAAA,YACP,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,gBAAe;AAAA,YACf,eAAW;AAAA,YAEX,sDAAC,UAAK,GAAE,yEAAwE;AAAA;AAAA,QAClF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;;;AC/LA,uBAA6B;;;ACGtB,IAAM,UAAmB,MAAM;AACpC,MAAI,OAAO,cAAc,YAAa,QAAO;AAK7C,QAAM,WACH,UAAoE,eAAe,YACpF,UAAU,YACV,UAAU,aACV;AACF,SAAO,wBAAwB,KAAK,QAAQ;AAC9C,GAAG;AASI,IAAM,aAAqB,SAAS,WAAM;AAG1C,SAAS,gBAAgB,GAAoD;AAClF,SAAO,SAAS,EAAE,UAAU,EAAE;AAChC;;;ADmD8C,IAAAC,sBAAA;AAvE9C,IAAM,YAAY;AAClB,IAAM,MAAM;AASL,SAAS,iBAAiB,EAAE,MAAM,KAAK,GAAuC;AAEnF,QAAM,QAAQ,KAAK,MAAM;AACzB,QAAM,YAAY,OAAO,WAAW,cAAc,OAAO,aAAa,YAAY;AAClF,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,YAAY,YAAY,CAAC,CAAC;AAEvE,QAAM,QAAuB;AAAA,IAC3B,UAAU;AAAA,IACV;AAAA,IACA,KAAK,QAAQ,KAAK,MAAM,MAAM,KAAK,SAAS;AAAA,IAC5C,WAAW,QAAQ,sBAAsB;AAAA,IACzC,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,eAAe;AAAA,EACjB;AAEA,QAAM,WAA0B;AAAA,IAC9B,SAAS;AAAA,IACT,UAAU,YAAY;AAAA,IACtB,UAAU;AAAA,IACV,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AAEA,QAAM,YAA2B;AAAA,IAC/B,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AAIA,QAAM,cAA6B;AAAA,IACjC,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,WAAW;AAAA,EACb;AAKA,MAAI,OAAO,aAAa,YAAa,QAAO,6EAAE;AAE9C,aAAO;AAAA,IACL,8CAAC,SAAI,OAAc,iBAAiB,OAAO,eAAW,MAAC,MAAK,WAC1D;AAAA,mDAAC,UAAK,OAAO,UAAW,gBAAK;AAAA,MAC7B,8CAAC,UAAK,OAAO,WACX;AAAA,qDAAC,SAAI,OAAO,aAAc,sBAAW;AAAA,QACrC,6CAAC,UAAK,OAAO,EAAE,SAAS,IAAI,GAAG,iCAAmB;AAAA,SACpD;AAAA,OACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;AE7FA,IAAAC,gBAAoE;;;ACApE,IAAAC,gBAMO;;;ACKA,IAAM,sBAAqC;AAAA,EAChD,UAAU;AAAA,EACV,KAAK;AAAA,EACL,MAAM;AAAA,EACN,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,UAAU;AACZ;;;ADoDQ,IAAAC,sBAAA;AArDD,SAAS,aAAiC;AAC/C,QAAM,EAAE,aAAa,mBAAmB,IAAI,iBAAiB;AAC7D,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,eAAW,sBAAyB,IAAI;AAC9C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,EAAE;AAEnC,+BAAU,MAAM;AACd,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AACb,QAAI,aAAa;AACf,cAAQ,YAAY,WAAW;AAC/B,UAAI,CAAC,OAAO,KAAM,QAAO,UAAU;AAEnC,qBAAe,MAAM;AACnB,cAAM,QAAQ,SAAS;AACvB,YAAI,OAAO;AACT,gBAAM,MAAM;AACZ,gBAAM,OAAO;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH,WAAW,OAAO,MAAM;AACtB,aAAO,MAAM;AAAA,IACf;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,MAAI,CAAC,YAAa,QAAO;AAEzB,QAAM,WAAW,CAAC,MAAiB;AACjC,MAAE,eAAe;AACjB,uBAAmB,KAAK,KAAK,CAAC;AAAA,EAChC;AAEA,QAAM,WAAW,MAAM;AAErB,uBAAmB,EAAE;AAAA,EACvB;AAEA,QAAM,WAAW,MAAM;AACrB,uBAAmB,IAAI;AAAA,EACzB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,MACT,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,iBAAS;AAAA,MACX;AAAA,MAEA,wDAAC,UAAK,UAAoB,WAAU,uBAClC;AAAA,sDAAC,SAAI,WAAU,sBACb;AAAA,uDAAC,WAAM,SAAQ,gBAAe,iBAAG;AAAA,UACjC;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,KAAK;AAAA,cACL,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,cACvC,aAAY;AAAA,cACZ,cAAa;AAAA;AAAA,UACf;AAAA,WACF;AAAA,QACA,8CAAC,SAAI,WAAU,0BACZ;AAAA,sBAAY,cACX,6CAAC,YAAO,MAAK,UAAS,SAAS,UAAU,WAAU,yBAAwB,0BAE3E,IAEA,6CAAC,UAAK;AAAA,UAER,8CAAC,SAAI,WAAU,0BACb;AAAA,yDAAC,YAAO,MAAK,UAAS,SAAS,UAAU,sBAEzC;AAAA,YACA,6CAAC,YAAO,MAAK,UAAS,WAAU,wBAAuB,qBAEvD;AAAA,aACF;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;;;AExGA,IAAAC,gBAA+E;AAC/E,IAAAC,eAQO;AA0GC,IAAAC,sBAAA;AA/FD,SAAS,mBAAuC;AACrD,QAAM,EAAE,mBAAmB,0BAA0B,gBAAgB,IAAI,iBAAiB;AAC1F,QAAM,gBAAY,sBAA0B,IAAI;AAIhD,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAqB,IAAI;AAC7D,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,GAAG;AAC1C,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,GAAG;AAC5C,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAuB,QAAQ;AACvE,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,IAAI;AACnD,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,wBAAS,IAAI;AACzD,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,IAAI;AACrD,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAS,IAAI;AAEvD,+BAAU,MAAM;AACd,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AACb,QAAI,mBAAmB;AACrB,0BAAoB,kBAAkB,SAAS;AAAA,QAC7C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,UAAI,CAAC,OAAO,KAAM,QAAO,UAAU;AAAA,IACrC,WAAW,OAAO,MAAM;AACtB,aAAO,MAAM;AAAA,IACf;AAAA,EACF,GAAG,CAAC,iBAAiB,CAAC;AAItB,+BAAU,MAAM;AACd,QAAI,eAAe,SAAU;AAC7B,UAAM,IAAI,0BAAa,UAAU;AACjC,eAAW,YAAQ,qBAAO,EAAE,KAAK,CAAC,CAAC;AACnC,gBAAY,YAAQ,qBAAO,EAAE,MAAM,CAAC,CAAC;AAAA,EACvC,GAAG,CAAC,UAAU,CAAC;AAGf,+BAAU,MAAM;AACd,QAAI,iBAAiB,SAAU;AAC/B,UAAM,KAAK,4BAAe,YAAY;AACtC,UAAM,KAAK,YAAQ,qBAAO,EAAE,CAAC;AAC7B,mBAAe,EAAE;AACjB,sBAAkB,EAAE;AACpB,oBAAgB,EAAE;AAClB,qBAAiB,EAAE;AAAA,EACrB,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,cAAc,eAAe;AAGnC,QAAM,gBAAY;AAAA,IAChB,OAAO;AAAA,MACL,OAAO,KAAK,UAAM,qBAAO,OAAO,CAAC;AAAA,MACjC,QAAQ,KAAK,UAAM,qBAAO,QAAQ,CAAC;AAAA,IACrC;AAAA,IACA,CAAC,SAAS,QAAQ;AAAA,EACpB;AAEA,MAAI,CAAC,kBAAmB,QAAO;AAE/B,QAAM,UAAU,CAAC,MAAiB;AAChC,MAAE,eAAe;AACjB,UAAM,OAA8B;AAAA,MAClC,WAAO,qBAAO,OAAO;AAAA,MACrB,YAAQ,qBAAO,QAAQ;AAAA,MACvB,eAAW,qBAAO,WAAW;AAAA,MAC7B,kBAAc,qBAAO,cAAc;AAAA,MACnC,gBAAY,qBAAO,YAAY;AAAA,MAC/B,iBAAa,qBAAO,aAAa;AAAA,IACnC;AACA,oBAAgB,IAAI;AACpB,6BAAyB,IAAI;AAAA,EAC/B;AACA,QAAM,WAAW,MAAM,yBAAyB,KAAK;AAErD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,MACT,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,iBAAS;AAAA,MACX;AAAA,MAEA,wDAAC,UAAK,UAAU,SAAS,WAAU,uBACjC;AAAA,qDAAC,QAAG,kCAAiB;AAAA,QAErB,8CAAC,cACC;AAAA,uDAAC,YAAO,qBAAO;AAAA,UACf,8CAAC,SAAI,WAAU,sBACb;AAAA,0DAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,eAAe;AAAA,kBACxB,UAAU,MAAM,cAAc,IAAI;AAAA;AAAA,cACpC;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,YACA,8CAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,eAAe;AAAA,kBACxB,UAAU,MAAM,cAAc,QAAQ;AAAA;AAAA,cACxC;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,YACA,8CAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,eAAe;AAAA,kBACxB,UAAU,MAAM,cAAc,QAAQ;AAAA;AAAA,cACxC;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,YACA,8CAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,eAAe;AAAA,kBACxB,UAAU,MAAM,cAAc,QAAQ;AAAA;AAAA,cACxC;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,aACF;AAAA,UACA,8CAAC,SAAI,WAAU,sBACb;AAAA,0DAAC,WAAM;AAAA;AAAA,cACQ;AAAA,cACb;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAM;AAAA,kBACN,KAAK;AAAA,kBACL,KAAK;AAAA,kBACL,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,WAAW,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,kBAClD,UAAU,CAAC;AAAA;AAAA,cACb;AAAA,eACF;AAAA,YACA,8CAAC,WAAM;AAAA;AAAA,cACO;AAAA,cACZ;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAM;AAAA,kBACN,KAAK;AAAA,kBACL,KAAK;AAAA,kBACL,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,YAAY,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,kBACnD,UAAU,CAAC;AAAA;AAAA,cACb;AAAA,eACF;AAAA,YACA,8CAAC,UAAK,WAAU,0BACb;AAAA,wBAAU;AAAA,cAAM;AAAA,cAAI,UAAU;AAAA,cAAO;AAAA,eACxC;AAAA,aACF;AAAA,WACF;AAAA,QAEA,8CAAC,cACC;AAAA,uDAAC,YAAO,qBAAO;AAAA,UACf,8CAAC,SAAI,WAAU,sBACb;AAAA,0DAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,iBAAiB;AAAA,kBAC1B,UAAU,MAAM,gBAAgB,QAAQ;AAAA;AAAA,cAC1C;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,YACA,8CAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,iBAAiB;AAAA,kBAC1B,UAAU,MAAM,gBAAgB,QAAQ;AAAA;AAAA,cAC1C;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,YACA,8CAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,iBAAiB;AAAA,kBAC1B,UAAU,MAAM,gBAAgB,MAAM;AAAA;AAAA,cACxC;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,YACA,8CAAC,WACC;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS,iBAAiB;AAAA,kBAC1B,UAAU,MAAM,gBAAgB,QAAQ;AAAA;AAAA,cAC1C;AAAA,cAAG;AAAA,cAAI;AAAA,eAET;AAAA,aACF;AAAA,UACA,8CAAC,SAAI,WAAU,sBACb;AAAA,0DAAC,WAAM;AAAA;AAAA,cACA;AAAA,cACL;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAM;AAAA,kBACN,KAAK;AAAA,kBACL,KAAK;AAAA,kBACL,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM;AACf,mCAAe,OAAO,EAAE,OAAO,KAAK,CAAC;AACrC,oCAAgB,QAAQ;AAAA,kBAC1B;AAAA;AAAA,cACF;AAAA,eACF;AAAA,YACA,8CAAC,WAAM;AAAA;AAAA,cACI;AAAA,cACT;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAM;AAAA,kBACN,KAAK;AAAA,kBACL,KAAK;AAAA,kBACL,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM;AACf,sCAAkB,OAAO,EAAE,OAAO,KAAK,CAAC;AACxC,oCAAgB,QAAQ;AAAA,kBAC1B;AAAA;AAAA,cACF;AAAA,eACF;AAAA,YACA,8CAAC,WAAM;AAAA;AAAA,cACI;AAAA,cACT;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAM;AAAA,kBACN,KAAK;AAAA,kBACL,KAAK;AAAA,kBACL,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM;AACf,oCAAgB,OAAO,EAAE,OAAO,KAAK,CAAC;AACtC,oCAAgB,QAAQ;AAAA,kBAC1B;AAAA;AAAA,cACF;AAAA,eACF;AAAA,YACA,8CAAC,WAAM;AAAA;AAAA,cACG;AAAA,cACR;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAM;AAAA,kBACN,KAAK;AAAA,kBACL,KAAK;AAAA,kBACL,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM;AACf,qCAAiB,OAAO,EAAE,OAAO,KAAK,CAAC;AACvC,oCAAgB,QAAQ;AAAA,kBAC1B;AAAA;AAAA,cACF;AAAA,eACF;AAAA,aACF;AAAA,WACF;AAAA,QAEA,8CAAC,SAAI,WAAU,0BACb;AAAA,uDAAC,YAAO,MAAK,UAAS,SAAS,UAAU,sBAEzC;AAAA,UACA,6CAAC,YAAO,MAAK,UAAS,WAAU,wBAAuB,qBAEvD;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,QAAQ,IAAoB;AACnC,SAAO,KAAK,MAAM,KAAK,EAAE,IAAI;AAC/B;AAEA,SAAS,oBACP,GACA,SAUM;AACN,QAAM,aAAS,2BAAa,EAAE,OAAO,EAAE,MAAM;AAC7C,UAAQ,cAAc,MAAM;AAC5B,UAAQ,WAAW,YAAQ,qBAAO,EAAE,KAAK,CAAC,CAAC;AAC3C,UAAQ,YAAY,YAAQ,qBAAO,EAAE,MAAM,CAAC,CAAC;AAC7C,QAAM,MAAM,YAAQ,qBAAO,EAAE,SAAS,CAAC;AACvC,QAAM,SAAS,YAAQ,qBAAO,EAAE,YAAY,CAAC;AAC7C,QAAM,OAAO,YAAQ,qBAAO,EAAE,UAAU,CAAC;AACzC,QAAM,QAAQ,YAAQ,qBAAO,EAAE,WAAW,CAAC;AAC3C,UAAQ,eAAe,GAAG;AAC1B,UAAQ,kBAAkB,MAAM;AAChC,UAAQ,gBAAgB,IAAI;AAC5B,UAAQ,iBAAiB,KAAK;AAC9B,UAAQ,gBAAgB,mBAAmB,CAAC,CAAC;AAC/C;AAEA,SAAS,mBAAmB,GAA+B;AACzD,QAAM,MAAM;AACZ,QAAM,WACJ,KAAK,IAAI,EAAE,YAAY,EAAE,YAAY,KAAK,OAC1C,KAAK,IAAI,EAAE,YAAY,EAAE,UAAU,KAAK,OACxC,KAAK,IAAI,EAAE,YAAY,EAAE,WAAW,KAAK;AAC3C,MAAI,CAAC,SAAU,QAAO;AACtB,aAAW,CAAC,MAAM,EAAE,KAAK,OAAO,QAAQ,2BAAc,GAAmD;AACvG,QAAI,KAAK,IAAI,EAAE,YAAY,EAAE,KAAK,IAAK,QAAO;AAAA,EAChD;AACA,SAAO;AACT;;;ACxVA,IAAAC,gBAAsE;AAgE9D,IAAAC,sBAAA;AAnDD,SAAS,qBAAyC;AACvD,QAAM,EAAE,qBAAqB,2BAA2B,IAAI,iBAAiB;AAC7E,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,eAAW,sBAAyB,IAAI;AAC9C,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,EAAE;AACzC,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,QAAQ;AAEzD,+BAAU,MAAM;AACd,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AACb,QAAI,qBAAqB;AACvB,iBAAW,oBAAoB,cAAc;AAC7C,eAAS,oBAAoB,YAAY;AACzC,UAAI,CAAC,OAAO,KAAM,QAAO,UAAU;AACnC,qBAAe,MAAM;AACnB,cAAM,QAAQ,SAAS;AACvB,YAAI,OAAO;AACT,gBAAM,MAAM;AACZ,gBAAM,OAAO;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH,WAAW,OAAO,MAAM;AACtB,aAAO,MAAM;AAAA,IACf;AAAA,EACF,GAAG,CAAC,mBAAmB,CAAC;AAExB,MAAI,CAAC,oBAAqB,QAAO;AAEjC,QAAM,WAAW,CAAC,MAAiB;AACjC,MAAE,eAAe;AACjB,+BAA2B,EAAE,SAAS,MAAM,CAAC;AAAA,EAC/C;AACA,QAAM,WAAW,MAAM,2BAA2B,EAAE,SAAS,IAAI,MAAM,CAAC;AACxE,QAAM,WAAW,MAAM,2BAA2B,IAAI;AAEtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MAKV,OAAO;AAAA,MACP,SAAS;AAAA,MACT,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,iBAAS;AAAA,MACX;AAAA,MAEA,wDAAC,UAAK,UAAoB,WAAU,uBAClC;AAAA,sDAAC,SAAI,WAAU,sBACb;AAAA,uDAAC,WAAM,SAAQ,oBAAmB,qBAAO;AAAA,UACzC;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,KAAK;AAAA,cACL,MAAK;AAAA,cACL,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA,cAC1C,aAAY;AAAA,cACZ,cAAa;AAAA;AAAA,UACf;AAAA,WACF;AAAA,QACA,8CAAC,SAAI,WAAU,sBACb;AAAA,uDAAC,UAAK,yBAAW;AAAA,UACjB,6CAAC,SAAI,WAAU,0BAAyB,MAAK,cAAa,cAAW,0BACjE,WAAC,QAAQ,UAAU,OAAO,EAAY,IAAI,CAAC,UAC3C,8CAAC,WAAkB,WAAU,2BAC3B;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL;AAAA,gBACA,SAAS,UAAU;AAAA,gBACnB,UAAU,MAAM,SAAS,KAAK;AAAA;AAAA,YAChC;AAAA,YACC,UAAU,SAAS,aAAa,UAAU,WAAW,WAAW;AAAA,eARvD,KASZ,CACD,GACH;AAAA,WACF;AAAA,QACA,8CAAC,SAAI,WAAU,0BACZ;AAAA,8BAAoB,iBACnB,6CAAC,YAAO,MAAK,UAAS,SAAS,UAAU,WAAU,yBAAwB,6BAE3E,IAEA,6CAAC,UAAK;AAAA,UAER,8CAAC,SAAI,WAAU,0BACb;AAAA,yDAAC,YAAO,MAAK,UAAS,SAAS,UAAU,sBAEzC;AAAA,YACA,6CAAC,YAAO,MAAK,UAAS,WAAU,wBAAuB,qBAEvD;AAAA,aACF;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;;;ACjHA,IAAAC,gBAAmF;;;AC0B5E,IAAM,SAAqC;AAAA,EAChD;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,MACL,EAAE,OAAO,gBAAU,SAAS,aAAa;AAAA,MACzC,EAAE,OAAO,YAAY,SAAS,MAAM;AAAA,MACpC,EAAE,OAAO,aAAU,SAAS,MAAM;AAAA,MAClC,EAAE,OAAO,QAAQ,SAAS,WAAW;AAAA,MACrC,EAAE,OAAO,mBAAgB,SAAS,eAAe;AAAA,MACjD,EAAE,OAAO,gBAAa,SAAS,cAAc;AAAA,MAC7C,EAAE,OAAO,iBAAc,SAAS,eAAe;AAAA,MAC/C,EAAE,OAAO,YAAY,SAAS,cAAc;AAAA,MAC5C,EAAE,OAAO,UAAU,SAAS,qBAAqB;AAAA,MACjD,EAAE,OAAO,YAAY,SAAS,iBAAiB;AAAA,MAC/C;AAAA,QACE,OAAO;AAAA,QACP,SAAS;AAAA,MACX;AAAA,MACA,EAAE,OAAO,WAAW,SAAS,uCAAuC;AAAA,MACpE,EAAE,OAAO,iBAAc,SAAS,oBAAoB;AAAA,MACpD,EAAE,OAAO,YAAY,SAAS,cAAc;AAAA,MAC5C,EAAE,OAAO,aAAU,SAAS,oBAAoB;AAAA,IAClD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,MACL,EAAE,OAAO,QAAK,SAAS,QAAQ,QAAQ,gBAAgB;AAAA,MACvD,EAAE,OAAO,QAAK,SAAS,WAAW,QAAQ,sBAAgB;AAAA,MAC1D,EAAE,OAAO,QAAK,SAAS,SAAS,QAAQ,aAAU;AAAA,MAClD,EAAE,OAAO,QAAK,SAAS,UAAU,QAAQ,8BAAwB;AAAA,MACjE,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,WAAW;AAAA,MACrD,EAAE,OAAO,QAAK,SAAS,WAAW,QAAQ,OAAO;AAAA,MACjD,EAAE,OAAO,KAAK,SAAS,OAAO,QAAQ,YAAY;AAAA,MAClD,EAAE,OAAO,KAAK,SAAS,OAAO,QAAQ,qBAAkB;AAAA,IAC1D;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,MACL,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,eAAe;AAAA,MACvD,EAAE,OAAO,UAAK,SAAS,YAAY,QAAQ,kBAAkB;AAAA,MAC7D,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,gBAAa;AAAA,MACvD,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,mBAAmB;AAAA,MAC3D,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,mBAAmB;AAAA,MAC3D,EAAE,OAAO,UAAK,SAAS,YAAY,QAAQ,iBAAiB;AAAA,MAC5D,EAAE,OAAO,UAAK,SAAS,UAAU,QAAQ,kBAAkB;AAAA,MAC3D,EAAE,OAAO,UAAK,SAAS,cAAc,QAAQ,aAAa;AAAA,MAC1D,EAAE,OAAO,UAAK,SAAS,UAAU,QAAQ,eAAe;AAAA,MACxD,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,eAAe;AAAA,IACzD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,MACL,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,OAAO;AAAA,MACjD,EAAE,OAAO,UAAK,SAAS,UAAU,QAAQ,OAAO;AAAA,MAChD,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,OAAO;AAAA,MACjD,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,QAAQ;AAAA,MAClD,EAAE,OAAO,UAAK,SAAS,gBAAgB,QAAQ,aAAU;AAAA,MACzD,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,OAAO;AAAA,MACjD,EAAE,OAAO,UAAK,SAAS,YAAY,QAAQ,SAAS;AAAA,MACpD,EAAE,OAAO,UAAK,SAAS,QAAQ,QAAQ,KAAK;AAAA,MAC5C,EAAE,OAAO,UAAK,SAAS,QAAQ,QAAQ,KAAK;AAAA,MAC5C,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,QAAK;AAAA,MAC7C,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,QAAQ;AAAA,MAClD,EAAE,OAAO,UAAK,SAAS,YAAY,QAAQ,KAAK;AAAA,MAChD,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,WAAQ;AAAA,MAClD,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,qBAAkB;AAAA,MAC5D;AAAA,QACE,OAAO;AAAA,QACP,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAAA,MACA,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,wBAAkB;AAAA,MAC5D,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,kBAAe;AAAA,IACzD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,MACL,EAAE,OAAO,UAAK,SAAS,QAAQ,QAAQ,aAAa;AAAA,MACpD,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,oBAAiB;AAAA,MAC3D,EAAE,OAAO,UAAK,SAAS,YAAY,QAAQ,aAAa;AAAA,MACxD,EAAE,OAAO,UAAK,SAAS,cAAc,QAAQ,qBAAqB;AAAA,MAClE,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,WAAQ;AAAA,MAChD,EAAE,OAAO,UAAK,SAAS,SAAS,QAAQ,mBAAa;AAAA,MACrD,EAAE,OAAO,UAAK,SAAS,gBAAgB,QAAQ,iBAAiB;AAAA,MAChE,EAAE,OAAO,UAAK,SAAS,eAAe,QAAQ,wBAAwB;AAAA,MACtE,EAAE,OAAO,UAAK,SAAS,eAAe,QAAQ,wBAAwB;AAAA,MACtE,EAAE,OAAO,UAAK,SAAS,eAAe,QAAQ,yBAAyB;AAAA,MACvE,EAAE,OAAO,UAAK,SAAS,eAAe,QAAQ,qBAAqB;AAAA,MACnE,EAAE,OAAO,UAAK,SAAS,eAAe,QAAQ,yBAAyB;AAAA,MACvE,EAAE,OAAO,UAAK,SAAS,YAAY,QAAQ,YAAY;AAAA,MACvD,EAAE,OAAO,UAAK,SAAS,YAAY,QAAQ,SAAS;AAAA,MACpD,EAAE,OAAO,OAAO,SAAS,YAAY,QAAQ,qBAAqB;AAAA,IACpE;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,MACL,EAAE,OAAO,OAAO,SAAS,uBAAuB,QAAQ,OAAO;AAAA,MAC/D,EAAE,OAAO,OAAO,SAAS,SAAS,QAAQ,UAAU;AAAA,MACpD,EAAE,OAAO,MAAM,SAAS,sBAAsB,QAAQ,WAAW;AAAA,MACjE,EAAE,OAAO,QAAQ,SAAS,wBAAwB,QAAQ,aAAa;AAAA,MACvE,EAAE,OAAO,OAAO,SAAS,YAAY,QAAQ,YAAY;AAAA,MACzD,EAAE,OAAO,MAAM,SAAS,QAAQ,QAAQ,oBAAoB;AAAA,MAC5D,EAAE,OAAO,OAAO,SAAS,SAAS,QAAQ,cAAc;AAAA,IAC1D;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,MACL,EAAE,OAAO,UAAK,SAAS,QAAQ,QAAQ,qBAAqB;AAAA,MAC5D,EAAE,OAAO,UAAK,SAAS,eAAe,QAAQ,2BAA2B;AAAA,MACzE,EAAE,OAAO,UAAK,SAAS,gBAAgB,QAAQ,aAAa;AAAA,MAC5D,EAAE,OAAO,UAAK,SAAS,oBAAoB,QAAQ,kBAAkB;AAAA,MACrE,EAAE,OAAO,UAAK,SAAS,uBAAuB,QAAQ,2BAAqB;AAAA,MAC3E,EAAE,OAAO,YAAY,SAAS,sBAAsB,QAAQ,+BAA+B;AAAA,MAC3F,EAAE,OAAO,SAAS,SAAS,WAAW,QAAQ,oBAAiB;AAAA,MAC/D,EAAE,OAAO,UAAK,SAAS,WAAW,QAAQ,YAAS;AAAA,MACnD,EAAE,OAAO,UAAK,SAAS,cAAc,QAAQ,eAAY;AAAA,IAC3D;AAAA,EACF;AACF;AA4BO,SAAS,aAAa,SAAyB;AACpD,SAAO,QAAQ,WAAW,MAAM,MAAM;AACxC;AA4BO,SAAS,WAAW,SAAyB;AAClD,SAAO,QAAQ,WAAW,MAAM,UAAK;AACvC;AAiBO,SAAS,gBACd,QACA,SACA,OAGuB;AACvB,MAAI,CAAC,OAAQ,QAAO,EAAE,UAAU,MAAM;AACtC,SAAO,EAAE,yBAAyB,EAAE,QAAQ,OAAO,WAAW,OAAO,CAAC,EAAE,EAAE;AAC5E;;;ACvPO,IAAM,cAAc;AAS3B,IAAM,cAAc;AASpB,IAAM,eAAe;AAoBd,SAAS,YAAY,OAAe,SAA2B;AACpE,MAAI,eAAe,OAAO,OAAO,MAAM,KAAM,QAAO;AACpD,SAAO,MAAM,KAAK,MAAM,MAAM,SAAS,OAAO;AAChD;AAEO,SAAS,eAAe,OAAe,SAAiC;AAC7E,MACE,MAAM,SAAS,WAAW,KAC1B,YAAY,KAAK,KAAK,KACtB,aAAa,KAAK,KAAK,GACvB;AACA,WAAO;AAAA,EACT;AACA,MAAI,WAAW,CAAC,QAAQ,GAAI,QAAO,QAAQ,SAAS;AACpD,SAAO;AACT;;;AF6LY,IAAAC,sBAAA;AA7NZ,IAAM,6BAA6B,oBAAI,IAAI,CAAC,UAAU,QAAQ,CAAC;AAUxD,SAAS,cAAc;AAAA,EAC5B;AACF,GAEuB;AACrB,QAAM,EAAE,gBAAgB,sBAAsB,IAAI,iBAAiB;AACnE,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,cAAU,sBAAuB,IAAI;AAC3C,QAAM,eAAW,sBAAgC,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAC5C,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,CAAC;AAE1C,+BAAU,MAAM;AACd,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AACb,QAAI,gBAAgB;AAClB,eAAS,eAAe,YAAY;AACpC,iBAAW,eAAe,cAAc;AACxC,kBAAY,CAAC;AACb,UAAI,CAAC,OAAO,KAAM,QAAO,UAAU;AAAA,IACrC,WAAW,OAAO,MAAM;AACtB,aAAO,MAAM;AAAA,IACf;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAInB,QAAM,kBAAc,sBAA0D,IAAI;AAGlF,QAAM,uBAAmB,sBAAuC,IAAI;AAIpE,QAAM,gBAAY,sBAA2C,IAAI;AACjE,QAAM,CAAC,SAAS,UAAU,QAAI,wBAA+B,IAAI;AACjE,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAS,KAAK;AAMlD,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,wBAAwB,IAAI;AAExE,+BAAU,MAAM;AACd,QAAI,CAAC,kBAAkB,YAAY,QAAS;AAC5C,kBAAc,IAAI;AAClB,sBAAkB,IAAI;AAItB,SAAK,QAAQ,IAAI,CAAC,OAAO,mBAAmB,GAAG,OAAO,UAAU,CAAC,CAAC,EAC/D,KAAK,CAAC,CAAC,MAAM,QAAQ,MAAM;AAC1B,kBAAY,UAAU,KAAK;AAC3B,uBAAiB,UAAU,SAAS;AACpC,gBAAU,UAAU,SAAS;AAK7B,eAAS,iBAAiB,kBAAkB;AAC5C,UAAI,gBAAgB;AAClB,iBAAS,iBAAiB,iBAAiB;AAAA,MAC7C;AACA,oBAAc,KAAK;AAAA,IACrB,CAAC,EACA,MAAM,MAAM;AAMX,oBAAc,KAAK;AACnB;AAAA,QACE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL,GAAG,CAAC,cAAc,CAAC;AAInB,+BAAU,MAAM;AACd,UAAM,SAAS,YAAY;AAC3B,QAAI,CAAC,UAAU,CAAC,MAAM,KAAK,GAAG;AAC5B,iBAAW,IAAI;AACf;AAAA,IACF;AACA,eAAW,OAAO,OAAO,OAAO,CAAC;AAAA,EACnC,GAAG,CAAC,OAAO,SAAS,UAAU,CAAC;AAO/B,QAAM,yBAAqB,2BAAY,CAAC,MAAa;AACnD,UAAM,KAAK;AACX,QAAI,CAAC,GAAG,QAAQ,CAAC,2BAA2B,IAAI,GAAG,IAAI,EAAG;AAC1D,OAAG,eAAe;AAClB,OAAG,gBAAgB;AACnB,aAAS,SAAS,OAAO,SAAS,EAAE,OAAO,KAAK,CAAC;AAAA,EACnD,GAAG,CAAC,CAAC;AAML,+BAAU,MAAM;AACd,UAAM,OAAO,iBAAiB;AAC9B,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAgB;AAEvC,UAAM,KAAK,IAAI,KAAK;AACpB,OAAG,YAAY;AACf,UAAM,UAAU,MAAM,SAAS,GAAG,SAAS,OAAO,CAAC;AACnD,OAAG,iBAAiB,SAAS,OAAO;AACpC,OAAG,iBAAiB,eAAe,oBAAoB,IAAI;AAiB3D,SAAK,YAAY,EAAE;AAInB,OAAG,4BAA4B;AAG/B,OAAG,kBAAkB;AAAA,MACnB,GAAG,GAAG;AAAA,MACN,KAAK;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAYN,KAAK,EAAE,OAAO,SAAS,OAAO,MAAM;AAAA,IACtC;AACA,OAAG,SAAS,eAAe,YAAY;AAMvC,aAAS,GAAG,SAAS,OAAO,CAAC;AAC7B,aAAS,UAAU;AACnB,mBAAe,MAAM,GAAG,MAAM,CAAC;AAE/B,WAAO,MAAM;AACX,SAAG,oBAAoB,SAAS,OAAO;AACvC,SAAG,oBAAoB,eAAe,oBAAoB,IAAI;AAC9D,SAAG,OAAO;AACV,eAAS,UAAU;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,gBAAgB,UAAU,CAAC;AAE/B,MAAI,CAAC,eAAgB,QAAO;AAE5B,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,eAAe,OAAO,OAAO;AAC5C,QAAM,eAAe,YAAY,OAAO,OAAO;AAE/C,QAAM,WAAW,CAAC,MAAiB;AACjC,MAAE,eAAe;AACjB,QAAI,CAAC,aAAc;AACnB,0BAAsB,EAAE,OAAO,QAAQ,CAAC;AAAA,EAC1C;AACA,QAAM,WAAW,MAAM,sBAAsB,IAAI;AAEjD,QAAM,WAAW,CAAC,YAAoB;AACpC,UAAM,KAAK,SAAS;AACpB,QAAI,CAAC,GAAI;AACT,OAAG,OAAO,aAAa,OAAO,GAAG,EAAE,OAAO,KAAK,CAAC;AAChD,aAAS,GAAG,SAAS,OAAO,CAAC;AAAA,EAC/B;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,MACT,UAAU,CAAC,MAAM;AACf,UAAE,eAAe;AACjB,iBAAS;AAAA,MACX;AAAA,MAEA,wDAAC,UAAK,UAAoB,WAAU,mBAClC;AAAA,qDAAC,SAAI,WAAU,mBAAkB,MAAK,WACnC,iBAAO,IAAI,CAAC,KAAK,MAChB;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,iBAAe,MAAM;AAAA,YACrB,WAAU;AAAA,YACV,SAAS,MAAM,YAAY,CAAC;AAAA,YAE3B,cAAI;AAAA;AAAA,UAPA,IAAI;AAAA,QAQX,CACD,GACH;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,MAAK;AAAA,YACL,OAAO,EAAE,qBAAqB,UAAU,OAAO,QAAQ,EAAE,OAAO,SAAS;AAAA,YAExE,iBAAO,QAAQ,EAAE,MAAM,IAAI,CAAC,MAC3B;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,OAAO,EAAE,UAAU,EAAE;AAAA,gBACrB,cAAY,EAAE,UAAU,EAAE;AAAA,gBAC1B,SAAS,MAAM,SAAS,EAAE,OAAO;AAAA,gBAOhC,GAAG,gBAAgB,QAAQ,EAAE,SAAS,EAAE,KAAK;AAAA;AAAA,cAZzC,EAAE;AAAA,YAaT,CACD;AAAA;AAAA,QACH;AAAA,QACA,6CAAC,SAAI,KAAK,SAAS,WAAU,mBAAkB;AAAA,QAC/C,8CAAC,WAAM,WAAU,sBACf;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,OAAO;AAAA;AAAA,UAC9C;AAAA,UAAE;AAAA,WAEJ;AAAA,QACA,6CAAC,SAAI,WAAU,sBAAqB,aAAU,UAC3C,uBACC,6CAAC,UAAK,WAAU,oBAAmB,uCAAoB,IACrD,iBACF,6CAAC,UAAK,WAAU,mBAAkB,MAAK,SACpC,0BACH,IACE,SACF,6CAAC,UAAK,WAAU,mBAAkB,MAAK,SACpC,kBACH,IACE,MACN;AAAA,QACA,8CAAC,SAAI,WAAU,oBACb;AAAA,uDAAC,YAAO,MAAK,UAAS,SAAS,UAAU,sBAEzC;AAAA,UACA,6CAAC,YAAO,MAAK,UAAS,UAAU,CAAC,cAAc,qBAE/C;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;;;AL7RI,IAAAC,sBAAA;AAnBJ,IAAM,oBAAgB,6BAAsC,IAAI;AAazD,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,GAAqC;AACnC,SACE,8CAAC,cAAc,UAAd,EAAuB,OAAO,QAC5B;AAAA;AAAA,IACD,6CAAC,cAAW;AAAA,IACZ,6CAAC,oBAAiB;AAAA,IAClB,6CAAC,sBAAmB;AAAA,IACpB,6CAAC,iBAAc,gBAAgB,wBAAwB;AAAA,KACzD;AAEJ;AAEO,SAAS,mBAAoC;AAClD,QAAM,UAAM,0BAAW,aAAa;AACpC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,SAAO;AACT;;;AQ1CA,IAAAC,gBASO;AAoMH,IAAAC,sBAAA;AAxJG,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgD;AAC9C,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAsB,IAAI;AAClD,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,KAAK;AAC9C,QAAM,aAAS,sBAAgC,IAAI;AAEnD,QAAM,kBAAc,2BAAY,MAA+B;AAC7D,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,YAAgC;AACpC,QAAI,aAAa,MAAM;AACrB,kBAAY,KAAK;AAAA,QACf,sBAAsB,UAAU,wBAAwB,SAAS;AAAA,MACnE;AAAA,IACF,OAAO;AACL,kBAAY,qBAAqB,MAAM,YAAY,MAAM;AAAA,IAC3D;AACA,QAAI,CAAC,UAAW,QAAO;AAEvB,UAAM,gBAAgB,UAAU,QAAQ,gBACpC,OAAO,SAAS,UAAU,QAAQ,eAAe,EAAE,KAAK,IACxD;AACJ,UAAM,cAAc,SAAS;AAC7B,WAAO,UAAU;AAAA,MACf,8CAA8C,WAAW;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,SAAS,YAAY,QAAQ,SAAS,CAAC;AAE3C,QAAM,cAAU,2BAAY,MAAM;AAChC,UAAM,MAAM,YAAY;AACxB,QAAI,CAAC,KAAK;AACR,cAAQ,CAAC,SAAU,SAAS,OAAO,OAAO,IAAK;AAC/C;AAAA,IACF;AACA,WAAO,UAAU;AACjB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,IAAI,IAAI,sBAAsB;AACpC,UAAM,KAAK,KAAK,sBAAsB;AACtC,UAAM,OAAa;AAAA,MACjB,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK;AAAA,MAC3B,MAAM,EAAE,OAAO,GAAG,OAAO,KAAK;AAAA,MAC9B,OAAO,EAAE;AAAA,MACT,QAAQ,EAAE;AAAA,IACZ;AACA;AAAA,MAAQ,CAAC,SACP,QACA,KAAK,QAAQ,KAAK,OAClB,KAAK,SAAS,KAAK,QACnB,KAAK,UAAU,KAAK,SACpB,KAAK,WAAW,KAAK,SACjB,OACA;AAAA,IACN;AAAA,EACF,GAAG,CAAC,aAAa,OAAO,CAAC;AAEzB,qCAAgB,MAAM;AACpB,YAAQ;AAUR,QAAI,YAAY;AAChB,QAAI,QAAQ;AACZ,QAAI,QAAQ;AACZ,UAAM,YAAY,MAAM;AACtB,UAAI,UAAW;AACf,cAAQ;AAER,UAAI,CAAC,YAAY,KAAK,UAAU,IAAI;AAClC,gBACE,OAAO,0BAA0B,aAC7B,sBAAsB,SAAS,KAC9B,eAAe,SAAS,GAAG;AAAA,MACpC;AAAA,IACF;AACA,YACE,OAAO,0BAA0B,aAC7B,sBAAsB,SAAS,KAC9B,eAAe,SAAS,GAAG;AAClC,WAAO,MAAM;AACX,kBAAY;AACZ,UAAI,OAAO,yBAAyB,cAAc,OAAO,UAAU,YAAY,QAAQ,GAAG;AACxF,6BAAqB,KAAK;AAAA,MAC5B;AAAA,IACF;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EACR,CAAC;AAED,+BAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,WAAW,MAAM,QAAQ;AAC/B,WAAO,iBAAiB,UAAU,QAAQ;AAC1C,SAAK,iBAAiB,UAAU,QAAQ;AACxC,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,QAAQ;AAC7C,WAAK,oBAAoB,UAAU,QAAQ;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,SAAS,OAAO,CAAC;AAcrB,+BAAU,MAAM;AACd,QAAI,OAAO,mBAAmB,YAAa;AAC3C,UAAM,MAAM,YAAY;AACxB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,OAAO,CAAC,KAAM;AACnB,UAAM,KAAK,IAAI,eAAe,MAAM,QAAQ,CAAC;AAC7C,OAAG,QAAQ,GAAG;AACd,OAAG,QAAQ,IAAI;AACf,WAAO,MAAM,GAAG,WAAW;AAAA,EAC7B,GAAG,CAAC,aAAa,SAAS,SAAS,MAAM,QAAQ,MAAM,OAAO,MAAM,GAAG,CAAC;AAExE,MAAI,CAAC,KAAM,QAAO;AAElB,QAAM,SAAS,MAAM,UAAU;AAC/B,QAAM,UAAU,WAAW,YAAY,iBAAiB;AAExD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,MAChF,iBAAiB;AAAA,MAEhB;AAAA;AAAA;AAAA;AAAA;AAAA,QAKC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK,MAAM;AAAA,YACX,KAAI;AAAA,YACJ,eAAW;AAAA,YACX,WAAW;AAAA,YACX,OAAO;AAAA,cACL,UAAU;AAAA,cACV,OAAO;AAAA,cACP,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,WAAW;AAAA,cACX,SAAS;AAAA,cACT,eAAe;AAAA,YACjB;AAAA;AAAA,QACF;AAAA,QAED,WACC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,cAAc,MAAM,WAAW;AAAA,YAC/B,cAAc,MAAM,WAAW;AAAA,YAC/B;AAAA,YACA,UAAU;AAAA,YACV,cAAc;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,aAAa,MAAM,YAAY,IAAI;AAAA,YACnC,WAAW,MAAM,YAAY,KAAK;AAAA;AAAA,QACpC;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,QAAO;AAAA,YACP,QAAQ,MAAM;AAAA,YACd,QAAQ,MAAM;AAAA,YACd;AAAA,YACA;AAAA,YACA,cAAc;AAAA;AAAA,QAChB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,QAAO;AAAA,YACP,QAAQ,MAAM;AAAA,YACd,QAAQ,MAAM;AAAA,YACd;AAAA,YACA;AAAA,YACA,cAAc;AAAA;AAAA,QAChB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,QAAO;AAAA,YACP,QAAQ,MAAM;AAAA,YACd,QAAQ,MAAM;AAAA,YACd;AAAA,YACA;AAAA,YACA,cAAc;AAAA;AAAA,QAChB;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,QAAO;AAAA,YACP,QAAQ,MAAM;AAAA,YACd,QAAQ,MAAM;AAAA,YACd;AAAA,YACA;AAAA,YACA,cAAc;AAAA;AAAA,QAChB;AAAA;AAAA;AAAA,EACF;AAEJ;AAuBA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,cAAU,sBAKN,IAAI;AAEd,QAAM,gBAAgB,CAAC,MAAyC;AAC9D,QAAI,EAAE,WAAW,EAAG;AACpB,QAAI,CAAC,OAAO,QAAS;AACrB,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,IAAC,EAAE,OAAuB,kBAAkB,EAAE,SAAS;AACvD,YAAQ,UAAU;AAAA,MAChB,QAAQ,EAAE;AAAA,MACV,QAAQ,EAAE;AAAA,MACV,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAMA,UAAM,SAAS,OAAO,QAAQ,QAAqB,YAAY,KAAK,OAAO;AAC3E,WAAO,MAAM,UAAU;AACvB,kBAAc;AAAA,EAChB;AAEA,QAAM,gBAAgB,CAAC,MAAyC;AAC9D,UAAM,IAAI,QAAQ;AAClB,QAAI,CAAC,KAAK,CAAC,OAAO,QAAS;AAC3B,UAAM,KAAK,EAAE,UAAU,EAAE;AACzB,UAAM,KAAK,EAAE,UAAU,EAAE;AAIzB,UAAM,SAAS,OAAO,QAAQ,QAAqB,YAAY,KAAK,OAAO;AAC3E,QAAI,MAAM;AACV,QAAI,MAAM;AACV,QAAI,WAAW,aAAa;AAE1B,YAAM,KAAK,IAAI,GAAG,eAAe,EAAE;AACnC,YAAM,KAAK,IAAI,GAAG,eAAe,EAAE;AACnC,aAAO,MAAM,cAAc,GAAG,GAAG;AACjC,aAAO,MAAM,YAAY,GAAG,GAAG;AAAA,IACjC,WAAW,WAAW,cAAc;AAElC,YAAM,KAAK,IAAI,GAAG,eAAe,EAAE;AACnC,YAAM,KAAK,IAAI,GAAG,eAAe,EAAE;AACnC,aAAO,MAAM,aAAa,GAAG,GAAG;AAChC,aAAO,MAAM,YAAY,GAAG,GAAG;AAAA,IACjC,WAAW,WAAW,YAAY,WAAW,SAAS;AACpD,YAAM,eAAe;AACrB,YAAM,eAAe;AACrB,aAAO,MAAM,OAAO,GAAG,GAAG;AAC1B,aAAO,MAAM,MAAM,GAAG,GAAG;AAAA,IAC3B;AACA,MAAE,SAAS;AACX,MAAE,SAAS;AACX,iBAAa;AAAA,EACf;AAMA,QAAM,wBAAwB,CAAC,MAAmB,OAAuB;AACvE,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,OAAO,KAAK,QAAqB,UAAU;AACjD,UAAM,UAAU,MAAM,cAA2B,kBAAkB;AACnE,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,WAAW,KAAK,sBAAsB;AAC5C,UAAM,cAAc,QAAQ,sBAAsB;AAClD,UAAM,OAAO,IAAI,sBAAsB,EAAE;AAEzC,UAAM,cAAc,YAAY,MAAM,SAAS;AAC/C,UAAM,cAAc,YAAY,SAAS,SAAS,MAAM;AACxD,WAAO,KAAK,MAAM,KAAK,IAAI,aAAa,KAAK,IAAI,IAAI,WAAW,CAAC,CAAC;AAAA,EACpE;AAEA,QAAM,gBAAgB,CAAC,IAAY,OAAe;AAChD,UAAM,MAAM,OAAO;AACnB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,OAAO,CAAC,QAAS,WAAW,YAAY,WAAW,SAAU;AAChE,eAAS,IAAI,EAAE;AACf;AAAA,IACF;AACA,UAAM,OAAO,qBAAqB,MAAM,YAAY,MAAM;AAC1D,QAAI,CAAC,MAAM;AACT,eAAS,IAAI,EAAE;AACf;AAAA,IACF;AACA,aAAS,IAAI,sBAAsB,MAAM,EAAE,CAAC;AAAA,EAC9C;AAEA,QAAM,SAAS,CAAC,MAAyC;AACvD,UAAM,IAAI,QAAQ;AAClB,QAAI,CAAC,EAAG;AACR,YAAQ,UAAU;AAClB,QAAI;AACF,MAAC,EAAE,OAAuB,sBAAsB,EAAE,SAAS;AAAA,IAC7D,QAAQ;AAAA,IAER;AAKA,QAAI,OAAO,SAAS;AAClB,YAAM,SAAS,OAAO,QAAQ,QAAqB,YAAY,KAAK,OAAO;AAC3E,aAAO,MAAM,UAAU;AAAA,IACzB;AACA,gBAAY;AAEZ,UAAM,MAAM,OAAO;AACnB,UAAM,OAAO,QAAQ;AACrB,UAAM,SAAS,KAAK,MAAM,EAAE,MAAM;AAClC,UAAM,SAAS,KAAK,MAAM,EAAE,MAAM;AAKlC,QAAI,CAAC,OAAO,CAAC,QAAQ,eAAe,UAAa,aAAa,MAAM;AAClE,oBAAc,QAAQ,MAAM;AAC5B;AAAA,IACF;AAEA,UAAM,UAAU,IAAI,sBAAsB;AAC1C,UAAM,UAAU,QAAQ,MAAM,QAAQ,SAAS;AAQ/C,UAAM,QAAQ,MAAM,KAAK,KAAK,iBAA8B,UAAU,CAAC;AACvE,QAAI,WAA+B;AACnC,QAAI,eAAe;AACnB,eAAW,MAAM,OAAO;AACtB,YAAM,IAAI,GAAG,sBAAsB;AACnC,UAAI,WAAW,EAAE,OAAO,WAAW,EAAE,QAAQ;AAC3C,mBAAW;AACX;AAAA,MACF;AACA,YAAM,OAAO,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,UAAU,EAAE;AAC7D,UAAI,OAAO,cAAc;AACvB,uBAAe;AACf,mBAAW;AAAA,MACb;AAAA,IACF;AAGA,UAAM,cAAc,qBAAqB,MAAM,YAAY,MAAM;AACjE,UAAM,cAAc,aAAa,QAAqB,UAAU,KAAK;AAGrE,QAAI,YAAY,QAAQ,aAAa,aAAa;AAChD,oBAAc,QAAQ,MAAM;AAC5B;AAAA,IACF;AAKA,UAAM,eAAe,MAAM;AAAA,MACzB,SAAS,iBAA8B,oBAAoB;AAAA,IAC7D;AACA,QAAI,WAA+B;AACnC,QAAI,gBAAgB;AACpB,eAAW,KAAK,cAAc;AAC5B,YAAM,OAAO,KAAK,IAAI,EAAE,sBAAsB,EAAE,MAAM,QAAQ,GAAG;AACjE,UAAI,OAAO,eAAe;AACxB,wBAAgB;AAChB,mBAAW;AAAA,MACb;AAAA,IACF;AAGA,QAAI,CAAC,UAAU;AACb,oBAAc,QAAQ,MAAM;AAC5B;AAAA,IACF;AAEA,UAAM,iBAAiB,eAAe,UAAU,IAAI;AACpD,QAAI,kBAAkB,MAAM;AAC1B,oBAAc,QAAQ,MAAM;AAC5B;AAAA,IACF;AAIA,UAAM,aAAa,SAAS,QAAQ,gBAChC,OAAO,SAAS,SAAS,QAAQ,eAAe,EAAE,KAAK,IACvD;AAMJ,UAAM,WAAW,SAAS,sBAAsB;AAChD,UAAM,aAAa,KAAK,MAAM,QAAQ,OAAO,SAAS,IAAI;AAC1D,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,KAAK,MAAM,QAAQ,MAAM,SAAS,GAAG;AAAA,IACvC;AAEA;AAAA,MACE,EAAE,YAAY,gBAAgB,QAAQ,YAAY,WAAW,OAAU;AAAA,MACvE;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,iBAAiB;AAAA;AAAA,EACnB;AAEJ;AAWA,IAAM,QAAQ;AAEd,SAAS,OAAO;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,cAAU,sBAQN,IAAI;AAEd,QAAM,gBAAgB,CAAC,MAAyC;AAC9D,QAAI,EAAE,WAAW,EAAG;AACpB,QAAI,CAAC,OAAO,QAAS;AACrB,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,IAAC,EAAE,OAAuB,kBAAkB,EAAE,SAAS;AACvD,UAAM,SAAS,SAAS,KAAK,SAAS,IAAI,SAAS,SAAS;AAC5D,YAAQ,UAAU;AAAA,MAChB,QAAQ,EAAE;AAAA,MACV,QAAQ,EAAE;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,MAAyC;AAC9D,UAAM,IAAI,QAAQ;AAClB,QAAI,CAAC,KAAK,CAAC,OAAO,QAAS;AAC3B,UAAM,QAAQ,EAAE,UAAU,EAAE;AAE5B,UAAM,KAAK,WAAW,QAAQ,WAAW,OAAO,QAAQ,CAAC;AACzD,QAAI,IAAI,KAAK,IAAI,OAAO,EAAE,SAAS,EAAE;AACrC,QAAI,IAAI,IAAI,EAAE;AAEd,WAAO,QAAQ,MAAM,QAAQ,GAAG,CAAC;AACjC,WAAO,QAAQ,MAAM,SAAS,GAAG,CAAC;AAClC,WAAO,QAAQ,QAAQ,KAAK,MAAM,CAAC;AACnC,WAAO,QAAQ,SAAS,KAAK,MAAM,CAAC;AACpC,MAAE,QAAQ;AACV,MAAE,QAAQ;AACV,iBAAa;AAAA,EACf;AAEA,QAAM,SAAS,CAAC,MAAyC;AACvD,UAAM,IAAI,QAAQ;AAClB,QAAI,CAAC,EAAG;AACR,YAAQ,UAAU;AAClB,QAAI;AACF,MAAC,EAAE,OAAuB,sBAAsB,EAAE,SAAS;AAAA,IAC7D,QAAQ;AAAA,IAER;AACA,aAAS,KAAK,MAAM,EAAE,KAAK,GAAG,KAAK,MAAM,EAAE,KAAK,CAAC;AAAA,EACnD;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,mBAAmB,MAAM;AAAA,MACpC;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,iBAAiB;AAAA;AAAA,EACnB;AAEJ;;;ACznBA,IAAAC,gBAQO;AACP,IAAAC,eAIO;AA+LC,IAAAC,sBAAA;AAxJR,IAAM,gBAAgB;AAQf,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAkD;AAChD,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAA2B,CAAC,CAAC;AACvD,QAAM,aAAS,sBAAsB,IAAI;AAEzC,QAAM,cAAU,2BAAY,MAAM;AAChC,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,MAAM;AACT,eAAS,CAAC,SAAU,KAAK,WAAW,IAAI,OAAO,CAAC,CAAE;AAClD;AAAA,IACF;AACA,UAAM,KAAK,KAAK,sBAAsB;AACtC,UAAM,MAAwB,CAAC;AAE/B,cAAU,UAAU,EAAE,QAAQ,CAAC,OAAO,aAAa;AACjD,UAAI,aAAa,UAAU,SAAU;AACrC,YAAM,IAAI;AAIV,UAAI,CAAC,EAAE,OAAQ;AACf,YAAM,UAAM,8BAAgB,KAAK,EAAE,MAAM;AACzC,UAAI,CAAC,IAAK;AAEV,YAAM,QAAQ,EAAE,MAAM,SAAS;AAC/B,YAAM,OAAO,EAAE,MAAM,QAAQ;AAE7B,YAAM,QAAQ,YAAY,MAAM,IAAI,KAAK;AACzC,UAAI,QAAiC;AACrC,UAAI,OAAO;AACT,YAAI;AACF,gBAAM,QAAQ,SAAS,YAAY;AACnC,gBAAM,SAAS,MAAM,MAAM,MAAM,MAAM;AACvC,gBAAM,SAAS,IAAI;AACnB,cAAI,OAAO,MAAM,sBAAsB;AACvC,cAAI,KAAK,WAAW,KAAK,MAAM,KAAK,aAAa,KAAK,cAAc;AAClE,mBAAQ,MAAM,KAAqB,sBAAsB;AAAA,UAC3D;AACA,kBAAQ;AAAA,YACN,KAAK,KAAK,MAAM,GAAG,MAAM,KAAK;AAAA,YAC9B,MAAM,KAAK,OAAO,GAAG,OAAO,KAAK;AAAA,YACjC,QAAQ,KAAK,UAAU;AAAA,UACzB;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAEA,YAAM,YACJ,IAAI,OAAO,eAAe,IAAI,MAAM,eACnC,IAAI,OAAO,aAAa,SAAS,IAAI,MAAM,aAAa,OACzD,IAAI,OAAO,WAAW,IAAI,MAAM;AAElC,YAAM,YAAoB,CAAC;AAC3B,UAAI,CAAC,aAAa,OAAO;AACvB,cAAM,SAAS,YAAY,MAAM,IAAI,MAAM;AAC3C,YAAI,QAAQ;AACV,cAAI;AACF,kBAAM,IAAI,SAAS,YAAY;AAC/B,cAAE,SAAS,OAAO,MAAM,OAAO,MAAM;AACrC,cAAE,OAAO,MAAM,MAAM,MAAM,MAAM;AACjC,gBAAI,EAAE,WAAW;AACf,gBAAE,SAAS,MAAM,MAAM,MAAM,MAAM;AACnC,gBAAE,OAAO,OAAO,MAAM,OAAO,MAAM;AAAA,YACrC;AACA,uBAAW,MAAM,MAAM,KAAK,EAAE,eAAe,CAAC,GAAG;AAC/C,kBAAI,GAAG,UAAU,KAAK,GAAG,WAAW,EAAG;AACvC,wBAAU,KAAK;AAAA,gBACb,KAAK,GAAG,MAAM,GAAG,MAAM,KAAK;AAAA,gBAC5B,MAAM,GAAG,OAAO,GAAG,OAAO,KAAK;AAAA,gBAC/B,OAAO,GAAG;AAAA,gBACV,QAAQ,GAAG;AAAA,cACb,CAAC;AAAA,YACH;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAEA,UAAI,KAAK,EAAE,UAAU,MAAM,OAAO,OAAO,UAAU,CAAC;AAAA,IACtD,CAAC;AAED,aAAS,GAAG;AAAA,EACd,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC;AAE5B,QAAM,sBAAkB,2BAAY,MAAM;AACxC,QAAI,OAAO,WAAW,KAAM;AAC5B,UAAM,MACJ,OAAO,0BAA0B,aAC7B,wBACA,CAAC,OAA6B,WAAW,MAAM,GAAG,CAAC,GAAG,EAAE;AAC9D,WAAO,UAAU,IAAI,MAAM;AACzB,aAAO,UAAU;AACjB,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,CAAC;AAGZ,+BAAU,MAAM;AACd,UAAM,WAAW,MAAM,gBAAgB;AACvC,cAAU,GAAG,UAAU,QAAQ;AAC/B,oBAAgB;AAChB,WAAO,MAAM,UAAU,IAAI,UAAU,QAAQ;AAAA,EAC/C,GAAG,CAAC,WAAW,eAAe,CAAC;AAG/B,+BAAU,MAAM;AACd,oBAAgB;AAAA,EAClB,GAAG,CAAC,UAAU,eAAe,CAAC;AAG9B,+BAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,WAAW,MAAM,gBAAgB;AACvC,SAAK,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AAC3D,QAAI,KAA4B;AAChC,QAAI,OAAO,mBAAmB,aAAa;AACzC,WAAK,IAAI,eAAe,MAAM,gBAAgB,CAAC;AAC/C,SAAG,QAAQ,IAAI;AAAA,IACjB;AACA,WAAO,MAAM;AACX,WAAK,oBAAoB,UAAU,QAAQ;AAC3C,UAAI,WAAW;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,SAAS,eAAe,CAAC;AAE7B,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,QAAM,QAAuB;AAAA,IAC3B,UAAU;AAAA,IACV,OAAO;AAAA,IACP,eAAe;AAAA,IACf,QAAQ;AAAA,EACV;AAEA,SACE,6CAAC,SAAI,WAAU,qBAAoB,eAAW,MAAC,OAAO,OACnD,gBAAM,IAAI,CAAC,MACV,8CAAC,SACE;AAAA,MAAE,UAAU,IAAI,CAAC,GAAG,MACnB;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO;AAAA,UACL,UAAU;AAAA,UACV,KAAK,EAAE;AAAA,UACP,MAAM,EAAE;AAAA,UACR,OAAO,EAAE;AAAA,UACT,QAAQ,EAAE;AAAA,UACV,YAAY,EAAE;AAAA,UACd,SAAS;AAAA,UACT,cAAc;AAAA,QAChB;AAAA;AAAA,MAVK;AAAA,IAWP,CACD;AAAA,IACA,EAAE,SACD;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,UACL,UAAU;AAAA,UACV,KAAK,EAAE,MAAM;AAAA,UACb,MAAM,EAAE,MAAM;AAAA,UACd,QAAQ,EAAE,MAAM;AAAA,UAChB,OAAO;AAAA,UACP,YAAY,EAAE;AAAA,QAChB;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,KAAK;AAAA,cACL,MAAM;AAAA,cACN,UAAU;AAAA,cACV,YAAY;AAAA,cACZ,SAAS;AAAA,cACT,cAAc;AAAA,cACd,OAAO;AAAA,cACP,YAAY,EAAE;AAAA,cACd,YAAY;AAAA,cACZ,YAAY;AAAA,cACZ,YAAY;AAAA,YACd;AAAA,YAEC,YAAE;AAAA;AAAA,QACL;AAAA;AAAA,IACF;AAAA,OA7CM,EAAE,QA+CZ,CACD,GACH;AAEJ;;;AC/PA,IAAAC,iBAA8E;AAC9E,IAAAC,eASO;;;ACVP,IAAAC,gBAAmE;AACnE,IAAAC,eAOO;AAmBI,IAAAC,uBAAA;AAPJ,SAAS,aAAa,OAAkB,WAAuC;AACpF,MACE,MAAM,WAAW,KAChB,MAAM,WAAW,KAChB,OAAO,MAAM,CAAC,EAAE,WAAW,YAC3B,MAAM,CAAC,EAAE,WAAW,IACtB;AACA,WAAO,8CAAC,QAAG,cAAW,QAAO;AAAA,EAC/B;AACA,MAAI,SAAS;AAOb,QAAM,cAA2B,CAAC;AAClC,QAAM,OAAoB,CAAC;AAC3B,aAAW,MAAM,OAAO;AACtB,QAAI,OAAO,GAAG,WAAW,UAAU;AACjC,YAAM,OAAO,GAAG;AAChB,UAAI,KAAK,WAAW,EAAG;AACvB,YAAM,MAAM,GAAG,SAAS,IAAI,MAAM;AAClC,WAAK,KAAK,8CAAC,0BAAoB,eAAK,MAAM,GAAG,UAAU,KAA9B,GAAgC,CAAW;AACpE,gBAAU,KAAK;AAAA,IACjB,eAAW,2BAAa,GAAG,MAAM,GAAG;AAClC,YAAM,MAAM,GAAG,SAAS,IAAI,MAAM;AAClC,YAAM,SAAS,GAAG,OAAO,UAAU;AAKnC,YAAM,gBAAgB,GAAG,YAAY,SAAS;AAC9C,UAAI,WAAW,eAAe,WAAW,cAAc;AACrD,oBAAY;AAAA,UACV,8CAAC,kBAAqC,OAAO,GAAG,QAAQ,QAAgB,YAAU,QAA7D,GAAG,GAAG,SAAwD;AAAA,QACrF;AACA,aAAK;AAAA,UACH;AAAA,YAAC;AAAA;AAAA,cAEC,sBAAmB;AAAA,cACnB,qBAAmB;AAAA,cACnB,iBAAiB;AAAA,cACjB,OAAO;AAAA;AAAA,YAJF,GAAG,GAAG;AAAA,UAKb;AAAA,QACF;AAAA,MACF,OAAO;AACL,cAAM,UAAU,8CAAC,kBAAyB,OAAO,GAAG,QAAQ,UAAvB,GAAuC;AAC5E,YAAI,eAAe;AACjB,eAAK;AAAA,YACH,8CAAC,UAAwB,mBAAiB,eAAe,WAAU,cAChE,qBADQ,GAAG,GAAG,MAEjB;AAAA,UACF;AAAA,QACF,OAAO;AACL,eAAK,KAAK,OAAO;AAAA,QACnB;AAAA,MACF;AACA,gBAAU;AAAA,IACZ,WAAW,GAAG,UAAU,MAAM;AAE5B,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,YAAY,WAAW,KAAK,KAAK,WAAW,EAAG,QAAO,8CAAC,QAAG,cAAW,QAAO;AAChF,SAAO,CAAC,GAAG,aAAa,GAAG,IAAI;AACjC;AAEA,IAAM,gBAA+B;AAAA,EACnC,SAAS;AAAA,EACT,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,eAAe;AACjB;AAOA,IAAM,aAA4B;AAAA,EAChC,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,QAAQ;AACV;AAiBA,SAAS,qBAAqB,GAAwB,GAAiC;AACrF,MAAI,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,WAAY,QAAO;AACnE,QAAM,IAAI,EAAE;AACZ,QAAM,IAAI,EAAE;AACZ,MAAI,MAAM,EAAG,QAAO;AACpB,SACE,EAAE,UAAU,EAAE,SACd,EAAE,WAAW,EAAE,UACf,EAAE,WAAW,EAAE,UACf,EAAE,UAAU,EAAE,SACd,EAAE,YAAY,EAAE,WAChB,EAAE,YAAY,EAAE,WAChB,EAAE,YAAY,EAAE,WAChB,EAAE,iBAAiB,EAAE;AAAA;AAAA;AAAA,EAIrB,EAAE,QAAQ,EAAE;AAEhB;AAEA,IAAM,qBAAiB,oBAAK,oBAAoB,oBAAoB;AAEpE,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,SAAS,MAAM,UAAU;AAC/B,QAAM,QAAQ,MAAM;AACpB,QAAM,KAAK,MAAM,WAAW;AAC5B,QAAM,KAAK,MAAM,WAAW;AAC5B,QAAM,aAAa,CAAC,CAAC,MAAM,WAAW,MAAM,QAAQ,SAAS;AAO7D,MAAI;AACJ,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,OAAO,MAAM;AAAA,QACb,aAAa,KAAK,IAAI,GAAG,EAAE;AAAA,QAC3B,WAAW,KAAK,IAAI,GAAG,EAAE;AAAA,QACzB,cAAc;AAAA,MAChB;AACA;AAAA,IACF,KAAK;AACH,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,OAAO,MAAM;AAAA,QACb,YAAY,KAAK,IAAI,GAAG,EAAE;AAAA,QAC1B,WAAW,KAAK,IAAI,GAAG,EAAE;AAAA,QACzB,cAAc;AAAA,MAChB;AACA;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,mBAAa;AAAA,QACX,UAAU;AAAA,QACV,MAAM;AAAA,QACN,KAAK;AAAA,QACL,OAAO,MAAM;AAAA,QACb,QAAQ,WAAW,WAAW,KAAK;AAAA,MACrC;AACA;AAAA,IACF,KAAK;AAAA,IACL;AACE,mBAAa,QACT;AAAA,QACE,SAAS;AAAA,QACT,OAAO,MAAM;AAAA,QACb,YAAY,UAAU,SAAS,IAAI;AAAA,QACnC,aAAa,UAAU,UAAU,IAAI;AAAA,MACvC,IACA;AAAA,QACE,SAAS;AAAA;AAAA;AAAA,QAGT,eAAe,MAAM,SAAS,UAAU;AAAA,QACxC,OAAO,MAAM;AAAA,MACf;AACJ;AAAA,EACJ;AAWA,QAAM,WAA0B;AAAA,IAC9B,OAAO,MAAM;AAAA,IACb,QAAQ,MAAM;AAAA,IACd,SAAS;AAAA,EACX;AAMA,QAAM,MACJ;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,MAAM;AAAA,MACX,OAAO,MAAM;AAAA,MACb,QAAQ,MAAM;AAAA,MACd,iBAAiB;AAAA,MACjB,WAAW;AAAA,MACX,UAAS;AAAA,MACT,SAAQ;AAAA,MACR,cAAW;AAAA,MACX,qBAAmB;AAAA,MACnB,oBAAkB,SAAS;AAAA,MAC3B,qBAAmB;AAAA,MACnB,oBAAkB,aAAa,SAAS;AAAA,MACxC,OAAO;AAAA,MACP,KAAI;AAAA;AAAA,EACN;AAMF,QAAM,eAAe,MAAM,gBAAgB;AAC3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,iBAAiB;AAAA,MACjB,qBAAmB,aAAa,SAAS;AAAA,MACzC,qBAAmB;AAAA,MACnB,OAAO;AAAA,MAEN;AAAA;AAAA,QACA,cACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,sBAAoB;AAAA,YACpB,OAAO,EAAE,WAAW,aAAa;AAAA,YAEhC,gBAAM;AAAA;AAAA,QACT;AAAA;AAAA;AAAA,EAEJ;AAEJ;AASA,SAAS,eAAe,MAAyB;AAC/C,QAAM,WAAO,kCAAoB,IAAI;AAErC,MAAI,KAAK,WAAW,KAAK,CAAC,KAAK,CAAC,EAAE,MAAO,QAAO;AAChD,SAAO,KAAK;AAAA,IAAI,CAAC,GAAG,MAClB,EAAE,QACA,8CAAC,UAAa,cAAW,QAAO,OAAO,0BACpC,YAAE,QADM,CAEX,IAEA,8CAAC,0BAAkB,YAAE,QAAN,CAAW;AAAA,EAE9B;AACF;AAEA,SAAS,KAAK,MAAc,OAA8B;AAExD,MAAI,OAAkB,eAAe,IAAI;AACzC,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,MAAM,KAAM,QAAO,8CAAC,YAAQ,gBAAK;AACrC,MAAI,MAAM,OAAQ,QAAO,8CAAC,QAAI,gBAAK;AACnC,MAAI,MAAM,UAAW,QAAO,8CAAC,OAAG,gBAAK;AACrC,MAAI,MAAM,OAAQ,QAAO,8CAAC,OAAG,gBAAK;AAElC,QAAM,aAA4B,CAAC;AACnC,MAAI,MAAM,MAAO,YAAW,QAAQ,MAAM;AAC1C,MAAI,MAAM,UAAW,YAAW,kBAAkB,MAAM;AACxD,MAAI,MAAM,WAAY,YAAW,aAAa,MAAM;AACpD,MAAI,MAAM,SAAU,YAAW,WAAW,MAAM;AAChD,MAAI,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;AACtC,WAAO,8CAAC,UAAK,OAAO,YAAa,gBAAK;AAAA,EACxC;AAEA,MAAI,MAAM,MAAM,MAAM;AACpB,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,MAAM,KAAK;AAAA,QACjB,OAAO,MAAM,KAAK;AAAA,QAClB,KAAI;AAAA,QACJ,QAAO;AAAA,QACP,OAAO;AAAA,QACP,SAAS,CAAC,MAAM;AAOd,YAAE,eAAe;AACjB,cAAI,gBAAgB,CAAC,KAAK,MAAM,MAAM,MAAM;AAC1C,mBAAO,KAAK,MAAM,KAAK,MAAM,UAAU,qBAAqB;AAAA,UAC9D;AAAA,QACF;AAAA,QAEC;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,MAAI,MAAM,SAAS,QAAQ;AACzB,WACE,8CAAC,UAAK,mBAAiB,MAAM,QAAQ,QAAQ,WAAU,cACpD,gBACH;AAAA,EAEJ;AAEA,SAAO;AACT;;;AC3VO,SAAS,YAAY,OAA0B;AACpD,MAAI,MAAM;AACV,aAAW,MAAM,OAAO;AACtB,QAAI,OAAO,GAAG,WAAW,SAAU,QAAO,GAAG,OAAO;AAAA,aAC3C,GAAG,UAAU,KAAM,QAAO;AAAA,EACrC;AACA,SAAO;AACT;AAOO,SAAS,WAAW,OAAkB,OAAe,KAAwB;AAClF,MAAI,OAAO,MAAO,QAAO,CAAC;AAC1B,QAAM,MAAiB,CAAC;AACxB,MAAI,SAAS;AACb,aAAW,MAAM,OAAO;AACtB,QAAI,OAAO,GAAG,WAAW,UAAU;AACjC,YAAM,UAAU;AAChB,YAAM,QAAQ,SAAS,GAAG,OAAO;AACjC,eAAS;AACT,UAAI,SAAS,MAAO;AACpB,UAAI,WAAW,IAAK;AACpB,YAAM,YAAY,KAAK,IAAI,SAAS,KAAK,IAAI;AAC7C,YAAM,UAAU,KAAK,IAAI,OAAO,GAAG,IAAI;AACvC,UAAI,KAAK;AAAA,QACP,QAAQ,GAAG,OAAO,MAAM,WAAW,OAAO;AAAA,QAC1C,YAAY,GAAG;AAAA,MACjB,CAAC;AAAA,IACH,WAAW,GAAG,UAAU,MAAM;AAC5B,YAAM,UAAU;AAChB,YAAM,QAAQ,SAAS;AACvB,eAAS;AACT,UAAI,SAAS,MAAO;AACpB,UAAI,WAAW,IAAK;AACpB,UAAI,KAAK,EAAE,QAAQ,GAAG,QAAQ,YAAY,GAAG,WAAW,CAAC;AAAA,IAC3D;AAAA,EACF;AACA,SAAO;AACT;;;AC5CA,IAAAC,iBASO;AACP,IAAAC,eAAiD;AAyX7C,IAAAC,uBAAA;AAxWG,SAAS,kBAAkB,MAAc,iBAAiC;AAC/E,MAAI,EAAE,kBAAkB,GAAI,QAAO;AACnC,SAAQ,OAAO,kBAAmB;AACpC;AAaO,SAAS,iBAAiB,MAAgB,SAA2B;AAC1E,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,QAAQ,UAAU,KAAK;AAC7B,SAAO,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,2BAAc,KAAK,MAAM,IAAI,KAAK,CAAC,CAAC;AACtE;AA0DO,SAAS,mBAAmB,EAAE,UAAU,YAAY,MAAM,KAAK,GAA8B;AAClG,QAAM,SAAS,iBAAiB;AAIhC,QAAM,CAAC,QAAQ,SAAS,QAAI,yBAKlB,IAAI;AACd,QAAM,iBAAa,uBAAuB,IAAI;AAc9C,QAAM,iBAAa,4BAAY,MAAqB;AAClD,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,YAAY,MAAM,sBAAsB;AAE9C,UAAM,SAAS,MAAM,iBAAsC,gBAAgB;AAC3E,QAAI,SAAmB,CAAC;AACxB,QAAI,OAAO,WAAW,MAAM;AAC1B,UAAI,MAAM;AACV,aAAO,QAAQ,CAAC,MAAM;AACpB,eAAO,EAAE,sBAAsB,EAAE;AACjC,eAAO,KAAK,GAAG;AAAA,MACjB,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,gBAAgB,MAAM,iBAAuC,6BAA6B;AAChG,UAAI,MAAM;AACV,oBAAc,QAAQ,CAAC,OAAO;AAE5B,cAAM,KAAK,KAAK,IAAI,GAAG,GAAG,WAAW,CAAC;AACtC,cAAM,IAAI,GAAG,sBAAsB,EAAE,QAAQ;AAC7C,iBAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,iBAAO;AACP,iBAAO,KAAK,GAAG;AAAA,QACjB;AAAA,MACF,CAAC;AAAA,IACH;AACA,QAAI,OAAO,WAAW,MAAM;AAE1B,aAAO;AAAA,IACT;AAKA,UAAM,MAAM,MAAM,iBAAsC,YAAY;AACpE,UAAM,UAAoB,CAAC;AAC3B,QAAI,OAAO;AACX,QAAI,QAAQ,CAAC,OAAO;AAClB,cAAQ,GAAG,sBAAsB,EAAE;AACnC,cAAQ,KAAK,IAAI;AAAA,IACnB,CAAC;AAOD,WAAO;AAAA,MACL;AAAA,MACA,SAAS,IAAI,WAAW,OAAO,UAAU,CAAC;AAAA,MAC1C,OAAO,UAAU;AAAA,MACjB,QAAQ,UAAU;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,MAAM,MAAM,QAAQ,CAAC;AAEzB,QAAM,cAAU,4BAAY,MAAM;AAChC,cAAU,WAAW,CAAC;AAAA,EACxB,GAAG,CAAC,UAAU,CAAC;AAEf,sCAAgB,MAAM;AACpB,YAAQ;AAAA,EACV,GAAG,CAAC,OAAO,CAAC;AAEZ,gCAAU,MAAM;AACd,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAK,IAAI,eAAe,MAAM,QAAQ,CAAC;AAC7C,OAAG,QAAQ,KAAK;AAChB,WAAO,MAAM,GAAG,WAAW;AAAA,EAC7B,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,QAAM,kBAAc;AAAA,IAClB,CAAC,MAA0C;AACzC,iBAAW,UAAU;AAOrB,cAAQ;AACR,UAAI;AACF,QAAC,EAAE,cAA0B,sBAAsB,EAAE,SAAS;AAAA,MAChE,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,oBAAgB;AAAA,IACpB,CAAC,MAA0C;AACzC,YAAM,IAAI,WAAW;AACrB,UAAI,CAAC,EAAG;AACR,cAAQ,EAAE,MAAM;AAAA,QACd,KAAK,UAAU;AACb,gBAAM,OAAO,kBAAkB,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;AACnE,iBAAO,kBAAkB,YAAY,EAAE,UAAU,MAAM,EAAE,UAAU;AACnE;AAAA,QACF;AAAA,QACA,KAAK,gBAAgB;AACnB,gBAAM,OAAO,kBAAkB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM;AAC7D,iBAAO,cAAc,YAAY,EAAE,eAAe,IAAI;AACtD;AAAA,QACF;AAAA,QACA,KAAK,SAAS;AACZ,iBAAO,aAAa,YAAY,EAAE,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO;AAC9E;AAAA,QACF;AAAA,QACA,KAAK,eAAe;AAClB,iBAAO,cAAc,YAAY,iBAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC;AACtF;AAAA,QACF;AAAA,QACA,KAAK,SAAS;AACZ,gBAAM,OAAO,kBAAkB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM;AAC7D,iBAAO,cAAc,YAAY,EAAE,eAAe,IAAI;AACtD,iBAAO,cAAc,YAAY,iBAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC;AACtF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,YAAY,MAAM;AAAA,EACrB;AAIA,QAAM,yBAAqB,4BAAY,CAAC,OAAyB,iBAAiC;AAChG,WAAO,MAAM,eAAe,sBAAsB,EAAE,SAAS;AAAA,EAC/D,GAAG,CAAC,CAAC;AAEL,QAAM,0BAAsB;AAAA,IAC1B,CAAC,aAAqB,CAAC,MAA0C;AAE/D,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,YAAM,QAAQ,SAAS;AACvB,UAAI,CAAC,MAAO;AACZ,YAAM,eAAe,MAAM,sBAAsB,EAAE,SAAS;AAC5D,YAAM,QAAQ,OAAO,IAAI,cAAc,UAAU;AACjD,YAAM,iBAAa,iCAAmB,MAAM,WAAmC,IAAI;AACnF,iBAAW,UAAU,EAAE,MAAM,UAAU,UAAU,QAAQ,EAAE,SAAS,YAAY,aAAa;AAC7F,MAAC,EAAE,cAA0B,kBAAkB,EAAE,SAAS;AAAA,IAC5D;AAAA,IACA,CAAC,UAAU,QAAQ,YAAY,IAAI;AAAA,EACrC;AAEA,QAAM,8BAA0B;AAAA,IAC9B,CAAC,MAA0C;AACzC,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,YAAM,QAAQ,SAAS;AACvB,UAAI,CAAC,MAAO;AACZ,YAAM,eAAe,MAAM,sBAAsB,EAAE,SAAS;AAQ5D,YAAM,SAAS,mBAAmB,OAAO,YAAY;AACrD,YAAM,QAAQ,OAAO,IAAI,cAAc,UAAU;AACjD,YAAM,eAAe,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;AAC/E,iBAAW,UAAU,EAAE,MAAM,gBAAgB,QAAQ,EAAE,SAAS,cAAc,OAAO;AACrF,MAAC,EAAE,cAA0B,kBAAkB,EAAE,SAAS;AAAA,IAC5D;AAAA,IACA,CAAC,UAAU,QAAQ,YAAY,kBAAkB;AAAA,EACnD;AAGA,QAAM,qBAAqB,CAAC,YAC1B,QAAQ,IAAI,CAAC,GAAG,MAAM,KAAK,MAAM,IAAI,IAAI,QAAQ,IAAI,CAAC,EAAG;AAE3D,QAAM,uBAAmB;AAAA,IACvB,CAAC,QAAgB,CAAC,MAA0C;AAC1D,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAIlB,YAAM,IAAI,WAAW;AACrB,UAAI,CAAC,KAAK,EAAE,QAAQ,WAAW,EAAG;AAClC,YAAM,eAAe,EAAE,QAAQ,GAAG,KAAM,QAAQ,IAAI,IAAI,EAAE,QAAQ,MAAM,CAAC;AACzE,iBAAW,UAAU,EAAE,MAAM,SAAS,KAAK,QAAQ,EAAE,SAAS,aAAa;AAC3E,MAAC,EAAE,cAA0B,kBAAkB,EAAE,SAAS;AAAA,IAC5D;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,+BAA2B;AAAA,IAC/B,CAAC,MAA0C;AACzC,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,YAAM,IAAI,WAAW;AACrB,UAAI,CAAC,KAAK,EAAE,QAAQ,WAAW,EAAG;AAClC,iBAAW,UAAU,EAAE,MAAM,eAAe,QAAQ,EAAE,SAAS,aAAa,mBAAmB,EAAE,OAAO,EAAE;AAC1G,MAAC,EAAE,cAA0B,kBAAkB,EAAE,SAAS;AAAA,IAC5D;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,0BAAsB;AAAA,IAC1B,CAAC,MAA0C;AACzC,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,YAAM,QAAQ,SAAS;AACvB,YAAM,IAAI,WAAW;AACrB,UAAI,CAAC,SAAS,CAAC,EAAG;AAClB,YAAM,eAAe,MAAM,sBAAsB,EAAE,SAAS;AAC5D,YAAM,SAAS,mBAAmB,OAAO,YAAY;AACrD,YAAM,QAAQ,OAAO,IAAI,cAAc,UAAU;AACjD,YAAM,eAAe,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;AAC/E,iBAAW,UAAU;AAAA,QACnB,MAAM;AAAA,QACN,QAAQ,EAAE;AAAA,QACV,QAAQ,EAAE;AAAA,QACV;AAAA,QACA;AAAA,QACA,aAAa,mBAAmB,EAAE,OAAO;AAAA,MAC3C;AACA,MAAC,EAAE,cAA0B,kBAAkB,EAAE,SAAS;AAAA,IAC5D;AAAA,IACA,CAAC,UAAU,QAAQ,YAAY,YAAY,kBAAkB;AAAA,EAC/D;AAEA,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,iBAAiB,CAAC,OAA8B;AAAA,IACpD,UAAU;AAAA,IACV,KAAK;AAAA,IACL,MAAM,GAAG,IAAI,CAAC;AAAA,IACd,OAAO;AAAA,IACP,QAAQ,GAAG,OAAO,MAAM;AAAA,IACxB,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,QAAM,iBAAiB,CAAC,OAA8B;AAAA,IACpD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,KAAK,GAAG,IAAI,CAAC;AAAA,IACb,OAAO,GAAG,OAAO,KAAK;AAAA,IACtB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACA,QAAM,oBAAmC;AAAA,IACvC,UAAU;AAAA,IACV,MAAM,GAAG,OAAO,QAAQ,CAAC;AAAA,IACzB,KAAK,GAAG,OAAO,SAAS,CAAC;AAAA,IACzB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAEA,SACE,gFAEG;AAAA,WAAO,OAAO;AAAA,MAAI,CAAC,GAAG,MACrB,IAAI,OAAO,IACT;AAAA,QAAC;AAAA;AAAA,UAEC,iBAAiB;AAAA,UACjB,WAAU;AAAA,UACV,OAAO,eAAe,CAAC;AAAA,UACvB,eAAe,oBAAoB,CAAC;AAAA,UACpC;AAAA,UACA;AAAA,UACA,cAAY,wBAAwB,IAAI,CAAC;AAAA,UACzC,MAAK;AAAA;AAAA,QARA,OAAO,CAAC;AAAA,MASf,IAEA;AAAA,QAAC;AAAA;AAAA,UAEC,iBAAiB;AAAA,UACjB,WAAU;AAAA,UACV,OAAO,eAAe,CAAC;AAAA,UACvB,eAAe;AAAA,UACf;AAAA,UACA;AAAA,UACA,cAAW;AAAA,UACX,MAAK;AAAA;AAAA,QARD;AAAA,MASN;AAAA,IAEJ;AAAA,IAEC,OAAO,QAAQ;AAAA,MAAI,CAAC,GAAG,MACtB,IAAI,OAAO,IACT;AAAA,QAAC;AAAA;AAAA,UAEC,iBAAiB;AAAA,UACjB,WAAU;AAAA,UACV,OAAO,eAAe,CAAC;AAAA,UACvB,eAAe,iBAAiB,CAAC;AAAA,UACjC;AAAA,UACA;AAAA,UACA,cAAY,uBAAuB,IAAI,CAAC;AAAA,UACxC,MAAK;AAAA;AAAA,QARA,OAAO,CAAC;AAAA,MASf,IAEA;AAAA,QAAC;AAAA;AAAA,UAEC,iBAAiB;AAAA,UACjB,WAAU;AAAA,UACV,OAAO,eAAe,CAAC;AAAA,UACvB,eAAe;AAAA,UACf;AAAA,UACA;AAAA,UACA,cAAW;AAAA,UACX,MAAK;AAAA;AAAA,QARD;AAAA,MASN;AAAA,IAEJ;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,iBAAiB;AAAA,QACjB,WAAU;AAAA,QACV,OAAO;AAAA,QACP,eAAe;AAAA,QACf;AAAA,QACA;AAAA,QACA,cAAW;AAAA,QACX,MAAK;AAAA;AAAA,IACP;AAAA,KACF;AAEJ;;;AHlaW,IAAAC,uBAAA;AAFJ,SAAS,SAAS,EAAE,OAAO,OAAO,UAAU,UAAU,cAAc,GAA+B;AACxG,MAAI,MAAM,SAAS,SAAS;AAC1B,WAAO,8CAAC,aAAU,OAAc,OAAc,eAA8B;AAAA,EAC9E;AACA,QAAM,SAAS,WACX,aAAa,WAAW,MAAM,OAAO,SAAS,OAAO,SAAS,GAAG,GAAG,KAAK,IACzE,aAAa,MAAM,OAAO,KAAK;AACnC,QAAM,SAAS,iBAAiB,MAAM,OAAO,OAAO,MAAM,MAAM,QAAQ;AAExE,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,WAAW;AACd,YAAM,QAAQC,YAAW,MAAM,MAAM,KAAK;AAC1C,YAAM,MAAO,IAAI,KAAK;AACtB,aACE;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ,WAAW,kCAAkC,KAAK,GAAG,WAAW,MAAM,MAAM,KAAK,CAAC,GAAG,cAAc,QAAQ,CAAC;AAAA,UAE3G;AAAA;AAAA,MACH;AAAA,IAEJ;AAAA,IACA,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ,WAAW,yBAAyB,WAAW,MAAM,MAAM,KAAK,CAAC,GAAG,cAAc,QAAQ,CAAC;AAAA,UAE1F;AAAA;AAAA,MACH;AAAA,IAEJ,KAAK;AACH,aACE;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ,iBAAe,MAAM,MAAM,YAAY;AAAA,UACvC,WAAW,wBAAwB,WAAW,MAAM,MAAM,KAAK,CAAC;AAAA,UAEhE,wDAAC,UAAM,kBAAO;AAAA;AAAA,MAChB;AAAA,IAEJ,KAAK,YAAY;AACf,YAAM,QAAQ,eAAe,MAAM,MAAM,SAAS;AAClD,aACE;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ,kBAAgB,MAAM,MAAM,YAAY;AAAA,UACxC,mBAAiB;AAAA,UACjB,WAAW,uBAAuB,WAAW,MAAM,MAAM,KAAK,CAAC;AAAA,UAE9D;AAAA;AAAA,YACA;AAAA;AAAA;AAAA,MACH;AAAA,IAEJ;AAAA,IACA,KAAK;AAAA,IACL;AACE,aACE;AAAA,QAAC;AAAA;AAAA,UACE,GAAG;AAAA,UACJ,WAAW,wBAAwB,WAAW,MAAM,MAAM,KAAK,CAAC,GAAG,cAAc,QAAQ,CAAC;AAAA,UAEzF;AAAA;AAAA,MACH;AAAA,EAEN;AACF;AAEO,SAAS,iBACd,OACA,OACA,MACA,UACA;AACA,QAAM,QAAuB,CAAC;AAC9B,MAAI,MAAM,MAAO,OAAM,YAAY,MAAM;AAGzC,SAAO,OAAO,WAAO,8BAAgB,KAAK,KAAK,CAAC,CAAC;AACjD,QAAM,OAAgC;AAAA,IACpC,oBAAoB;AAAA,IACpB,mBAAmB;AAAA,IACnB,KAAK,MAAM;AAAA,IACX,OAAO,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,QAAQ;AAAA,EACjD;AACA,MAAI,CAAC,SAAU,QAAO;AACtB,OAAK,qBAAqB,IAAI,SAAS;AACvC,OAAK,qBAAqB,IAAI,SAAS;AACvC,OAAK,mBAAmB,IAAI,SAAS;AACrC,SAAO;AACT;AAEA,SAAS,cAAc,GAA8B;AACnD,MAAI,CAAC,EAAG,QAAO;AAEf,SAAO,4BAA4B,EAAE,KAAK;AAC5C;AAEA,SAAS,WAAW,GAAiC;AACnD,SAAO,IAAI,aAAa,CAAC,KAAK;AAChC;AAMO,SAAS,UACd,OACA,QAC2B;AAC3B,QAAM,QAAuB,EAAE,GAAI,OAAqC;AACxE,MAAI,OAAO,MAAO,OAAM,YAAY,MAAM;AAC1C,MAAI,OAAO,QAAS,OAAM,kBAAkB,MAAM;AAClD,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,QAAQ;AACjD;AAEA,SAASA,YAAW,GAA+C;AACjE,MAAI,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,EAAG,QAAO;AAChE,SAAO;AACT;AAEA,SAAS,eAAe,GAAoC;AAC1D,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AACzD,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC;AAC/C;AAQA,SAAS,UAAU,EAAE,OAAO,OAAO,cAAc,GAAgC;AAC/E,QAAM,OAAO,cAAc,MAAM,MAAM,IAAI;AAC3C,QAAM,OAAO,cAAc,MAAM,MAAM,IAAI;AAC3C,QAAM,QAAQ,MAAM,SAAS,CAAC;AAC9B,QAAM,aAAS,iCAAmB,MAAM,MAAM,WAAmC,IAAI;AACrF,QAAM,aAAa,MAAM,MAAM;AAC/B,QAAM,aAAa,MAAM,MAAM;AAC/B,QAAM,eAAW,uBAAgC,IAAI;AAGrD,QAAM,WAAW,eAAe,YAAY;AAC5C,QAAM,SAAS,eAAe,UAAU;AACxC,QAAM,kBAAkB,CAAC,iBAAiB,cAAc,UAAU;AAIlE,QAAM,SAAS,iBAAiB;AAChC,QAAM,WAAO,wBAAQ,MAAM;AACzB,QAAI,OAAO,UAAU,OAAO,eAAe,MAAO,QAAO;AACzD,QAAI,OAAO,UAAU,MAAM,eAAe,MAAO,QAAO;AACxD,eAAO,iCAAmB,OAAO,KAAK,OAAO,SAAS;AAAA,EACxD,GAAG,CAAC,OAAO,KAAK,OAAO,WAAW,KAAK,CAAC;AAExC,SACE,+CAAC,SAAI,WAAU,iBACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,oBAAkB;AAAA,QAClB,mBAAgB;AAAA,QAChB,WAAU;AAAA,QACV,OAAO,OAAO,eAAe,WAAW,EAAE,OAAO,GAAG,UAAU,IAAI,IAAI;AAAA,QAEtE;AAAA,wDAAC,cACE,iBAAO,IAAI,CAAC,GAAG,MACd,8CAAC,SAAY,OAAO,EAAE,OAAO,GAAG,CAAC,IAAI,KAA3B,CAA8B,CACzC,GACH;AAAA,UACA,8CAAC,WACA,gBAAM,KAAK,EAAE,QAAQ,SAAS,SAAS,GAAG,CAAC,GAAG,MAAM;AACnD,kBAAM,IAAI,WAAW;AACrB,mBACA;AAAA,cAAC;AAAA;AAAA,gBAEC,iBAAe;AAAA,gBAIf,OACE,OAAO,aAAa,CAAC,MAAM,WACvB,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC,KAAK,IAC/B;AAAA,gBAGL,gBAAM,KAAK,EAAE,QAAQ,KAAK,GAAG,CAACC,IAAG,MAAM;AACtC,wBAAM,OAAO,IAAI,OAAO;AACxB,wBAAM,OAAO,MAAM,IAAI;AACvB,sBAAI,MAAM,MAAM,YAAY,KAAM,QAAO;AACzC,wBAAM,UAAU,SAAS,MAAM,MAAM,OAAO;AAC5C,wBAAM,UAAU,SAAS,MAAM,MAAM,OAAO;AAC5C,wBAAM,QAAQ,MAAM,SAAS,CAAC;AAC9B,wBAAM,SACJ,SAAS,QACT,KAAK,KAAK,OAAO,KAAK,KAAK,UAC3B,KAAK,KAAK,QAAQ,KAAK,KAAK;AAE9B,yBACE;AAAA,oBAAC;AAAA;AAAA,sBAEC,mBAAiB;AAAA,sBACjB,iBAAe;AAAA,sBACf,iBAAe;AAAA,sBACf,qBAAmB;AAAA,sBACnB,qBAAmB;AAAA,sBACnB,SAAS,UAAU,IAAI,UAAU;AAAA,sBACjC,SAAS,UAAU,IAAI,UAAU;AAAA,sBACjC,OAAO;AAAA,wBACL,MAAM;AAAA;AAAA;AAAA,4BAGN;AAAA,0BACE,MAAM,MAAM;AAAA,0BACZ,EAAE,KAAK,GAAG,KAAK,GAAG,SAAS,SAAS,MAAM,UAAU,OAAO;AAAA,0BAC3D;AAAA,0BACA,MAAM,MAAM;AAAA,wBACd;AAAA,sBACF;AAAA,sBACA,WAAW,SAAS,8BAA8B;AAAA,sBAEjD,uBAAa,OAAO,IAAI,KAAK,KAAK,IAAI,EAAE;AAAA;AAAA,oBArBpC;AAAA,kBAsBP;AAAA,gBAEJ,CAAC;AAAA;AAAA,cAjDI;AAAA,YAkDP;AAAA,UAEF,CAAC,GACD;AAAA;AAAA;AAAA,IACF;AAAA,IACC,mBACC,8CAAC,sBAAmB,UAAoB,YAAY,OAAO,MAAY,MAAY;AAAA,KAEvF;AAEJ;AAEA,SAAS,SAAS,GAAoB;AACpC,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,EAAG,QAAO;AAClE,SAAO,KAAK,MAAM,CAAC;AACrB;AAEA,SAAS,cAAc,GAAoB;AACzC,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,EAAG,QAAO;AACnE,SAAO,KAAK,MAAM,CAAC;AACrB;;;AIzRA,IAAAC,iBAAkE;AAsC3D,IAAM,UAAwB;AAAA,EACnC,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AACf;AAEO,SAAS,cAAc,GAAiB,YAAoB,GAAW;AAC5E,QAAM,QAAQ,cAAc,IAAI,EAAE,qBAAqB,IAAI;AAC3D,SAAO,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe;AACnD;AAEO,SAAS,aAAa,GAAyB;AACpD,SAAO,EAAE,QAAQ,EAAE,aAAa,EAAE;AACpC;AA4CO,SAAS,kBAAkB,eAAmC;AACnE,SAAO;AAAA,IACL,OAAO;AAAA,MACL;AAAA,QACE,OAAO,MAAM,KAAK,EAAE,QAAQ,cAAc,GAAG,CAAC,GAAG,OAAO,EAAE,eAAe,EAAE,EAAE;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACF;AAgBO,SAAS,cACd,SACA,UACA,eACA,MACkB;AAClB,QAAM,CAAC,OAAO,QAAQ,QAAI,yBAA0B,SAAS;AAC7D,QAAM,CAAC,QAAQ,SAAS,QAAI,yBAAqB,MAAM,kBAAkB,aAAa,CAAC;AACvF,QAAM,kBAAc,uBAAyB,IAAI;AA0BjD,sCAAgB,MAAM;AACpB,UAAM,cAAc,CAAC,GAAG,MAAM,eAAe,QAAQ;AACrD,QAAI,YAAY,YAAY,QAAQ,kBAAkB,YAAY,SAAS,WAAW,GAAG;AACvF;AAAA,IACF;AACA,gBAAY,UAAU;AACtB,cAAU,CAAC,SAAS;AAClB,YAAM,OAAO,kBAAkB,aAAa;AAC5C,aAAO,iBAAiB,MAAM,IAAI,IAAI,OAAO;AAAA,IAC/C,CAAC;AACD,aAAS,SAAS;AAAA,EAMpB,CAAC;AAKD,sCAAgB,MAAM;AACpB,QAAI,UAAU,UAAW;AACzB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AAEX,QAAI,CAAC,eAAe,MAAM,EAAG;AAC7B,UAAM,OAAO,cAAc,MAAM,UAAU,aAAa;AACxD,QAAI,CAAC,iBAAiB,QAAQ,IAAI,EAAG,WAAU,IAAI;AACnD,aAAS,QAAQ;AAAA,EACnB,GAAG,CAAC,OAAO,QAAQ,UAAU,eAAe,OAAO,CAAC;AAEpD,SAAO,EAAE,QAAQ,MAAM;AACzB;AAIA,SAAS,eAAe,QAA6B;AACnD,SACE,OAAO,MAAM,WAAW,KACxB,OAAO,MAAM,CAAC,EAAE,MAAM;AAAA,IACpB,CAAC,MACC,EAAE,aAAa,UACf,EAAE,kBAAkB,UACpB,EAAE,iBAAiB;AAAA,EACvB;AAEJ;AAEA,SAAS,cACP,MACA,UACA,eACY;AACZ,QAAM,OAAO,wBAAwB,IAAI;AACzC,MAAI,KAAK,WAAW,EAAG,QAAO,kBAAkB,aAAa;AAI7D,QAAM,WAAW,CAAC,cAAsB,cAAc,UAAU,SAAS;AACzE,QAAM,QAAqB,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;AACzC,MAAI,cAAc,MAAM,CAAC;AACzB,MAAI,QAAQ,SAAS,CAAC;AACtB,MAAI,OAAO;AAEX,QAAM,MAAM,KAAK,IAAI,KAAK,QAAQ,aAAa;AAC/C,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,UAAM,KAAK,KAAK,CAAC;AACjB,UAAM,IAAI,cAAc,EAAE;AAE1B,QAAI,OAAO,KAAK,OAAO;AACrB,kBAAY,MAAM,KAAK,EAAE,eAAe,EAAE,CAAC;AAC3C,cAAQ;AACR;AAAA,IACF;AAEA,QAAI,eAAe,EAAE,GAAG;AACtB,YAAM,SAAS,qBAAqB,IAAI,GAAG,OAAO,MAAM,QAAQ;AAChE,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO,OAAO;AACd;AAAA,IACF;AAEA,QAAI,gBAAgB,EAAE,GAAG;AACvB,YAAM,SAAS,qBAAqB,IAAI,GAAG,OAAO,MAAM,QAAQ;AAChE,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO,OAAO;AACd;AAAA,IACF;AAEA,QAAI,eAAe,EAAE,GAAG;AACtB,YAAM,SAAS,oBAAoB,IAAI,GAAG,OAAO,MAAM,QAAQ;AAC/D,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO,OAAO;AACd;AAAA,IACF;AAGA,QAAI,YAAY,MAAM,WAAW,GAAG;AAElC,kBAAY,MAAM,KAAK,EAAE,eAAe,EAAE,CAAC;AAC3C,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO;AAAA,IACT,OAAO;AACL,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,kBAAY,MAAM,KAAK,EAAE,eAAe,EAAE,CAAC;AAC3C,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,EAAE,MAAM,WAAW,EAAG,OAAM,IAAI;AAE9E,SAAO,EAAE,MAAM;AACjB;AAEA,SAAS,qBACP,IACA,eACA,OACA,WACA,UACsB;AACtB,MAAI,QAAQ,SAAS,MAAM,SAAS,CAAC;AACrC,QAAM,aAAa,mBAAmB,EAAE;AACxC,MAAI,eAAe,GAAG;AACpB,UAAM,IAAI,cAAc,EAAE;AAC1B,QAAIC,eAAc,MAAM,MAAM,SAAS,CAAC;AACxC,QAAI,YAAY,IAAI,SAASA,aAAY,MAAM,SAAS,GAAG;AACzD,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,MAAAA,eAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,kBAAY;AAAA,IACd;AACA,IAAAA,aAAY,MAAM,KAAK,EAAE,cAAc,CAAC;AACxC,WAAO,EAAE,UAAU,YAAY,EAAE;AAAA,EACnC;AAMA,QAAM,QAAQ,aAAa,EAAE;AAC7B,MAAI,MAAM,WAAW,GAAG;AAEtB,UAAM,IAAI,cAAc,EAAE;AAC1B,QAAIA,eAAc,MAAM,MAAM,SAAS,CAAC;AACxC,QAAI,YAAY,IAAI,SAASA,aAAY,MAAM,SAAS,GAAG;AACzD,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,MAAAA,eAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,kBAAY;AAAA,IACd;AACA,IAAAA,aAAY,MAAM,KAAK,EAAE,cAAc,CAAC;AACxC,WAAO,EAAE,UAAU,YAAY,EAAE;AAAA,EACnC;AAEA,MAAI,UAAU;AACd,MAAI,cAAc,MAAM,MAAM,SAAS,CAAC;AACxC,MAAI,OAAO;AACX,MAAI,YAAY;AAEhB,SAAO,UAAU,MAAM,QAAQ;AAC7B,UAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,IAAI;AAC1C,QAAI,aAAa,GAAG;AAClB,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO;AACP;AAAA,IACF;AAGA,UAAM,WAAW,MAAM,OAAO,EAAE;AAChC,QAAI,QAAQ;AACZ,aAAS,IAAI,SAAS,IAAI,MAAM,QAAQ,KAAK;AAC3C,YAAM,IAAI,MAAM,CAAC,EAAE,SAAS;AAC5B,UAAI,KAAK,UAAW,SAAQ;AAAA,UACvB;AAAA,IACP;AAEA,QAAI,QAAQ,GAAG;AAEb,UAAI,YAAY,MAAM,WAAW,GAAG;AAElC,gBAAQ;AAAA,MACV,OAAO;AACL,cAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,sBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,gBAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,eAAO;AACP;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY,MAAM,OAAO,EAAE;AACjC,UAAM,UAAU,QAAQ,IAAI,MAAM,SAAS,MAAM,QAAQ,CAAC,EAAE,QAAQ;AACpE,UAAM,aAAa,MAAM,KAAK,EAAE,SAAS;AAEzC,gBAAY,MAAM,KAAK;AAAA,MACrB;AAAA,MACA,UAAU,EAAE,OAAO,WAAW,OAAO,WAAW,KAAK,QAAQ;AAAA,IAC/D,CAAC;AACD,YAAQ;AACR;AACA,cAAU,QAAQ;AAElB,QAAI,UAAU,MAAM,QAAQ;AAC1B,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,KAAK;AAC1B;AAwBA,IAAM,mBAAmB,oBAAI,IAA0B;AACvD,IAAM,2BAA2B;AAEjC,SAAS,aAAa,IAA+B;AACnD,QAAM,OAAO,GAAG,eAAe;AAC/B,QAAM,WAAW,GAAG,GAAG,WAAW,IAAI,IAAI;AAC1C,QAAM,SAAS,iBAAiB,IAAI,QAAQ;AAC5C,MAAI,QAAQ;AAEV,qBAAiB,OAAO,QAAQ;AAChC,qBAAiB,IAAI,UAAU,MAAM;AACrC,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,qBAAqB,EAAE;AACrC,mBAAiB,IAAI,UAAU,KAAK;AACpC,MAAI,iBAAiB,OAAO,0BAA0B;AACpD,UAAM,SAAS,iBAAiB,KAAK,EAAE,KAAK,EAAE;AAC9C,QAAI,WAAW,OAAW,kBAAiB,OAAO,MAAM;AAAA,EAC1D;AACA,SAAO;AACT;AAEA,SAAS,qBAAqB,IAA+B;AAC3D,QAAM,QAAQ,SAAS,YAAY;AACnC,QAAM,QAAsB,CAAC;AAI7B,QAAM,SAAS,SAAS;AAAA,IACtB;AAAA,IACA,WAAW,YAAY,WAAW;AAAA,IAClC,oBAAoB,EAAE;AAAA,EACxB;AACA,MAAI;AACJ,MAAI,aAAa;AAEjB,SAAQ,IAAI,OAAO,SAAS,GAAI;AAC9B,QAAI,EAAE,aAAa,KAAK,WAAW;AACjC,YAAM,OAAO,EAAE,eAAe;AAC9B,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAI;AACF,gBAAM,SAAS,GAAG,CAAC;AACnB,gBAAM,OAAO,GAAG,IAAI,CAAC;AAAA,QACvB,QAAQ;AACN;AAAA,QACF;AACA,cAAM,OAAO,MAAM,sBAAsB;AACzC,YAAI,KAAK,UAAU,KAAK,KAAK,WAAW,EAAG;AAC3C,cAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,YAAI,CAAC,QAAQ,KAAK,OAAO,KAAK,QAAQ;AACpC,gBAAM,KAAK,EAAE,OAAO,aAAa,GAAG,KAAK,KAAK,KAAK,QAAQ,KAAK,OAAO,CAAC;AAAA,QAC1E,OAAO;AACL,eAAK,MAAM,KAAK,IAAI,KAAK,KAAK,KAAK,GAAG;AACtC,eAAK,SAAS,KAAK,IAAI,KAAK,QAAQ,KAAK,MAAM;AAAA,QACjD;AAAA,MACF;AACA,oBAAc,KAAK;AACnB;AAAA,IACF;AACA,QACE,EAAE,aAAa,KAAK,gBACnB,EAAkB,QAAQ,UAAU,SACrC;AACA,YAAM,OAAQ,EAAkB,sBAAsB;AACtD,UAAI,KAAK,SAAS,GAAG;AACnB,cAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,YAAI,CAAC,QAAQ,KAAK,OAAO,KAAK,QAAQ;AACpC,gBAAM,KAAK,EAAE,OAAO,YAAY,KAAK,KAAK,KAAK,QAAQ,KAAK,OAAO,CAAC;AAAA,QACtE,OAAO;AACL,eAAK,MAAM,KAAK,IAAI,KAAK,KAAK,KAAK,GAAG;AACtC,eAAK,SAAS,KAAK,IAAI,KAAK,QAAQ,KAAK,MAAM;AAAA,QACjD;AAAA,MACF;AACA,oBAAc;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAIA,SAAS,cAAc,IAAyB;AAC9C,QAAM,KAAK,iBAAiB,EAAE;AAC9B,QAAM,IAAI,WAAW,GAAG,aAAa,GAAG,IAAI,WAAW,GAAG,gBAAgB,GAAG;AAC7E,SAAO,GAAG,eAAe;AAC3B;AAEA,SAAS,eAAe,IAA0B;AAGhD,QAAM,OAAO,GAAG,aAAa,iBAAiB;AAC9C,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,SAAS,UAAW,QAAO;AAC/B,MAAI,SAAS,aAAc,QAAO;AAClC,SAAO;AACT;AAMA,SAAS,gBAAgB,IAA0B;AACjD,SAAO,GAAG,cAAc,yCAAyC,MAAM;AACzE;AAwBA,SAAS,qBACP,IACA,eACA,OACA,WACA,UAC4B;AAC5B,MAAI,QAAQ,SAAS,MAAM,SAAS,CAAC;AACrC,QAAM,QAAQ,GAAG,cAAc,yCAAyC;AACxE,QAAM,QAAQ,OAAO,cAAc,gBAAgB;AACnD,QAAM,MAAM,QAAS,MAAM,KAAK,MAAM,QAAQ,IAA8B,CAAC;AAC7E,MAAI,IAAI,WAAW,GAAG;AAEpB,UAAM,IAAI,cAAc,EAAE;AAC1B,QAAIA,eAAc,MAAM,MAAM,SAAS,CAAC;AACxC,QAAI,YAAY,IAAI,SAASA,aAAY,MAAM,SAAS,GAAG;AACzD,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,MAAAA,eAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,kBAAY;AAAA,IACd;AACA,IAAAA,aAAY,MAAM,KAAK,EAAE,cAAc,CAAC;AACxC,WAAO,EAAE,UAAU,YAAY,EAAE;AAAA,EACnC;AAEA,QAAM,aAAa,IAAI,IAAI,CAAC,OAAO,GAAG,sBAAsB,EAAE,MAAM;AAIpE,QAAM,UAAU,oBAAI,IAAY;AAChC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,MAAM,MAAM,KAAK,IAAI,CAAC,EAAE,QAAQ;AACtC,eAAW,MAAM,KAAK;AACpB,YAAM,KAAK,GAAG,WAAW;AACzB,UAAI,KAAK,GAAG;AACV,iBAAS,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,GAAG,KAAK;AAC1D,kBAAQ,IAAI,CAAC;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAIA,QAAM,aAAa,cAAc,EAAE;AACnC,QAAM,cAAc,QAAQ,MAAM,sBAAsB,EAAE,SAAS;AACnE,QAAM,SAAS,KAAK,IAAI,GAAG,aAAa,WAAW;AAEnD,MAAI,SAAS;AACb,MAAI,OAAO;AACX,MAAI,YAAY;AAChB,MAAI,cAAc,MAAM,MAAM,SAAS,CAAC;AAExC,SAAO,SAAS,IAAI,QAAQ;AAC1B,UAAM,WAAW,cAAc,IAAI,SAAS;AAC5C,UAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,OAAO,QAAQ;AACrD,QAAI,aAAa,GAAG;AAClB,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO;AACP;AAAA,IACF;AAIA,QAAI,UAAU;AACd,QAAI,OAAO;AACX,aAAS,IAAI,QAAQ,IAAI,IAAI,QAAQ,KAAK;AACxC,cAAQ,WAAW,CAAC;AACpB,UAAI,OAAO,UAAW;AACtB,UAAI,MAAM,IAAI,SAAS,KAAK,CAAC,QAAQ,IAAI,CAAC,EAAG,WAAU,IAAI;AAAA,IAC7D;AAEA,QAAI,UAAU,GAAG;AACf,UAAI,YAAY,MAAM,WAAW,GAAG;AAGlC,YAAI,IAAI;AACR,eAAO,IAAI,IAAI,SAAS,KAAK,QAAQ,IAAI,CAAC,EAAG;AAC7C,kBAAU,IAAI;AAAA,MAChB,OAAO;AACL,cAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,sBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,gBAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,eAAO;AACP;AAAA,MACF;AAAA,IACF;AAEA,QAAI,aAAa;AACjB,aAAS,IAAI,QAAQ,IAAI,SAAS,IAAK,eAAc,WAAW,CAAC;AACjE,QAAI,cAAc,EAAG,eAAc;AAEnC,gBAAY,MAAM,KAAK;AAAA,MACrB;AAAA,MACA,eAAe,EAAE,OAAO,WAAW,UAAU,QAAQ,QAAQ,QAAQ;AAAA,IACvE,CAAC;AACD,YAAQ;AACR;AACA,aAAS;AAET,QAAI,SAAS,IAAI,QAAQ;AACvB,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,KAAK;AAC1B;AAOA,SAAS,eAAe,IAA0B;AAChD,MAAI,GAAG,YAAY,QAAQ,GAAG,YAAY,KAAM,QAAO;AACvD,SAAO,GAAG,UAAU,SAAS,SAAS;AACxC;AAcA,SAAS,oBACP,IACA,eACA,OACA,WACA,UAC2B;AAC3B,MAAI,QAAQ,SAAS,MAAM,SAAS,CAAC;AAKrC,QAAM,QAAyB,CAAC;AAChC,aAAW,KAAK,MAAM,KAAK,GAAG,QAAQ,GAAG;AACvC,QAAI,aAAa,iBAAiB,EAAE,UAAU,SAAS,aAAa,GAAG;AACrE,YAAM,KAAK,CAAC;AAAA,IACd;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI,cAAc,EAAE;AAC1B,QAAIA,eAAc,MAAM,MAAM,SAAS,CAAC;AACxC,QAAI,YAAY,IAAI,SAASA,aAAY,MAAM,SAAS,GAAG;AACzD,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,MAAAA,eAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,kBAAY;AAAA,IACd;AACA,IAAAA,aAAY,MAAM,KAAK,EAAE,cAAc,CAAC;AACxC,WAAO,EAAE,UAAU,YAAY,EAAE;AAAA,EACnC;AAEA,QAAM,UAAU,MAAM,IAAI,CAAC,OAAO,cAAc,EAAE,CAAC;AAKnD,QAAM,aAAa,QAAQ,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACpD,QAAM,kBAAkB,KAAK,IAAI,GAAG,cAAc,EAAE,IAAI,UAAU;AAElE,MAAI,IAAI;AACR,MAAI,cAAc,MAAM,MAAM,SAAS,CAAC;AACxC,MAAI,OAAO;AACX,MAAI,YAAY;AAEhB,SAAO,IAAI,MAAM,QAAQ;AACvB,UAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,IAAI;AAG1C,UAAM,kBAAkB,cAAc;AACtC,QAAI,QAAQ;AACZ,QAAI,WAAW,kBAAkB,kBAAkB;AACnD,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,kBAAY,QAAQ,CAAC;AACrB,UAAI,YAAY,UAAW,SAAQ;AAAA,UAC9B;AAAA,IACP;AAEA,QAAI,QAAQ,GAAG;AAEb,UAAI,YAAY,MAAM,WAAW,GAAG;AAElC,gBAAQ;AAAA,MACV,OAAO;AAEL,cAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,sBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,gBAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,eAAO;AACP;AAAA,MACF;AAAA,IACF;AAEA,gBAAY,MAAM,KAAK;AAAA,MACrB;AAAA,MACA,cAAc,EAAE,OAAO,WAAW,WAAW,GAAG,SAAS,QAAQ,EAAE;AAAA,IACrE,CAAC;AAGD,QAAI,WAAW,kBAAkB,kBAAkB;AACnD,aAAS,IAAI,GAAG,KAAK,OAAO,IAAK,aAAY,QAAQ,CAAC;AACtD,YAAQ;AAER,QAAI,QAAQ;AACZ;AACA,QAAI,IAAI,MAAM,QAAQ;AACpB,YAAM,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;AACxB,oBAAc,MAAM,MAAM,SAAS,CAAC;AACpC,cAAQ,SAAS,MAAM,SAAS,CAAC;AACjC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,KAAK;AAC1B;AAEA,SAAS,wBAAwB,MAAkC;AAQjE,QAAM,eAAe,KAAK,iBAA8B,sCAAsC;AAC9F,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,MAAqB,CAAC;AAC5B,iBAAa,QAAQ,CAAC,OAAO;AAC3B,YAAM,KAAK,GAAG,QAAQ,EAAE,QAAQ,CAAC,MAAM,IAAI,KAAK,CAAgB,CAAC;AAAA,IACnE,CAAC;AACD,WAAO;AAAA,EACT;AACA,SAAO,MAAM,KAAK,KAAK,QAAQ;AACjC;AAMA,SAAS,mBAAmB,IAAyB;AACnD,QAAM,SAAS,SAAS;AAAA,IACtB;AAAA,IACA,WAAW,YAAY,WAAW;AAAA,IAClC,oBAAoB,EAAE;AAAA,EACxB;AACA,MAAI;AACJ,MAAI,MAAM;AACV,SAAQ,IAAI,OAAO,SAAS,GAAI;AAC9B,QAAI,EAAE,aAAa,KAAK,WAAW;AACjC,cAAQ,EAAE,eAAe,IAAI;AAAA,IAC/B,WACE,EAAE,aAAa,KAAK,gBACnB,EAAkB,QAAQ,UAAU,SACrC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,SAAkC;AAC7D,SAAO;AAAA,IACL,WAAW,GAAS;AAClB,UAAI,IAAiB,EAAE;AACvB,aAAO,KAAK,MAAM,SAAS;AACzB,YAAI,EAAE,aAAa,KAAK,cAAc;AACpC,gBAAM,IAAI;AACV,cAAI,EAAE,QAAQ,cAAc,KAAM,QAAO,WAAW;AAAA,QACtD;AACA,YAAI,EAAE;AAAA,MACR;AACA,aAAO,WAAW;AAAA,IACpB;AAAA,EACF;AACF;AAIA,SAAS,kBAAkB,GAAc,GAAuB;AAC9D,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,KAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAC7D,SAAO;AACT;AAEA,SAAS,iBAAiB,GAAe,GAAwB;AAC/D,MAAI,EAAE,MAAM,WAAW,EAAE,MAAM,OAAQ,QAAO;AAC9C,WAAS,IAAI,GAAG,IAAI,EAAE,MAAM,QAAQ,KAAK;AACvC,UAAM,KAAK,EAAE,MAAM,CAAC,EAAE;AACtB,UAAM,KAAK,EAAE,MAAM,CAAC,EAAE;AACtB,QAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,UAAI,GAAG,CAAC,EAAE,kBAAkB,GAAG,CAAC,EAAE,cAAe,QAAO;AACxD,YAAM,KAAK,GAAG,CAAC,EAAE;AACjB,YAAM,KAAK,GAAG,CAAC,EAAE;AACjB,UAAI,MAAM,IAAI;AACZ,YAAI,CAAC,MAAM,CAAC,GAAI,QAAO;AACvB,YAAI,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAK,QAAO;AAAA,MAClF;AACA,YAAM,KAAK,GAAG,CAAC,EAAE;AACjB,YAAM,KAAK,GAAG,CAAC,EAAE;AACjB,UAAI,MAAM,IAAI;AACZ,YAAI,CAAC,MAAM,CAAC,GAAI,QAAO;AACvB,YAAI,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ;AACnF,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,KAAK,GAAG,CAAC,EAAE;AACjB,YAAM,KAAK,GAAG,CAAC,EAAE;AACjB,UAAI,MAAM,IAAI;AACZ,YAAI,CAAC,MAAM,CAAC,GAAI,QAAO;AACvB,YAAI,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS;AACvF,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AC/1BA,IAAAC,iBAAkE;;;ACAlE,IAAAC,iBAA0D;AAYnD,SAAS,gBAAgB,KAAmC;AACjE,QAAM,eAAW,uBAA4B,IAAI;AAEjD,QAAM,gBAAY;AAAA,IAChB,CAAC,WAAuB,IAAI,oBAAoB,MAAM;AAAA,IACtD,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,kBAAc,4BAAY,MAAoB;AAClD,UAAM,OAAO,IAAI,gBAAgB;AACjC,UAAM,OAAO,SAAS;AACtB,QAAI,QAAQ,aAAa,MAAM,IAAI,EAAG,QAAO;AAC7C,aAAS,UAAU;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AAER,aAAO,qCAAqB,WAAW,aAAa,WAAW;AACjE;AAEA,SAAS,aAAa,GAAiB,GAA0B;AAC/D,SACE,EAAE,UAAU,EAAE,SACd,EAAE,WAAW,EAAE,UACf,EAAE,cAAc,EAAE,aAClB,EAAE,iBAAiB,EAAE,gBACrB,EAAE,eAAe,EAAE,cACnB,EAAE,gBAAgB,EAAE,eACpB,EAAE,WAAW,EAAE,UACf,EAAE,sBAAsB,EAAE,qBAC1B,EAAE,uBAAuB,EAAE;AAE/B;;;ADxCA,IAAAC,eA2DO;AAiOA,SAAS,UAAU,OAAyB,CAAC,GAAoB;AACtE,QAAM,UAAM,wBAAQ,MAAM,KAAK,YAAY,IAAI,4BAAe,GAAG,CAAC,KAAK,QAAQ,CAAC;AAChF,QAAM,cAAU,wBAAQ,MAAM,IAAI,2BAAc,GAAG,GAAG,CAAC,GAAG,CAAC;AAE3D,QAAM,CAAC,UAAU,WAAW,QAAI,yBAA6B,MAAM,IAAI,OAAO,CAAC;AAC/E,QAAM,CAAC,WAAW,iBAAiB,QAAI;AAAA,IAAoB,UACzD,iCAAmB,EAAE,YAAY,GAAG,QAAQ,EAAE,CAAC;AAAA,EACjD;AACA,QAAM,mBAAe,uBAAO,SAAS;AACrC,eAAa,UAAU;AAKvB,QAAM,sBAAkB,uBAAqB,CAAC,CAAC;AAC/C,QAAM,CAAC,cAAc,oBAAoB,QAAI,yBAAuB,CAAC,CAAC;AAEtE,QAAM,sBAAkB,4BAAY,CAAC,SAAuB;AAC1D,oBAAgB,UAAU;AAC1B,yBAAqB,IAAI;AAAA,EAC3B,GAAG,CAAC,CAAC;AAEL,gCAAU,MAAM;AASd,QAAI,YAAY;AAChB,QAAI,YAAY;AAChB,QAAI,QAAQ;AACZ,UAAM,QAAQ,MAAM;AAClB,kBAAY;AACZ,UAAI,UAAW;AACf,kBAAY,IAAI,OAAO,CAAC;AAAA,IAC1B;AACA,UAAM,WAAW,MAAM;AACrB,UAAI,UAAW;AACf,kBAAY;AAEZ,UAAI,OAAO,0BAA0B,YAAY;AAC/C,gBAAQ,sBAAsB,KAAK;AAAA,MACrC,OAAO;AACL,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AACA,QAAI,KAAK,GAAG,oBAAoB,QAAQ;AACxC,WAAO,MAAM;AACX,kBAAY;AACZ,UACE,aACA,OAAO,0BAA0B,cACjC,OAAO,yBAAyB,YAChC;AACA,6BAAqB,KAAe;AAAA,MACtC;AACA,UAAI,KAAK,IAAI,oBAAoB,QAAQ;AAAA,IAC3C;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,mBAAe,4BAAY,MAAM,aAAa,SAAS,CAAC,CAAC;AAC/D,QAAM,mBAAe;AAAA,IACnB,CAAC,QAAmB;AAClB,YAAM,OAAO,aAAa;AAC1B,mBAAa,UAAU;AACvB,wBAAkB,GAAG;AAErB,UACE,OAAO,KAAK,gBAAgB,OAAO,EAAE,SAAS,MAC7C,KAAK,MAAM,eAAe,IAAI,MAAM,cAAc,KAAK,MAAM,WAAW,IAAI,MAAM,SACnF;AAEA,cAAM,gBACJ,KAAK,MAAM,eAAe,IAAI,MAAM,cACpC,IAAI,MAAM,WAAW,KAAK,MAAM,SAAS;AAC3C,YAAI,CAAC,eAAe;AAClB,0BAAgB,UAAU,CAAC;AAC3B,+BAAqB,CAAC,CAAC;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,aAAS,uBAAuB,EAAE,KAAK,cAAc,aAAa,CAAC;AACzE,SAAO,UAAU,EAAE,KAAK,cAAc,aAAa;AAEnD,QAAM,0BAAsB,4BAAY,MAAgC;AACtE,UAAM,UAAU,gBAAgB;AAChC,QAAI,OAAO,KAAK,OAAO,EAAE,WAAW,EAAG,QAAO;AAC9C,oBAAgB,UAAU,CAAC;AAC3B,yBAAqB,CAAC,CAAC;AACvB,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAe;AAAA,IACnB,CAAC,MAAgB,UAAyC;AACxD,YAAM,MAAM,aAAa;AACzB,YAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,UAAI,MAAM,eAAe,IAAI,cAAc,MAAM,WAAW,IAAI,QAAQ;AAEtE,cAAM,UAAU,gBAAgB,QAAQ,IAAI;AAC5C,YAAI,YAAY,QAAW;AACzB,cAAI,YAAY,KAAM,QAAO;AAC7B,iBAAO,UAAU,SAAY,OAAO,UAAU,SAAS,KAAK;AAAA,QAC9D;AACA,cAAMC,SAAQ,IAAI,aAAa,MAAM,UAAU;AAC/C,YAAI,CAACA,UAAS,MAAM,WAAW,EAAG,QAAO;AACzC,mBAAO,mCAAqBA,QAAO,MAAM,SAAS,GAAG,MAAM,QAAQ,MAAM,KAAK;AAAA,MAChF;AACA,UAAI,MAAM,eAAe,IAAI,YAAY;AAEvC,iBAAS,IAAI,MAAM,YAAY,KAAK,IAAI,YAAY,KAAK;AACvD,gBAAMA,SAAQ,IAAI,aAAa,CAAC;AAChC,cAAI,CAACA,OAAO,QAAO;AACnB,gBAAM,IAAI,MAAM,MAAM,aAAa,MAAM,SAAS;AAClD,gBAAM,IAAI,MAAM,IAAI,aAAa,IAAI,SAASA,OAAM;AACpD,cAAI,KAAK,EAAG;AACZ,cAAI,KAAC,mCAAqBA,QAAO,GAAG,GAAG,MAAM,KAAK,EAAG,QAAO;AAAA,QAC9D;AACA,eAAO;AAAA,MACT;AACA,YAAM,QAAQ,IAAI,aAAa,MAAM,UAAU;AAC/C,UAAI,CAAC,MAAO,QAAO;AACnB,iBAAO,mCAAqB,OAAO,MAAM,QAAQ,IAAI,QAAQ,MAAM,KAAK;AAAA,IAC1E;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,qBAAiB,4BAAY,MAAM;AACvC,UAAM,MAAM,aAAa;AACzB,UAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,QAAI,MAAM,eAAe,IAAI,YAAY;AAEvC,aAAO,CAAC;AAAA,IACV;AACA,UAAM,QAAQ,IAAI,aAAa,MAAM,UAAU;AAC/C,QAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAI,MAAM,WAAW,IAAI,QAAQ;AAC/B,UAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,iBAAO,8BAAgB,OAAO,MAAM,SAAS,GAAG,MAAM,MAAM;AAAA,IAC9D;AACA,eAAO,8BAAgB,OAAO,MAAM,QAAQ,IAAI,MAAM;AAAA,EACxD,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,iBAAa;AAAA,IACjB,CAAC,MAAgB,UAAgC;AAC/C,YAAM,MAAM,aAAa;AACzB,YAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,YAAM,cAAc,MAAM,eAAe,IAAI,cAAc,MAAM,WAAW,IAAI;AAChF,UAAI,aAAa;AAEf,cAAM,OAAqB,EAAE,GAAG,gBAAgB,QAAQ;AACxD,cAAM,kBAAkB,aAAa,MAAM,KAAK;AAChD,YAAI,iBAAiB;AACnB,eAAK,IAAI,IAAI;AAAA,QACf,OAAO;AACL,eAAK,IAAI,IAAI,UAAU,aAAa,IAAI,IAAI,IAAI,OAAO;AAAA,QACzD;AACA,wBAAgB,IAAI;AACpB;AAAA,MACF;AACA,uBAAAC,YAAc,OAAO,SAAS,MAAM,KAAK;AAAA,IAC3C;AAAA,IACA,CAAC,cAAc,eAAe;AAAA,EAChC;AAEA,QAAM,cAAU;AAAA,IACd,CAAC,MAAgB,UAA+B;AAC9C,YAAM,MAAM,aAAa;AACzB,YAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,YAAM,cAAc,MAAM,eAAe,IAAI,cAAc,MAAM,WAAW,IAAI;AAChF,UAAI,aAAa;AACf,wBAAgB,EAAE,GAAG,gBAAgB,SAAS,CAAC,IAAI,GAAG,MAAM,CAAC;AAC7D;AAAA,MACF;AACA,uBAAAC,SAAW,OAAO,SAAS,MAAM,KAAK;AAAA,IACxC;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,iBAAa;AAAA,IACjB,CAAC,SAAmB;AAClB,YAAM,MAAM,aAAa;AACzB,YAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,YAAM,cAAc,MAAM,eAAe,IAAI,cAAc,MAAM,WAAW,IAAI;AAChF,UAAI,aAAa;AACf,wBAAgB,EAAE,GAAG,gBAAgB,SAAS,CAAC,IAAI,GAAG,KAAK,CAAC;AAC5D;AAAA,MACF;AACA,uBAAAC,YAAc,OAAO,SAAS,IAAI;AAAA,IACpC;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,mBAAe,4BAAY,MAA2B;AAC1D,UAAM,MAAM,aAAa;AACzB,UAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,UAAM,QAAQ,IAAI,aAAa,MAAM,UAAU;AAC/C,QAAI,UAAU,OAAW,QAAO;AAChC,aAAS,IAAI,MAAM,aAAa,GAAG,KAAK,IAAI,YAAY,KAAK;AAC3D,UAAI,IAAI,aAAa,CAAC,MAAM,MAAO,QAAO;AAAA,IAC5C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,mBAAe;AAAA,IACnB,CAA6B,QAAgD;AAC3E,YAAM,MAAM,aAAa;AACzB,YAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,YAAM,QAAQ,IAAI,cAAc,MAAM,UAAU,EAAE,GAAG;AACrD,eAAS,IAAI,MAAM,aAAa,GAAG,KAAK,IAAI,YAAY,KAAK;AAC3D,cAAM,OAAO,IAAI,cAAc,CAAC,EAAE,GAAG;AACrC,YAAI,SAAS,MAAO,QAAO;AAAA,MAC7B;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,mBAAe,4BAAY,CAAC,MAAiB,UAAuB;AACxE,qBAAAC,cAAgB,OAAO,SAAS,MAAM,KAAK;AAAA,EAC7C,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAe;AAAA,IACnB,CAA6B,KAAQ,UAAgC;AACnE,uBAAAC,cAAgB,OAAO,SAAS,KAAK,KAAK;AAAA,IAC5C;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,eAAW,4BAAY,CAAC,UAAsB;AAClD,UAAM,MAAM,aAAa;AAGzB,UAAM,SAAS,IAAI,MAAM,aAAa;AACtC,QAAI,OAAQ,kBAAAC,aAAe,OAAO,SAAS,SAAS,KAAK;AAAA,QACpD,kBAAAD,cAAgB,OAAO,SAAS,SAAS,KAAK;AAAA,EACrD,GAAG,CAAC,CAAC;AAEL,QAAM,eAAW,4BAAY,MAAwC;AACnE,UAAM,MAAM,aAAa;AACzB,QAAI,IAAI,MAAM,aAAa,QAAQ,IAAI,QAAQ,IAAI,MAAM,UAAU,GAAG;AAEpE,aAAO,IAAI,aAAa,IAAI,MAAM,YAAY,IAAI,MAAM,SAAS,EAAE;AAAA,IACrE;AAEA,WAAO,aAAa,OAAO;AAAA,EAC7B,GAAG,CAAC,KAAK,YAAY,CAAC;AAEtB,QAAM,wBAAoB,4BAAY,CAAC,UAAyB;AAG9D,qBAAAC,aAAe,OAAO,SAAS,WAAW,KAAK;AAAA,EACjD,GAAG,CAAC,CAAC;AAEL,QAAM,wBAAoB,4BAAY,MAA0B;AAC9D,UAAM,MAAM,aAAa;AACzB,QAAI,IAAI,MAAM,aAAa,QAAQ,CAAC,IAAI,QAAQ,IAAI,MAAM,UAAU,EAAG,QAAO;AAE9E,WAAO,IAAI,aAAa,IAAI,MAAM,YAAY,IAAI,MAAM,SAAS,EAAE;AAAA,EACrE,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,2BAAuB,4BAAY,CAAC,WAA8B;AACtE,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAI,QAAQ,IAAI,MAAM,UAAU,EAAG;AAGxC,qBAAAC,qBAAuB,OAAO,SAAS,IAAI,MAAM,YAAY,gBAAgB,MAAM;AAAA,EACrF,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,2BAAuB,4BAAY,MAAyB;AAChE,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAI,QAAQ,IAAI,MAAM,UAAU,EAAG,QAAO;AAC/C,WAAO,IAAI,cAAc,IAAI,MAAM,UAAU,EAAE,gBAAgB;AAAA,EACjE,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,0BAAsB,4BAAY,CAAC,UAAyB;AAChE,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAI,QAAQ,IAAI,MAAM,UAAU,EAAG;AACxC,qBAAAA,qBAAuB,OAAO,SAAS,IAAI,MAAM,YAAY,eAAe,KAAK;AAAA,EACnF,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,0BAAsB,4BAAY,MAA0B;AAChE,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAI,QAAQ,IAAI,MAAM,UAAU,EAAG,QAAO;AAC/C,WAAO,IAAI,cAAc,IAAI,MAAM,UAAU,EAAE;AAAA,EACjD,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,mBAAe;AAAA,IACnB,CAAC,SAA4B;AAC3B,YAAM,MAAM,aAAa;AACzB,YAAM,EAAE,OAAO,IAAI,QAAI,2BAAa,GAAG;AACvC,eAAS,IAAI,MAAM,YAAY,KAAK,IAAI,YAAY,KAAK;AACvD,YAAI,IAAI,aAAa,CAAC,MAAM,WAAY,QAAO;AAC/C,YAAI,IAAI,cAAc,CAAC,EAAE,aAAa,KAAM,QAAO;AAAA,MACrD;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,iBAAa,4BAAY,CAAC,SAAmB;AACjD,qBAAAC,YAAc,OAAO,SAAS,IAAI;AAAA,EACpC,GAAG,CAAC,CAAC;AACL,QAAM,iBAAa,4BAAY,MAAM;AACnC,qBAAAC,YAAc,OAAO,OAAO;AAAA,EAC9B,GAAG,CAAC,CAAC;AACL,QAAM,iBAAa,4BAAY,MAAM;AACnC,qBAAAC,YAAc,OAAO,OAAO;AAAA,EAC9B,GAAG,CAAC,CAAC;AACL,QAAM,oBAAgB,4BAAY,MAAe;AAC/C,eAAO,aAAAC,eAAiB,OAAO,OAAO;AAAA,EACxC,GAAG,CAAC,CAAC;AAGL,QAAM,gBAAY,4BAAY,MAAe;AAC3C,WAAO,aAAa,QAAQ,MAAM,aAAa;AAAA,EACjD,GAAG,CAAC,CAAC;AACL,QAAM,uBAAmB,4BAAY,MAA4B;AAC/D,eAAO,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAAA,EACxD,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,kBAAc,4BAAY,CAAC,MAAc,SAAiB;AAC9D,qBAAAC,aAAe,OAAO,SAAS,MAAM,IAAI;AAAA,EAC3C,GAAG,CAAC,CAAC;AACL,QAAM,wBAAoB,4BAAY,CAAC,OAAe,YAA+B;AACnF,qBAAAC,mBAAqB,OAAO,SAAS,OAAO,OAAO;AAAA,EACrD,GAAG,CAAC,CAAC;AACL,QAAM,kBAAc,4BAAY,CAAC,eAAwB;AACvD,UAAM,MAAM,cAAc,aAAa,QAAQ,MAAM;AACrD,qBAAAC,aAAe,OAAO,SAAS,GAAG;AAAA,EACpC,GAAG,CAAC,CAAC;AACL,QAAM,qBAAiB,4BAAY,MAAM;AACvC,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK;AACV,qBAAAC,gBAAa,OAAO,SAAS,IAAI,YAAY,IAAI,KAAK,QAAQ;AAAA,EAChE,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,qBAAiB,4BAAY,MAAM;AACvC,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK;AACV,qBAAAA,gBAAa,OAAO,SAAS,IAAI,YAAY,IAAI,KAAK,OAAO;AAAA,EAC/D,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,uBAAmB,4BAAY,MAAM;AACzC,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK;AACV,qBAAAC,gBAAa,OAAO,SAAS,IAAI,YAAY,IAAI,GAAG;AAAA,EACtD,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,uBAAmB,4BAAY,MAAM;AACzC,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK;AACV,qBAAAC,mBAAgB,OAAO,SAAS,IAAI,YAAY,IAAI,KAAK,QAAQ;AAAA,EACnE,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,wBAAoB,4BAAY,MAAM;AAC1C,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK;AACV,qBAAAA,mBAAgB,OAAO,SAAS,IAAI,YAAY,IAAI,KAAK,OAAO;AAAA,EAClE,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,0BAAsB,4BAAY,MAAM;AAC5C,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK;AACV,qBAAAC,mBAAgB,OAAO,SAAS,IAAI,YAAY,IAAI,GAAG;AAAA,EACzD,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,qBAAiB,4BAAY,MAAM;AACvC,qBAAAC,gBAAgB,OAAO,OAAO;AAAA,EAChC,GAAG,CAAC,CAAC;AACL,QAAM,qBAAiB,4BAAY,MAAM;AACvC,qBAAAC,gBAAgB,OAAO,OAAO;AAAA,EAChC,GAAG,CAAC,CAAC;AACL,QAAM,iBAAa,4BAAY,MAAe;AAC5C,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK,QAAO;AACjB,eAAO,aAAAC,YAAc,OAAO,SAAS,IAAI,YAAY,IAAI,KAAK,IAAI,GAAG;AAAA,EACvE,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,gBAAY,4BAAY,MAAe;AAC3C,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK,QAAO;AACjB,eAAO,aAAAC,WAAa,OAAO,SAAS,IAAI,YAAY,IAAI,KAAK,IAAI,GAAG;AAAA,EACtE,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,gBAAY,4BAAY,MAAe;AAC3C,UAAM,UAAM,8BAAgB,KAAK,aAAa,QAAQ,KAAK;AAC3D,QAAI,CAAC,IAAK,QAAO;AACjB,eAAO,aAAAC,WAAa,OAAO,SAAS,IAAI,YAAY,IAAI,KAAK,IAAI,GAAG;AAAA,EACtE,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,wBAAoB;AAAA,IACxB,CAAC,YAAoB,UAAkB,UAAkB,SAAoB;AAC3E,uBAAAC,mBAAqB,OAAO,SAAS,YAAY,UAAU,UAAU,IAAI;AAAA,IAC3E;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,oBAAgB,4BAAY,CAAC,YAAoB,QAAgB;AACrE,qBAAAC,eAAiB,OAAO,SAAS,YAAY,GAAG;AAAA,EAClD,GAAG,CAAC,CAAC;AACL,QAAM,mBAAe,4BAAY,CAAC,YAAoB,KAAa,OAAe;AAChF,qBAAAC,cAAgB,OAAO,SAAS,YAAY,KAAK,EAAE;AAAA,EACrD,GAAG,CAAC,CAAC;AACL,QAAM,oBAAgB,4BAAY,CAAC,YAAoB,YAAsB;AAC3E,qBAAAC,eAAiB,OAAO,SAAS,YAAY,OAAO;AAAA,EACtD,GAAG,CAAC,CAAC;AACL,QAAM,mBAAe;AAAA,IACnB,UAAM,iCAAmB,KAAK,aAAa,OAAO;AAAA,IAClD,CAAC,GAAG;AAAA,EACN;AACA,QAAM,4BAAwB,4BAAY,MAAe;AACvD,eAAO,iCAAmB,KAAK,aAAa,OAAO,MAAM;AAAA,EAC3D,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,qBAAiB,4BAAY,MAAe;AAChD,eAAO,aAAAC,gBAAkB,OAAO,OAAO;AAAA,EACzC,GAAG,CAAC,CAAC;AAGL,QAAM,kBAAc,4BAAY,CAAC,UAAsB;AACrD,qBAAAC,aAAe,OAAO,SAAS,KAAK;AAAA,EACtC,GAAG,CAAC,CAAC;AACL,QAAM,oBAAgB;AAAA,IACpB,CACE,YACA,QACA,SACA,WACA,cACG;AACH,uBAAAC,eAAiB,OAAO,SAAS,YAAY,QAAQ,SAAS,WAAW,SAAS;AAAA,IACpF;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,sBAAkB;AAAA,IACtB,CAAC,MAAgB,IAAc,YAAoB,eAAuB;AACxE,uBAAAC,iBAAmB,OAAO,SAAS,MAAM,IAAI,YAAY,UAAU;AAAA,IACrE;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,qBAAiB,uBAAO,KAAK,WAAW;AAC9C,iBAAe,UAAU,KAAK;AAC9B,QAAM,0BAAsB,4BAAY,OAAO,SAA8B;AAC3E,QAAI,CAAC,KAAK,KAAK,WAAW,QAAQ,EAAG;AAIrC,UAAM,UAAU,MAAM,cAAc,IAAI;AACxC,UAAM,OAAO,MAAM,oBAAoB,OAAO;AAC9C,QAAI,IAAI,KAAK;AACb,QAAI,IAAI,KAAK;AACb,QAAI,IAAI,kBAAkB;AACxB,UAAI,KAAK,MAAO,IAAI,mBAAoB,CAAC;AACzC,UAAI;AAAA,IACN;AACA,UAAM,SAAS,eAAe;AAC9B,UAAM,MAAM,SAAS,MAAM,OAAO,IAAI,IAAI;AAC1C,qBAAAF,aAAe,OAAO,SAAS,EAAE,MAAM,SAAS,KAAK,OAAO,GAAG,QAAQ,EAAE,CAAC;AAAA,EAC5E,GAAG,CAAC,CAAC;AACL,QAAM,uBAAmB,4BAAY,MAAM;AACzC,UAAM,MAAM,aAAa;AACzB,QAAI,IAAI,OAAO,eAAe,IAAI,MAAM,WAAY,QAAO;AAC3D,SAAK,IAAI,OAAO,aAAa,SAAS,IAAI,MAAM,aAAa,IAAK,QAAO;AACzE,UAAM,IAAI,IAAI,OAAO;AACrB,UAAM,IAAI,IAAI,MAAM;AACpB,QAAI,KAAK,IAAI,IAAI,CAAC,MAAM,EAAG,QAAO;AAClC,UAAM,QAAQ,KAAK,IAAI,GAAG,CAAC;AAC3B,UAAM,QAAQ,IAAI,OAAO,IAAI,OAAO,YAAY,IAAI,OAAO,SAAS;AACpE,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,QAAQ,MAAM,QAAQ;AAC5B,QAAI,SAAS;AACb,eAAW,MAAM,OAAO;AACtB,UAAI,OAAO,GAAG,WAAW,UAAU;AACjC,kBAAU,GAAG,OAAO;AACpB;AAAA,MACF;AACA,UAAI,WAAW,aAAS,2BAAa,GAAG,MAAM,GAAG;AAC/C,eAAO;AAAA,UACL,YAAY,IAAI,OAAO;AAAA,UACvB,WAAW,IAAI,OAAO;AAAA,UACtB,QAAQ;AAAA,UACR,OAAO,GAAG;AAAA,QACZ;AAAA,MACF;AACA,gBAAU;AAAA,IACZ;AACA,WAAO;AAAA,EACT,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,CAAC,aAAa,cAAc,QAAI,yBAA6B,IAAI;AACvE,QAAM,qBAAiB,uBAA2B,IAAI;AACtD,QAAM,kBAAc,4BAAY,CAAC,cAAc,OAA+B;AAC5E,WAAO,IAAI,QAAQ,CAAC,YAAY;AAK9B,YAAM,MAAmB,EAAE,aAAa,SAAS,WAAW,aAAa,QAAQ;AACjF,qBAAe,UAAU;AACzB,qBAAe,GAAG;AAAA,IACpB,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AACL,QAAM,yBAAqB;AAAA,IACzB,CAAC,SAAwB;AACvB,YAAM,MAAM,eAAe;AAC3B,qBAAe,UAAU;AACzB,qBAAe,IAAI;AACnB,UAAI,KAAK;AAGP,qBAAa,IAAI,SAAS;AAC1B,YAAI,QAAQ,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAGA,QAAM,eAAe,gBAAgB,GAAG;AACxC,QAAM,sBAAkB;AAAA,IACtB,CAAC,YAAmC;AAClC,UAAI,gBAAgB,OAAO;AAAA,IAC7B;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AACA,QAAM,CAAC,mBAAmB,oBAAoB,QAAI,yBAAmC,IAAI;AACzF,QAAM,wBAAoB,4BAAY,MAAwB;AAC5D,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,2BAAqB,EAAE,SAAS,IAAI,gBAAgB,GAAG,QAAQ,CAAC;AAAA,IAClE,CAAC;AAAA,EACH,GAAG,CAAC,GAAG,CAAC;AACR,QAAM,+BAA2B,4BAAY,CAAC,YAAqB;AACjE,yBAAqB,CAAC,SAAS;AAC7B,UAAI,KAAM,MAAK,QAAQ,OAAO;AAC9B,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAGL,QAAM,CAAC,qBAAqB,sBAAsB,QAChD,yBAAqC,IAAI;AAC3C,QAAM,0BAAsB;AAAA,IAC1B,CAAC,iBAAiB,IAAI,eAA6B,aAAiD;AAClG,aAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,+BAAuB,EAAE,gBAAgB,cAAc,QAAQ,CAAC;AAAA,MAClE,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,iCAA6B,4BAAY,CAAC,WAAsC;AACpF,2BAAuB,CAAC,SAAS;AAC/B,UAAI,KAAM,MAAK,QAAQ,MAAM;AAC7B,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAGL,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,yBAAgC,IAAI;AAChF,QAAM,qBAAiB;AAAA,IACrB,CAAC,eAAe,IAAI,iBAAiB,UAAyC;AAC5E,aAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,0BAAkB,EAAE,cAAc,gBAAgB,QAAQ,CAAC;AAAA,MAC7D,CAAC;AAAA,IACH;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,4BAAwB,4BAAY,CAAC,WAAiC;AAC1E,sBAAkB,CAAC,SAAS;AAC1B,UAAI,KAAM,MAAK,QAAQ,MAAM;AAC7B,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,oBAAgB;AAAA,IACpB,OAAO,OAAe,YAAoC;AAkBxD,YAAM,kBAAkB,OAAO,QAAQ,aAAa;AAQpD,YAAM,cAAc,iBAAiB;AAGrC,UAAI;AACJ,UAAI;AACF,qBAAa,MAAM,OAAO,mBAAmB;AAAA,MAC/C,QAAQ;AAON;AAAA,MACF;AACA,YAAM,EAAE,kBAAkB,eAAe,cAAc,gBAAgB,IAAI;AAC3E,YAAM,IAAI,iBAAiB,OAAO,OAAO;AACzC,UAAI,CAAC,EAAE,GAAI;AAIX,YAAM,OAAO,SAAS,cAA2B,UAAU;AAC3D,YAAM,OAAO,OAAO,cAAc,IAAI,IAAI;AAC1C,UAAI,IAAI,KAAK,MAAM,EAAE,UAAU,IAAI;AACnC,UAAI,IAAI,KAAK,MAAM,EAAE,WAAW,IAAI;AAMpC,UAAI,cAAc;AAClB,UAAI,IAAI,kBAAkB;AACxB,sBAAc,mBAAmB;AAGjC,YAAI,KAAK,MAAO,IAAI,mBAAoB,CAAC;AACzC,YAAI;AAAA,MACN;AACA,YAAM,MAAM,aAAa,EAAE,GAAG;AAC9B,UAAI;AACJ,UAAI;AACF,sBAAc,MAAM,gBAAgB,EAAE,KAAK,GAAG,CAAC;AAAA,MACjD,QAAQ;AAGN,sBAAc;AAAA,MAChB;AAMA,aAAO,QAAQ,aAAa,eAAe;AAa3C,YAAM,kBAAsE,CAAC;AAC7E,cAAI,6BAAe,aAAa,KAAK,GAAG;AAOtC,cAAM,OAAO,YAAY;AACzB,YAAI,KAAK,WAAW,OAAW,iBAAgB,SAAS,KAAK;AAC7D,YAAI,KAAK,YAAY,OAAW,iBAAgB,UAAU,KAAK;AAC/D,YAAI,KAAK,YAAY,OAAW,iBAAgB,UAAU,KAAK;AAAA,MACjE;AACA,uBAAAA,aAAe,OAAO,SAAS;AAAA,QAC7B,MAAM;AAAA,QACN;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,GAAG;AAAA,QACH,GAAI,UAAU,EAAE,OAAO,SAAkB,IAAI,CAAC;AAAA,QAC9C,SAAS;AAAA,UACP;AAAA,UACA;AAAA,UACA,GAAI,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,EAAE,WAAW,WAAW,KAAK;AAAA,QAC/D;AAAA,QACA,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,MACvC,CAAC;AAAA,IACH;AAAA,IACA,CAAC,gBAAgB;AAAA,EACnB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,KAAK;AAAA,EACpB;AACF;AAEA,SAAS,cAAc,MAA6B;AAClD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,IAAI,WAAW;AAC9B,WAAO,UAAU,MAAM,OAAO,OAAO,SAAS,IAAI,MAAM,kBAAkB,CAAC;AAC3E,WAAO,SAAS,MAAM,QAAQ,OAAO,MAAgB;AACrD,WAAO,cAAc,IAAI;AAAA,EAC3B,CAAC;AACH;AAEA,SAAS,oBAAoB,KAAyD;AACpF,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,UAAU,MAAM,OAAO,IAAI,MAAM,mBAAmB,CAAC;AACzD,QAAI,SAAS,MAAM,QAAQ,EAAE,OAAO,IAAI,cAAc,QAAQ,IAAI,cAAc,CAAC;AACjF,QAAI,MAAM;AAAA,EACZ,CAAC;AACH;AAEA,IAAM,eAA8B,oBAAI,IAAI,CAAC,QAAQ,UAAU,aAAa,QAAQ,CAAC;AAErF,SAAS,UAAU,GAAY,GAAqB;AAClD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,OAAO,MAAM,YAAY,KAAK,KAAK,OAAO,MAAM,UAAU;AAC5D,UAAM,KAAK,OAAO,KAAK,CAAC;AACxB,UAAM,KAAK,OAAO,KAAK,CAAC;AACxB,QAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,WAAO,GAAG,MAAM,CAAC,MAAO,EAA8B,CAAC,MAAO,EAA8B,CAAC,CAAC;AAAA,EAChG;AACA,SAAO;AACT;;;AxBAc,IAAAG,uBAAA;AAj+Bd,IAAM,gBAAgC,CAAC,EAAE,YAAY,UAAU,MAC7D,GAAG,UAAU,MAAM,SAAS;AAEvB,SAAS,OAAO;AAAA,EACrB,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AACF,GAA6B;AAC3B,QAAM,WAAW,UAAU;AAC3B,QAAM,SAAS,kBAAkB;AACjC,QAAM,EAAE,KAAK,SAAS,UAAU,cAAc,aAAa,IAAI;AAE/D,QAAM,cAAU,uBAAuB,IAAI;AAC3C,QAAM,qBAAiB,uBAAO,KAAK;AAWnC,QAAM,uBAAmB,uBAAO,KAAK;AAGrC,QAAM,CAAC,eAAe,gBAAgB,QAAI,yBAIhC,IAAI;AACd,QAAM,uBAAmB,uBAA6B,IAAI;AAG1D,QAAM,CAAC,aAAa,cAAc,QAAI,yBAG5B,IAAI;AACd,QAAM,uBAAmB,uBAAiC,IAAI;AAC9D,QAAM,+BAA2B,uBAAO,KAAK;AAI7C,gCAAU,MAAM;AACd,QAAI,CAAC,UAAW;AAChB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,QAAI,MAAqB;AACzB,UAAM,UAAU,MAAM;AACpB,YAAM;AACN,YAAM,MAAM,iBAAiB,IAAI;AACjC,gBAAU;AAAA,QACR;AAAA,QACA,UAAM,+BAAgB,KAAK,GAAG,KAAK,OAAO;AAAA,MAC5C;AAAA,IACF;AACA,UAAM,cAAc,MAAM;AACxB,YAAM,IAAI,SAAS,aAAa;AAChC,UAAI,CAAC,KAAK,EAAE,eAAe,EAAG;AAE9B,UAAI,EAAE,cAAc,CAAC,KAAK,SAAS,EAAE,UAAU,EAAG;AAClD,UAAI,OAAO,KAAM;AACjB,YAAM,sBAAsB,OAAO;AAAA,IACrC;AACA,UAAM,SAAS,MAAM,UAAU,mBAAmB,UAAU,IAAI;AAChE,aAAS,iBAAiB,mBAAmB,WAAW;AACxD,SAAK,iBAAiB,QAAQ,QAAQ,IAAI;AAC1C,WAAO,MAAM;AACX,eAAS,oBAAoB,mBAAmB,WAAW;AAC3D,WAAK,oBAAoB,QAAQ,QAAQ,IAAI;AAC7C,UAAI,OAAO,KAAM,sBAAqB,GAAG;AACzC,gBAAU,mBAAmB,UAAU,IAAI;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,WAAW,GAAG,CAAC;AAEnB,QAAM,aAAS,uBAAuB,EAAE,KAAK,cAAc,aAAa,CAAC;AACzE,SAAO,UAAU,EAAE,KAAK,cAAc,aAAa;AACnD,QAAM,iBAAa,uBAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,gBAAY,uBAAO,MAAM;AAC/B,YAAU,UAAU;AAOpB,sCAAgB,MAAM;AACpB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,QAAI,eAAe,QAAS;AAC5B,QAAI,SAAS,kBAAkB,KAAM;AACrC,UAAM,UAAU,iBAAiB,IAAI;AACrC,QAAI,WAAW,gBAAgB,SAAS,OAAO,SAAS,EAAG;AAC3D,QAAI,qBAAqB,OAAO,SAAS,GAAG;AAS1C,wBAAkB,MAAM,OAAO,SAAS;AACxC,+BAAyB,UAAU;AACnC;AAAA,IACF;AAQA,6BAAyB,UAAU;AACnC,sBAAkB,MAAM,OAAO,SAAS;AAAA,EAC1C,CAAC;AAED,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,UAAU,MAAM;AACpB,UAAI,eAAe,QAAS;AAC5B,UAAI,SAAS,kBAAkB,MAAM;AAGnC,iCAAyB,UAAU;AACnC;AAAA,MACF;AACA,YAAM,WAAW,iBAAiB,IAAI;AACtC,UAAI,yBAAyB,SAAS;AAMpC,YAAI,YAAY,gBAAgB,UAAU,UAAU,QAAQ,aAAa,CAAC,GAAG;AAC3E,mCAAyB,UAAU;AAAA,QACrC;AACA;AAAA,MACF;AACA,UAAI,SAAU,cAAa,QAAQ;AAAA,IACrC;AACA,aAAS,iBAAiB,mBAAmB,OAAO;AACpD,WAAO,MAAM,SAAS,oBAAoB,mBAAmB,OAAO;AAAA,EACtE,GAAG,CAAC,YAAY,CAAC;AASjB,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AAQX,QAAI,OAAyB;AAE7B,aAAS,eAAe,QAIf;AACP,UAAI,EAAE,kBAAkB,MAAO,QAAO;AACtC,UAAI,IAAiB;AACrB,UAAI,SAA6B;AACjC,UAAI,UAAmC;AACvC,aAAO,KAAK,MAAM,MAAM;AACtB,YAAI,EAAE,aAAa,KAAK,cAAc;AACpC,gBAAM,IAAI;AACV,cAAI,CAAC,UAAU,EAAE,QAAQ,aAAa,KAAM,UAAS;AACrD,cAAI,EAAE,YAAY,WAAW,EAAE,QAAQ,cAAc,MAAM;AACzD,sBAAU;AACV;AAAA,UACF;AAAA,QACF;AACA,YAAI,EAAE;AAAA,MACR;AACA,UAAI,CAAC,UAAU,CAAC,QAAS,QAAO;AAChC,YAAM,YAAY,OAAO,SAAS,OAAO,QAAQ,WAAY,EAAE;AAC/D,YAAM,aAAa,OAAO,SAAS,QAAQ,QAAQ,YAAa,EAAE;AAClE,aAAO,EAAE,YAAY,WAAW,QAAQ;AAAA,IAC1C;AAEA,UAAM,gBAAgB,CAAC,MAAoB;AACzC,UAAI,EAAE,WAAW,EAAG;AACpB,YAAM,MAAM,eAAe,EAAE,MAAM;AACnC,UAAI,CAAC,KAAK;AACR,eAAO;AACP;AAAA,MACF;AACA,aAAO;AAAA,QACL,YAAY,IAAI;AAAA,QAChB,iBAAiB,IAAI;AAAA,QACrB,eAAe,IAAI;AAAA,QACnB,YAAY;AAAA,MACd;AAAA,IACF;AAEA,UAAM,gBAAgB,CAAC,MAAoB;AACzC,UAAI,CAAC,KAAM;AAEX,WAAK,EAAE,UAAU,OAAO,GAAG;AACzB,eAAO;AACP;AAAA,MACF;AACA,YAAM,MAAM,eAAe,EAAE,MAAM;AACnC,UAAI,CAAC,IAAK;AACV,UAAI,IAAI,eAAe,KAAK,WAAY;AACxC,UAAI,IAAI,cAAc,KAAK,mBAAmB,CAAC,KAAK,WAAY;AAEhE,UAAI,CAAC,KAAK,YAAY;AACpB,aAAK,aAAa;AAElB,eAAO,aAAa,GAAG,gBAAgB;AAAA,MACzC;AACA,QAAE,eAAe;AACjB,aAAO,QAAQ,aAAa;AAAA,QAC1B,QAAQ,EAAE,YAAY,KAAK,YAAY,WAAW,KAAK,iBAAiB,QAAQ,EAAE;AAAA,QAClF,OAAO,EAAE,YAAY,KAAK,YAAY,WAAW,IAAI,WAAW,QAAQ,EAAE;AAAA,MAC5E,CAAC;AAAA,IACH;AAEA,UAAM,cAAc,MAAM;AACxB,aAAO;AAAA,IACT;AAEA,SAAK,iBAAiB,eAAe,aAAa;AAClD,aAAS,iBAAiB,eAAe,aAAa;AACtD,aAAS,iBAAiB,aAAa,WAAW;AAClD,WAAO,MAAM;AACX,WAAK,oBAAoB,eAAe,aAAa;AACrD,eAAS,oBAAoB,eAAe,aAAa;AACzD,eAAS,oBAAoB,aAAa,WAAW;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,aAAa,CAAC,MAAiB;AACnC,UAAI,CAAC,EAAE,aAAc;AACrB,YAAM,WAAW,MAAM,KAAK,EAAE,aAAa,SAAS,CAAC,CAAC,EAAE;AAAA,QACtD,CAAC,MAAM,EAAE,SAAS;AAAA,MACpB;AACA,UAAI,CAAC,SAAU;AACf,QAAE,eAAe;AACjB,QAAE,aAAa,aAAa;AAAA,IAC9B;AACA,UAAM,SAAS,CAAC,MAAiB;AAC/B,UAAI,CAAC,EAAE,aAAc;AACrB,YAAM,SAAS,MAAM,KAAK,EAAE,aAAa,KAAK,EAAE;AAAA,QAAO,CAAC,MACtD,EAAE,KAAK,WAAW,QAAQ;AAAA,MAC5B;AACA,UAAI,OAAO,WAAW,EAAG;AACzB,QAAE,eAAe;AAEjB,YAAM,QAAQ,oBAAoB,EAAE,SAAS,EAAE,OAAO;AACtD,UAAI,OAAO;AACT,cAAM,MAAM,OAAO,aAAa;AAChC,aAAK,gBAAgB;AACrB,aAAK,SAAS,KAAK;AACnB,cAAM,MAAM,iBAAiB,IAAI;AACjC,YAAI,IAAK,QAAO,QAAQ,aAAa,GAAG;AAAA,MAC1C;AACA,YAAM,YAAY;AAChB,mBAAW,KAAK,OAAQ,OAAM,UAAU,QAAQ,oBAAoB,CAAC;AAAA,MACvE,GAAG;AAAA,IACL;AACA,SAAK,iBAAiB,YAAY,UAAU;AAC5C,SAAK,iBAAiB,QAAQ,MAAM;AACpC,WAAO,MAAM;AACX,WAAK,oBAAoB,YAAY,UAAU;AAC/C,WAAK,oBAAoB,QAAQ,MAAM;AAAA,IACzC;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,gBAAgB,CAAC,MAAoB;AACzC,UAAI,EAAE,WAAW,EAAG;AACpB,YAAM,SAAS,EAAE;AACjB,UAAI,EAAE,kBAAkB,SAAU;AAClC,YAAM,MAAM,OAAO,QAAQ,yBAAyB;AACpD,UAAI,CAAC,IAAK;AACV,YAAM,UAAU,IAAI,QAAQ,oBAAoB;AAChD,UAAI,CAAC,QAAS;AACd,YAAM,aAAa,OAAO,SAAS,QAAQ,QAAQ,YAAa,EAAE;AAClE,YAAM,aAAa,IAAI,QAAQ;AAC/B,UAAI,cAAc,KAAM;AACxB,YAAM,cAAc,OAAO,SAAS,YAAY,EAAE;AAClD,YAAM,gBAAgB,OAAO,SAAS,QAAQ,QAAQ,iBAAiB,KAAK,EAAE,KAAK;AACnF,YAAM,SAAS,IAAI,QAAQ,mBAAmB;AAC9C,YAAM,YAAY,SACd,OAAO,SAAS,OAAO,QAAQ,WAAY,EAAE,IAC7C;AACJ,YAAM,SAAS,gBAAgB;AAC/B,QAAE,eAAe;AACjB,aAAO,QAAQ,aAAa;AAAA,QAC1B,QAAQ,EAAE,YAAY,WAAW,OAAO;AAAA,QACxC,OAAO,EAAE,YAAY,WAAW,QAAQ,SAAS,EAAE;AAAA,MACrD,CAAC;AAAA,IACH;AACA,SAAK,iBAAiB,eAAe,aAAa;AAClD,WAAO,MAAM,KAAK,oBAAoB,eAAe,aAAa;AAAA,EACpE,GAAG,CAAC,CAAC;AAIL,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,gBAAgB,CAAC,MAAkB;AACvC,YAAM,SAAS,EAAE;AACjB,UAAI,EAAE,kBAAkB,SAAU;AAClC,UAAI,CAAC,OAAO,QAAQ,yBAAyB,EAAG;AAChD,YAAM,KAAK,UAAU;AACrB,YAAM,MAAM,GAAG,iBAAiB;AAChC,UAAI,CAAC,OAAO,KAAC,8BAAe,IAAI,KAAK,EAAG;AACxC,QAAE,eAAe;AACjB,YAAM,EAAE,OAAO,QAAQ,IAAI,IAAI,MAAM;AAMrC,WAAK,GAAG,eAAe,OAAO,OAAO,EAAE,KAAK,CAAC,MAAM;AACjD,YAAI,CAAC,EAAG;AACR,aAAK,GAAG,cAAc,EAAE,OAAO,EAAE,OAAO;AAAA,MAC1C,CAAC;AAAA,IACH;AACA,SAAK,iBAAiB,YAAY,aAAa;AAC/C,WAAO,MAAM,KAAK,oBAAoB,YAAY,aAAa;AAAA,EACjE,GAAG,CAAC,CAAC;AAML,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,CACX,GACA,MAEA,MAAM,KACL,CAAC,CAAC,KACD,CAAC,CAAC,KACF,EAAE,eAAe,EAAE,cACnB,EAAE,WAAW,EAAE,WACd,EAAE,aAAa,SAAS,EAAE,aAAa;AAC5C,UAAM,MAAM,CAAC,SAA+B;AAC1C,UAAI,KAAK,MAAM,iBAAiB,OAAO,EAAG;AAC1C,uBAAiB,UAAU;AAC3B,uBAAiB,IAAI;AAAA,IACvB;AACA,QAAI,MAAM;AACV,UAAM,SAAS,CAAC,MAAoB;AAClC,UAAI,IAAK;AACT,YAAM,IAAI,EAAE;AACZ,YAAM,IAAI,EAAE;AACZ,YAAM,sBAAsB,MAAM;AAChC,cAAM;AACN,cAAM,OAAO,KAAK;AAAA,UAChB;AAAA,QACF;AACA,YAAI,MAA+B;AACnC,mBAAW,OAAO,MAAM;AACtB,gBAAM,IAAI,IAAI,sBAAsB;AACpC,cAAI,KAAK,EAAE,QAAQ,KAAK,EAAE,SAAS,KAAK,EAAE,OAAO,KAAK,EAAE,QAAQ;AAC9D,kBAAM;AACN;AAAA,UACF;AAAA,QACF;AACA,YAAI,CAAC,KAAK;AACR,cAAI,IAAI;AACR;AAAA,QACF;AACA,cAAM,UAAU,IAAI,QAAqB,oBAAoB;AAC7D,cAAM,aAAa,IAAI,QAAQ;AAC/B,YAAI,CAAC,WAAW,cAAc,MAAM;AAClC,cAAI,IAAI;AACR;AAAA,QACF;AACA,cAAM,aAAa,OAAO,SAAS,QAAQ,QAAQ,YAAa,EAAE;AAClE,cAAM,gBAAgB,OAAO,SAAS,QAAQ,QAAQ,iBAAiB,KAAK,EAAE,KAAK;AACnF,cAAM,SAAS,IAAI,QAAqB,mBAAmB;AAC3D,cAAM,YAAY,SACd,OAAO,SAAS,OAAO,QAAQ,WAAY,EAAE,IAC7C;AACJ,YAAI,EAAE,YAAY,QAAQ,gBAAgB,OAAO,SAAS,YAAY,EAAE,GAAG,UAAU,CAAC;AAAA,MACxF,CAAC;AAAA,IACH;AACA,UAAM,UAAU,MAAM,IAAI,IAAI;AAC9B,SAAK,iBAAiB,eAAe,MAAM;AAC3C,SAAK,iBAAiB,gBAAgB,OAAO;AAC7C,WAAO,MAAM;AACX,WAAK,oBAAoB,eAAe,MAAM;AAC9C,WAAK,oBAAoB,gBAAgB,OAAO;AAChD,UAAI,IAAK,sBAAqB,GAAG;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,CAAC;AAML,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,QAAQ,MAAM;AAClB,UAAI,iBAAiB,SAAS;AAC5B,yBAAiB,UAAU;AAC3B,uBAAe,IAAI;AAAA,MACrB;AAAA,IACF;AACA,UAAM,SAAS,CAAC,MAAkB;AAChC,YAAM,SAAS,EAAE;AACjB,YAAM,IAAI,QAAQ,UAAU,SAAS;AACrC,UAAI,KAAK,KAAK,SAAS,CAAC,GAAG;AACzB,YAAI,iBAAiB,YAAY,EAAG;AACpC,yBAAiB,UAAU;AAC3B,cAAM,IAAI,EAAE,sBAAsB;AAClC,uBAAe;AAAA,UACb,MAAM,EAAE,aAAa,MAAM,KAAK;AAAA,UAChC,MAAM,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,EAAE,QAAQ,OAAO,EAAE,MAAM;AAAA,QACrE,CAAC;AAAA,MACH,OAAO;AACL,cAAM;AAAA,MACR;AAAA,IACF;AACA,SAAK,iBAAiB,aAAa,MAAM;AACzC,SAAK,iBAAiB,cAAc,KAAK;AACzC,SAAK,iBAAiB,UAAU,OAAO,IAAI;AAC3C,WAAO,iBAAiB,UAAU,OAAO,IAAI;AAC7C,WAAO,MAAM;AACX,WAAK,oBAAoB,aAAa,MAAM;AAC5C,WAAK,oBAAoB,cAAc,KAAK;AAC5C,WAAK,oBAAoB,UAAU,OAAO,IAAI;AAC9C,aAAO,oBAAoB,UAAU,OAAO,IAAI;AAAA,IAClD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AAEX,UAAM,UAAU,CAAC,MAAkB;AACjC,YAAM,MAAM,OAAO;AACnB,YAAM,OAAO,WAAW;AACxB,YAAM,KAAK,UAAU;AACrB,YAAM,OAAO,EAAE;AAEf,UAAI,eAAe,WAAW,SAAS,wBAAyB;AAEhE,cAAQ,MAAM;AAAA,QACZ,KAAK,cAAc;AACjB,gBAAM,OAAO,EAAE,QAAQ;AACvB,cAAI,KAAK,SAAS,GAAG;AACnB,cAAE,eAAe;AACjB,0CAAW,KAAK,MAAM,GAAG,oBAAoB,CAAC;AAAA,UAChD;AACA;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AAOtB,YAAE,eAAe;AACjB,cAAI,GAAG,aAAa,MAAM,YAAY;AACpC,0CAAW,KAAK,MAAM,GAAG,oBAAoB,CAAC;AAC9C;AAAA,UACF;AAEA,gBAAM,WAAW,GAAG,iBAAiB;AACrC,cAAI,UAAU;AACZ,kBAAM,MAAM,SAAS,SAAS;AAC9B,gBAAI,aAAa;AAAA,cACf,QAAQ;AAAA,gBACN,YAAY,SAAS;AAAA,gBACrB,WAAW,SAAS;AAAA,gBACpB,QAAQ;AAAA,cACV;AAAA,cACA,OAAO;AAAA,gBACL,YAAY,SAAS;AAAA,gBACrB,WAAW,SAAS;AAAA,gBACpB,QAAQ;AAAA,cACV;AAAA,YACF,CAAC;AAAA,UACH;AACA,cAAI,GAAG,cAAc,EAAG;AACxB,6CAAgB,GAAG;AACnB;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AACtB,YAAE,eAAe;AAIjB,gBAAMC,iBAAgB,GAAG,iBAAiB;AAC1C,cAAIA,gBAAe;AACjB,kBAAM,MAAMA,eAAc,SAAS;AACnC,gBAAI,aAAa;AAAA,cACf,QAAQ;AAAA,gBACN,YAAYA,eAAc;AAAA,gBAC1B,WAAWA,eAAc;AAAA,gBACzB,QAAQ;AAAA,cACV;AAAA,cACA,OAAO;AAAA,gBACL,YAAYA,eAAc;AAAA,gBAC1B,WAAWA,eAAc;AAAA,gBACzB,QAAQ;AAAA,cACV;AAAA,YACF,CAAC;AAAA,UACH;AACA,cAAI,GAAG,cAAc,EAAG;AACxB,6CAAgB,GAAG;AACnB;AAAA,QACF;AAAA,QACA,KAAK,yBAAyB;AAC5B,gBAAM,OAAO,EAAE,QAAQ,EAAE,cAAc,QAAQ,YAAY,KAAK;AAChE,cAAI,KAAK,SAAS,GAAG;AACnB,cAAE,eAAe;AACjB,0CAAW,KAAK,MAAM,GAAG,oBAAoB,CAAC;AAAA,UAChD;AACA;AAAA,QACF;AAAA,QACA,KAAK,mBAAmB;AAKtB,YAAE,eAAe;AACjB;AAAA,QACF;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK,0BAA0B;AAC7B,YAAE,eAAe;AACjB,4CAAe,GAAG;AAClB;AAAA,QACF;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK,yBAAyB;AAC5B,YAAE,eAAe;AACjB,2CAAc,GAAG;AACjB;AAAA,QACF;AAAA,QACA,KAAK,eAAe;AAClB,YAAE,eAAe;AACjB,eAAK,KAAK;AACV;AAAA,QACF;AAAA,QACA,KAAK,eAAe;AAClB,YAAE,eAAe;AACjB,eAAK,KAAK;AACV;AAAA,QACF;AAAA,QACA,SAAS;AACP,YAAE,eAAe;AACjB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK,iBAAiB,eAAe,OAAO;AAC5C,WAAO,MAAM,KAAK,oBAAoB,eAAe,OAAO;AAAA,EAC9D,GAAG,CAAC,CAAC;AAEL,gCAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AAEX,UAAM,aAAa,CAAC,MAA+B;AACjD,YAAM,MAAM,OAAO;AACnB,YAAM,YAAQ,kCAAmB,IAAI,KAAK,IAAI,aAAa,CAAC;AAC5D,UAAI,CAAC,SAAS,CAAC,EAAE,cAAe,QAAO;AACvC,QAAE,cAAc,QAAQ,0BAAY,KAAK,UAAU,KAAK,CAAC;AACzD,QAAE,cAAc,QAAQ,kBAAc,2BAAY,KAAK,CAAC;AACxD,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,CAAC,MAAsB;AACpC,UAAI,WAAW,CAAC,EAAG,GAAE,eAAe;AAAA,IACtC;AAEA,UAAM,QAAQ,CAAC,MAAsB;AAKnC,QAAE,eAAe;AACjB,UAAI,WAAW,CAAC,EAAG,oCAAgB,OAAO,OAAO;AAAA,IACnD;AAEA,UAAM,UAAU,CAAC,MAAsB;AACrC,YAAM,MAAM,OAAO;AACnB,YAAM,KAAK,EAAE;AACb,UAAI,CAAC,GAAI;AAIT,YAAM,OAAO,UAAU,EAAE;AACzB,UAAI,KAAK,SAAS,OAAQ;AAC1B,QAAE,eAAe;AACjB,cAAQ,KAAK,MAAM;AAAA,QACjB,KAAK;AACH,yCAAY,KAAK,KAAK,KAAK;AAC3B;AAAA,QACF,KAAK,QAAQ;AACX,cAAI,CAAC,wBAAwB,KAAK,KAAK,GAAG;AACxC,2CAAY,KAAK,KAAK,KAAK;AAC3B;AAAA,UACF;AAQA,cAAI,iBAAiB,SAAS;AAC5B,oBAAQ;AAAA,cACN;AAAA,YACF;AACA;AAAA,UACF;AACA,2BAAiB,UAAU;AAK3B,gBAAM,oBAAoB,IAAI,aAAa;AAC3C,gBAAM,YAAY;AAChB,gBAAI;AACF,oBAAM,WAAW,MAAM,0BAA0B,KAAK,OAAO,UAAU,QAAQ,WAAW;AA+B1F,oBAAM,gBAAgB,OAAO,QAAQ,aAAa;AAClD,oBAAM,gBAAgB,CAAC,gBAAgB,eAAe,iBAAiB;AAEvE,oBAAM,EAAE,YAAY,UAAU,IAAI,kBAAkB;AACpD,oBAAM,aAAa,OAAO,QAAQ,IAAI,OAAO,YAAY,SAAS;AAClE,kBAAI,WAAW;AACf,kBAAI,CAAC,YAAY;AACf,wBAAQ;AAAA,kBACN;AAAA,gBACF;AACA,sBAAM,YAAY,KAAK,IAAI,GAAG,OAAO,QAAQ,IAAI,WAAW,IAAI,CAAC;AACjE,sBAAM,WAAW,OAAO,QAAQ,IAAI,aAAa,SAAS;AAC1D,+BAAW,kCAAmB,EAAE,YAAY,WAAW,QAAQ,WAAW,SAAS,SAAS,EAAE,CAAC;AAAA,cACjG;AAEA,qBAAO,QAAQ,aAAa,QAAQ;AACpC,6CAAY,OAAO,SAAS,QAAQ;AACpC,kBAAI,eAAe;AACjB,uBAAO,QAAQ,aAAa,aAAa;AAAA,cAC3C;AAAA,YACF,UAAE;AACA,+BAAiB,UAAU;AAAA,YAC7B;AAAA,UACF,GAAG;AACH;AAAA,QACF;AAAA,QACA,KAAK;AACH,gBAAM,YAAY;AAChB,uBAAW,KAAK,KAAK,MAAO,OAAM,UAAU,QAAQ,oBAAoB,CAAC;AAAA,UAC3E,GAAG;AACH;AAAA,QACF,KAAK,QAAQ;AACX,gBAAM,QAAQ,KAAK,KAAK,MAAM,YAAY;AAC1C,gBAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,gBAAI,IAAI,EAAG,oCAAgB,GAAG;AAC9B,gBAAI,KAAK,SAAS,EAAG,+BAAW,KAAK,IAAI;AAAA,UAC3C,CAAC;AACD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK,iBAAiB,QAAQ,MAAM;AACpC,SAAK,iBAAiB,OAAO,KAAK;AAClC,SAAK,iBAAiB,SAAS,OAAO;AACtC,WAAO,MAAM;AACX,WAAK,oBAAoB,QAAQ,MAAM;AACvC,WAAK,oBAAoB,OAAO,KAAK;AACrC,WAAK,oBAAoB,SAAS,OAAO;AAAA,IAC3C;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAY,4BAAY,CAAC,MAAqC;AAClE,UAAM,KAAK,UAAU;AACrB,UAAM,MAAM,EAAE,WAAW,EAAE;AAE3B,QAAI,EAAE,QAAQ,OAAO;AACnB,QAAE,eAAe;AAGjB,UAAI,GAAG,UAAU,GAAG;AAClB,YAAI,EAAE,SAAU,IAAG,eAAe;AAAA,YAC7B,IAAG,eAAe;AACvB;AAAA,MACF;AACA,UAAI,EAAE,SAAU,IAAG,WAAW;AAAA,UACzB,IAAG,WAAW;AACnB;AAAA,IACF;AAUA,QAAI,CAAC,QAAQ,EAAE,QAAQ,UAAU,EAAE,QAAQ,QAAQ;AACjD,YAAM,OAAO,QAAQ;AACrB,YAAM,MAAM,OAAO,aAAa;AAChC,YAAM,KAAM,KAAiG;AAC7G,UAAI,QAAQ,OAAO,IAAI,aAAa,KAAK,OAAO,OAAO,YAAY;AACjE,UAAE,eAAe;AACjB,cAAM,QAAQ,EAAE,WAAW,WAAW;AACtC,cAAM,YAAY,EAAE,QAAQ,SAAS,SAAS;AAC9C,WAAG,KAAK,KAAK,OAAO,WAAW,cAAc;AAC7C,cAAM,WAAW,iBAAiB,IAAI;AACtC,YAAI,SAAU,QAAO,QAAQ,aAAa,QAAQ;AAAA,MACpD;AACA;AAAA,IACF;AAgBA,QAAI,CAAC,QAAQ,EAAE,QAAQ,eAAe,EAAE,QAAQ,WAAW;AASzD,UAAI,eAAe,QAAS;AAC5B,YAAM,YAAY,EAAE,QAAQ,cAAc,aAAa;AACvD,YAAM,kBACJ,GAAG,iBAAiB,KAAK,YAAQ,sCAAuB,OAAO,SAAS,SAAS;AACnF,UAAI,iBAAiB;AACnB,UAAE,eAAe;AACjB,YAAI,cAAc,WAAY,mCAAe,OAAO,OAAO;AAAA,YACtD,kCAAc,OAAO,OAAO;AAAA,MACnC;AACA;AAAA,IACF;AAEA,QAAI,CAAC,IAAK;AACV,UAAM,MAAM,EAAE,IAAI,YAAY;AAE9B,QAAI,CAAC,EAAE,YAAY,CAAC,EAAE,UAAU,QAAQ,KAAK;AAC3C,QAAE,eAAe;AACjB,iBAAW,QAAQ,KAAK;AACxB;AAAA,IACF;AACA,QAAI,CAAC,EAAE,WAAW,QAAQ,OAAQ,EAAE,YAAY,QAAQ,MAAO;AAC7D,QAAE,eAAe;AACjB,iBAAW,QAAQ,KAAK;AACxB;AAAA,IACF;AAEA,QAAI,EAAE,UAAU,CAAC,EAAE,YAAY,UAAU,KAAK,GAAG,GAAG;AAClD,QAAE,eAAe;AACjB,YAAM,IAAI,OAAO,GAAG;AACpB,UAAI,MAAM,EAAG,IAAG,aAAa,WAAW;AAAA,UACnC,IAAG,aAAa,WAAW,EAAE,OAAO,EAA2B,CAAC;AACrE;AAAA,IACF;AAEA,QAAI,EAAE,YAAY,CAAC,EAAE,WAAW,QAAQ,OAAO,QAAQ,MAAM;AAC3D,QAAE,eAAe;AACjB,SAAG,WAAW,QAAQ,MAAM,YAAY,QAAQ;AAChD;AAAA,IACF;AAEA,UAAM,aAAa,CAAC,EAAE,YAAY,CAAC,EAAE,SAChC,kBAAkB,GAAG,IACtB;AACJ,QAAI,YAAY;AACd,QAAE,eAAe;AACjB,SAAG,WAAW,UAAU;AACxB;AAAA,IACF;AACA,QAAI,EAAE,YAAY,CAAC,EAAE,UAAU,QAAQ,KAAK;AAC1C,QAAE,eAAe;AACjB,SAAG,WAAW,QAAQ;AACtB;AAAA,IACF;AAEA,QAAI,CAAC,EAAE,UAAU,CAAC,EAAE,YAAY,QAAQ,KAAK;AAC3C,QAAE,eAAe;AACjB,YAAM,SAAS,GAAG,eAAe;AACjC,YAAM,cACJ,OAAO,QAAQ,OAAO,OAAO,SAAS,YAAY,UAAU,OAAO,OAC9D,OAAO,KAAK,OACb;AACN,WAAK,GAAG,YAAY,WAAW,EAAE,KAAK,CAAC,SAAS;AAC9C,YAAI,SAAS,KAAM;AACnB,YAAI,SAAS,GAAI,IAAG,WAAW,MAAM;AAAA,YAChC,IAAG,QAAQ,QAAQ,EAAE,KAAK,CAAC;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,yBAAqB,4BAAY,MAAM;AAC3C,mBAAe,UAAU;AAAA,EAC3B,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAmB,4BAAY,CAAC,MAA8C;AAClF,mBAAe,UAAU;AACzB,QAAI,EAAE,QAAQ,EAAE,KAAK,SAAS,GAAG;AAC/B,oCAAW,OAAO,SAAS,EAAE,IAAI;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAY,wBAAQ,MAAM,eAAe,SAAS,MAAM,GAAG,CAAC,QAAQ,CAAC;AAQ3E,QAAM,6BAAyB;AAAA,IAC7B,MACE,UACG;AAAA,MAAI,CAAC,MACJ,EAAE,SAAS,SACP,KAAK,EAAE,QAAQ,IAAI,EAAE,MAAM,MAAM,IAAI,EAAE,YAAY,KACnD,KAAK,EAAE,MAAM,IAAI,IAAI,EAAE,MAAM,MAAM,SAAS,EAAE;AAAA,IACpD,EACC,KAAK,GAAG;AAAA,IACb,CAAC,SAAS;AAAA,EACZ;AAQA,QAAM,cAAc,gBAAgB,OAAO,GAAG;AAC9C,QAAM,YAAY,iBAAiB;AACnC,QAAM,OAAqB,YAAa,gBAAgB,UAAW;AACnE,QAAM,YAAY,iBAAiB;AAEnC,QAAM,gBAAgB,OAAO,iBAAiB;AAE9C,QAAM,EAAE,QAAQ,OAAO,gBAAgB,IAAI;AAAA,IACzC;AAAA,IACA;AAAA,IACA,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAKV,CAAC,wBAAwB,YAAY;AAAA,EACvC;AAEA,QAAM,kBAA8B,YAAY,SAAS,kBAAkB,UAAU,MAAM;AAO3F,QAAM,gBAAY;AAAA,IAChB,MACE,YACK;AAAA,MACC,mBAAmB,GAAG,KAAK,KAAK;AAAA,MAChC,oBAAoB,GAAG,KAAK,MAAM;AAAA,MAClC,wBAAwB,GAAG,KAAK,SAAS;AAAA,MACzC,2BAA2B,GAAG,KAAK,YAAY;AAAA,MAC/C,yBAAyB,GAAG,KAAK,UAAU;AAAA,MAC3C,0BAA0B,GAAG,KAAK,WAAW;AAAA,IAC/C,IACA;AAAA,IACN;AAAA,MACE;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAEA,QAAM,OACJ;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,iBAAe;AAAA,MACf,gCAA8B;AAAA,MAC9B,YAAU;AAAA,MACV,aAAY;AAAA,MACZ,gBAAe;AAAA,MACf,MAAK;AAAA,MACL,kBAAe;AAAA,MACf,YACG,aAAa,cAAc,YAAY,oBAAoB;AAAA,MAE9D,yBAAuB,YAAY,kBAAkB;AAAA,MACrD,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA,oBACG,gBAAgB,MAAM,IAAI,CAAC,MAAM,OAAO;AACtC,gBAAM,MAAyB;AAAA,YAC7B,YAAY,KAAK;AAAA,YACjB,WAAW,gBAAgB,MAAM;AAAA,UACnC;AAIA,gBAAM,kBACJ,OAAO,KAAK,KAAK,oBACZ;AAAA,YACC,wBAAwB,GAAG,KAAK,YAAY,KAAK,iBAAiB;AAAA,UACpE,IACA;AACN,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cACV,oBAAkB,IAAI;AAAA,cACtB,mBAAiB,IAAI;AAAA,cACrB,OAAO;AAAA,cAEN;AAAA,oCACC,8CAAC,SAAI,WAAU,kBAAiB,iBAAiB,OAC9C,2BAAiB,GAAG,GACvB;AAAA,gBAEF,8CAAC,SAAI,WAAU,mBACZ,eAAK,MAAM,IAAI,CAAC,SAAS,WAAW,MAAM,SAAS,CAAC,GACvD;AAAA,gBACC,oBACC,8CAAC,SAAI,WAAU,kBAAiB,iBAAiB,OAC9C,2BAAiB,GAAG,GACvB;AAAA;AAAA;AAAA,YAjBG,QAAQ,EAAE;AAAA,UAmBjB;AAAA,QAEJ,CAAC,IACD,UAAU,IAAI,CAAC,MAAM,eAAe,CAAC,CAAC;AAAA,QACzC,iBACC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,YAAY,cAAc;AAAA,YAC1B,QAAQ,cAAc;AAAA,YACtB,WAAW,cAAc;AAAA,YACzB,OAAO,cAAc;AAAA,YACrB,UAAU,CAAC,GAAG,MACZ,OAAO;AAAA,cACL,cAAc;AAAA,cACd,cAAc;AAAA,cACd,EAAE,OAAO,GAAG,QAAQ,EAAE;AAAA,cACtB,cAAc;AAAA,YAChB;AAAA,YAEF,cAAc,CAAC,IAAI,OACjB,OAAO;AAAA,cACL,cAAc;AAAA,cACd,cAAc;AAAA,cACd,EAAE,SAAS,IAAI,SAAS,GAAG;AAAA,cAC3B,cAAc;AAAA,YAChB;AAAA,YAEF,YAAY,CAAC,IAAI,IAAI,OACnB,OAAO;AAAA,cACL;AAAA,gBACE,YAAY,cAAc;AAAA,gBAC1B,QAAQ,cAAc;AAAA,gBACtB,WAAW,cAAc;AAAA,cAC3B;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA;AAAA,QAEJ;AAAA,QAED,iBACC,EACE,iBACA,cAAc,eAAe,cAAc,cAC3C,cAAc,WAAW,cAAc,WACtC,cAAc,aAAa,SAAS,cAAc,aAAa,QAEhE;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,YAAY,cAAc;AAAA,YAC1B,QAAQ,cAAc;AAAA,YACtB,WAAW,cAAc;AAAA,YACzB,UAAU,MACR,aAAa;AAAA,cACX,QAAQ;AAAA,gBACN,YAAY,cAAc;AAAA,gBAC1B,WAAW,cAAc;AAAA,gBACzB,QAAQ,cAAc;AAAA,cACxB;AAAA,cACA,OAAO;AAAA,gBACL,YAAY,cAAc;AAAA,gBAC1B,WAAW,cAAc;AAAA,gBACzB,QAAQ,cAAc,SAAS;AAAA,cACjC;AAAA,YACF,CAAC;AAAA;AAAA,QAEL;AAAA,QAEH,eAAe,YAAY,QAC1B,8CAAC,oBAAiB,MAAM,YAAY,MAAM,MAAM,YAAY,MAAM;AAAA,QAEnE,aACC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA,UAAU;AAAA;AAAA,QACZ;AAAA;AAAA;AAAA,EAEJ;AAGF,SAAO,8CAAC,kBAAe,QAAiB,gBAAK;AAC/C;AA8BO,SAAS,eAAe,UAAyC;AACtE,QAAM,MAAkB,CAAC;AACzB,MAAI,IAAI;AACR,SAAO,IAAI,SAAS,QAAQ;AAC1B,UAAM,QAAQ,SAAS,CAAC;AACxB,QAAI,MAAM,SAAS,YAAY;AAC7B,YAAM,aAAa;AACnB,YAAM,QAAqB,CAAC;AAC5B,YAAM,OAAiB,MAAM,MAAM,aAAa,YAAY,YAAY;AACxE,YAAM,cAAcC,gBAAe,MAAM,MAAM,SAAS;AACxD,YAAM,aAAa,MAAM,MAAM;AAI/B,aAAO,IAAI,SAAS,QAAQ;AAC1B,cAAM,IAAI,SAAS,CAAC;AACpB,YAAI,EAAE,SAAS,WAAY;AAS3B,cAAM,SAASA,gBAAe,EAAE,MAAM,SAAS;AAC/C,YAAI,UAAU,gBAAgB,EAAE,MAAM,YAAY,cAAc,MAAM;AACpE;AAAA,QACF;AACA,YAAI,IAAI,YAAY;AAKlB,cAAI,OAAO,EAAE,MAAM,cAAc,SAAU;AAC3C,gBAAM,YAAY,EAAE,MAAM;AAC1B,cAAI,cAAc,UAAa,cAAc,WAAY;AAAA,QAC3D;AACA,cAAM,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;AACjC;AAAA,MACF;AACA,YAAM,aAAa,MAAM,CAAC,EAAE,MAAM;AAClC,YAAM,eACJ,OAAO,WAAW,cAAc,YAAY,WAAW,YAAY,IAC/D,WAAW,YACX;AACN,UAAI,KAAK;AAAA,QACP,MAAM;AAAA,QACN;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA,WAAW;AAAA,QACX,KAAK;AAAA,MACP,CAAC;AACD;AAAA,IACF;AACA,QAAI,KAAK,EAAE,MAAM,SAAS,OAAO,YAAY,GAAG,KAAK,EAAE,CAAC;AACxD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,MAAgB,WAA2C;AAC7E,QAAM,MAAM,UAAU,KAAK,aAAa;AACxC,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,KAAK,YAAY,IAAI,SAAS,SAAS;AACzC,UAAM,IAAsB;AAAA,MAC1B,OAAO,KAAK,SAAS;AAAA,MACrB,OAAO,KAAK,SAAS;AAAA,MACrB,KAAK,KAAK,SAAS;AAAA,IACrB;AACA,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO,IAAI;AAAA,QACX,OAAO,IAAI;AAAA,QACX,UAAU;AAAA;AAAA,MAHL,GAAG,IAAI,GAAG,KAAK,EAAE,KAAK;AAAA,IAI7B;AAAA,EAEJ;AACA,MAAI,KAAK,iBAAiB,IAAI,SAAS,WAAW,IAAI,MAAM,SAAS,SAAS;AAC5E,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO,IAAI;AAAA,QACX,OAAO,IAAI;AAAA,QACX,eAAe,KAAK;AAAA;AAAA,MAHf,GAAG,IAAI,GAAG,MAAM,KAAK,cAAc,KAAK;AAAA,IAI/C;AAAA,EAEJ;AACA,MAAI,KAAK,gBAAgB,IAAI,SAAS,QAAQ;AAC5C,WAAO,mBAAmB,KAAK,KAAK,YAAY;AAAA,EAClD;AACA,SAAO,eAAe,GAAG;AAC3B;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,IAAI,SAAS,QAAQ;AACvB,UAAM,OAAO,cAAc,IAAI,KAAK;AACpC,WAAO,eAAe,MAAM,KAAK,IAAI,cAAc,QAAQ,IAAI,GAAG,EAAE;AAAA,EACtE;AACA,SAAO,8CAAC,YAAuB,OAAO,IAAI,OAAO,OAAO,IAAI,cAAtC,IAAI,GAA8C;AAC1E;AAOO,SAAS,mBAAmB,KAAkC,MAA0E;AAC7I,QAAM,SAAS,mBAAmB,IAAI,KAAK;AAC3C,QAAM,aAAa,OAAO,KAAK,SAAS;AACxC,QAAM,WAAW,OAAO,KAAK,UAAU,CAAC;AACxC,MAAI,CAAC,cAAc,CAAC,SAAU,QAAO,eAAe,GAAG;AACvD,QAAM,SAAS,IAAI,MAAM,MAAM,WAAW,CAAC,GAAG,SAAS,CAAC,CAAC;AAEzD,QAAM,eAAe,IAAI,eAAe,KAAK;AAC7C,QAAM,MAAM,QAAQ,IAAI,GAAG,KAAK,KAAK,KAAK;AAC1C,QAAM,YAAyC,EAAE,GAAG,KAAK,OAAO,OAAO;AACvE,QAAM,OAAO,cAAc,MAAM;AACjC,SAAO,eAAe,MAAM,WAAW,cAAc,GAAG;AAC1D;AAQO,SAAS,mBAAmB,OAA6C;AAC9E,QAAM,MAA+B,CAAC;AACtC,MAAI,eAAe;AACnB,QAAM,QAAQ,CAAC,IAAI,QAAQ;AACzB,UAAM,QAAQA,gBAAe,GAAG,MAAM,MAAM,SAAS;AACrD,QAAI,UAAU,GAAG;AACf,UAAI,gBAAgB,EAAG,KAAI,KAAK,CAAC,cAAc,GAAG,CAAC;AACnD,qBAAe;AAAA,IACjB;AAAA,EACF,CAAC;AACD,MAAI,gBAAgB,EAAG,KAAI,KAAK,CAAC,cAAc,MAAM,MAAM,CAAC;AAC5D,SAAO;AACT;AAOA,SAAS,cAAc,OAAgC;AACrD,QAAM,QAAoB,CAAC;AAC3B,QAAM,QAAmD,CAAC,EAAE,UAAU,OAAO,OAAO,GAAG,CAAC;AACxF,aAAW,MAAM,OAAO;AACtB,UAAM,QAAQA,gBAAe,GAAG,MAAM,MAAM,SAAS;AACrD,WAAO,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,MAAO,OAAM,IAAI;AAC7E,UAAM,OAAiB,EAAE,OAAO,IAAI,UAAU,CAAC,EAAE;AACjD,UAAM,MAAM,SAAS,CAAC,EAAE,SAAS,KAAK,IAAI;AAC1C,UAAM,KAAK,EAAE,UAAU,KAAK,UAAU,MAAM,CAAC;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,eACP,MACA,KACA,OACA,KACa;AACb,QAAM,YAAY,mBAAmB,IAAI,QAAQ;AACjD,QAAM,QAAyC,IAAI,YAC/C,EAAE,eAAe,IAAI,UAAU,IAC/B;AACJ,QAAM,QAAQ,KAAK,IAAI,CAAC,SACtB,8CAAC,YAAgC,OAAO,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,OACzE,eAAK,SAAS,SAAS,IACpB,qBAAqB,KAAK,UAAU,UAAU,KAAK,MAAM,KAAK,EAAE,IAChE,QAHS,KAAK,MAAM,KAI1B,CACD;AACD,MAAI,IAAI,aAAa,WAAW;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,kBAAe;AAAA,QACf,OAAO,QAAQ,IAAI,QAAQ;AAAA,QAC3B;AAAA,QAEC;AAAA;AAAA,MANI;AAAA,IAOP;AAAA,EAEJ;AACA,SACE,8CAAC,QAAa,WAAsB,kBAAe,UAAS,OACzD,mBADM,GAET;AAEJ;AAeA,SAAS,qBAAqB,MAAkB,KAA0B;AACxE,QAAM,OAAiB,KAAK,CAAC,GAAG,MAAM,MAAM,MAAM,aAAa,YAAY,YAAY;AACvF,QAAM,MAAM,SAAS,YAAY,OAAO;AACxC,SACE,8CAAC,OAAc,WAAW,mBAAmB,IAAI,IAAI,kBAAgB,MAClE,eAAK,IAAI,CAAC,SACT,8CAAC,YAAgC,OAAO,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,OACzE,eAAK,SAAS,SAAS,IACpB,qBAAqB,KAAK,UAAU,UAAU,KAAK,MAAM,KAAK,EAAE,IAChE,QAHS,KAAK,MAAM,KAI1B,CACD,KAPO,GAQV;AAEJ;AAEA,SAASA,gBAAe,GAAoB;AAC1C,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AACzD,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC;AAC/C;AAEA,IAAM,oBAA8C;AAAA,EAClD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAYA,SAAS,oBAAoB,GAAW,GAAyB;AAC/D,QAAM,MAAM;AACZ,MAAI,OAAO,IAAI,wBAAwB,YAAY;AACjD,WAAO,IAAI,oBAAoB,GAAG,CAAC;AAAA,EACrC;AACA,MAAI,OAAO,IAAI,2BAA2B,YAAY;AACpD,UAAM,MAAM,IAAI,uBAAuB,GAAG,CAAC;AAC3C,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,QAAQ,SAAS,YAAY;AACnC,UAAM,SAAS,IAAI,YAAY,IAAI,MAAM;AACzC,UAAM,SAAS,IAAI;AACnB,WAAO;AAAA,EACT;AACA,SAAO;AACT;;;A0Bx8CA,IAAAC,iBASO;AAEP,IAAAC,gBAAoE;;;AC0C1D,IAAAC,uBAAA;AA1CH,IAAM,iBAIP;AAAA,EACJ,EAAE,KAAK,OAAO,OAAO,SAAS,OAAO,SAAI;AAAA,EACzC,EAAE,KAAK,SAAS,OAAO,kBAAe,OAAO,SAAI;AAAA,EACjD,EAAE,KAAK,cAAc,OAAO,qBAAkB,OAAO,SAAI;AAAA,EACzD,EAAE,KAAK,YAAY,OAAO,mBAAgB,OAAO,SAAI;AAAA,EACrD,EAAE,KAAK,QAAQ,OAAO,WAAW,OAAO,SAAI;AAC9C;AAGO,IAAM,oBAAoB;AAkB1B,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuC;AACrC,SACE,+CAAC,SAAI,WAAU,mBACb;AAAA,kDAAC,SAAI,WAAU,qBACZ,yBAAe,IAAI,CAAC,MACnB;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,gBAAc,WAAW,EAAE;AAAA,QAC3B,OAAO,EAAE;AAAA,QACT,SAAS,CAAC,MAAkB;AAC1B,YAAE,eAAe;AACjB,mBAAS,EAAE,GAAG;AAAA,QAChB;AAAA,QAEA;AAAA,wDAAC,UAAK,WAAU,mBAAkB,eAAW,MAC1C,YAAE,OACL;AAAA,UACC,EAAE;AAAA;AAAA;AAAA,MAbE,EAAE;AAAA,IAcT,CACD,GACH;AAAA,IACA,8CAAC,QAAG;AAAA,IACJ,+CAAC,WAAM,WAAU,oCACf;AAAA,oDAAC,UAAK,WAAU,qBAAoB,eAAW,MAAC,OAAO,EAAE,YAAY,MAAM,GAAG;AAAA,MAAE;AAAA,MAEhF;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,UACvC,cAAW;AAAA;AAAA,MACb;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,CAAC,MAAkB;AAC1B,YAAE,eAAe;AACjB,uBAAa;AAAA,QACf;AAAA,QACD;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;;;ADiKQ,IAAAC,uBAAA;AAtOR,IAAM,gBAAgB,CAAC,OAAO;AAC9B,IAAM,aAAa,CAAC,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;AASjG,IAAM,gBAA+B;AAAA,EACnC,EAAE,OAAO,aAAa,OAAO,gBAAa,MAAM,YAAY;AAAA,EAC5D,EAAE,OAAO,MAAM,OAAO,eAAY,MAAM,WAAW,OAAO,EAAE;AAAA,EAC5D,EAAE,OAAO,MAAM,OAAO,eAAY,MAAM,WAAW,OAAO,EAAE;AAAA,EAC5D,EAAE,OAAO,MAAM,OAAO,eAAY,MAAM,WAAW,OAAO,EAAE;AAAA,EAC5D,EAAE,OAAO,MAAM,OAAO,eAAY,MAAM,WAAW,OAAO,EAAE;AAAA,EAC5D,EAAE,OAAO,MAAM,OAAO,eAAY,MAAM,WAAW,OAAO,EAAE;AAAA,EAC5D,EAAE,OAAO,MAAM,OAAO,eAAY,MAAM,WAAW,OAAO,EAAE;AAAA,EAC5D,EAAE,OAAO,cAAc,OAAO,iBAAW,MAAM,aAAa;AAAA,EAC5D,EAAE,OAAO,aAAa,OAAO,aAAU,MAAM,YAAY;AAC3D;AAEA,IAAM,gBAAuE;AAAA,EAC3E,EAAE,OAAO,QAAQ,OAAO,UAAK,OAAO,wBAAqB;AAAA,EACzD,EAAE,OAAO,UAAU,OAAO,UAAK,OAAO,cAAc;AAAA,EACpD,EAAE,OAAO,SAAS,OAAO,UAAK,OAAO,uBAAoB;AAAA,EACzD,EAAE,OAAO,WAAW,OAAO,UAAK,OAAO,aAAa;AACtD;AAEA,IAAM,qBAAqB,oBAAI,IAAgB,CAAC,QAAQ,UAAU,OAAO,CAAC;AAU1E,IAAM,sBAAsB,oBAAI,IAAI,CAAC,UAAU,SAAS,QAAQ,CAAC;AAE1D,SAAS,gBAAgB,GAAgC;AAC9D,QAAM,KAAK;AACX,SAAO,CAAC,CAAC,MAAM,oBAAoB,IAAI,GAAG,OAAO;AACnD;AAOO,SAAS,kBAAkB,GAAgC;AAChE,QAAM,KAAK;AACX,SAAO,CAAC,CAAC,OAAO,oBAAoB,IAAI,GAAG,OAAO,KAAK,GAAG,YAAY;AACxE;AAEA,IAAM,uBAA+E;AAAA,EACnF,EAAE,OAAO,UAAU,OAAO,UAAK,OAAO,6BAA6B;AAAA,EACnE,EAAE,OAAO,aAAa,OAAO,UAAK,OAAO,8BAA2B;AAAA,EACpE,EAAE,OAAO,cAAc,OAAO,UAAK,OAAO,+BAA4B;AAAA,EACtE,EAAE,OAAO,UAAU,OAAO,gBAAM,OAAO,oBAAiB;AAAA,EACxD,EAAE,OAAO,SAAS,OAAO,gBAAM,OAAO,qBAAqB;AAC7D;AAMO,SAAS,QAAQ,EAAE,UAAU,GAA8B;AAChE,QAAM,SAAS,iBAAiB;AAChC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,SAAS,eAAe;AAC9B,QAAM,YAAY,aAAa;AAC/B,QAAM,aAAa,aAAa,OAAO;AACvC,QAAM,aAAa,SAAS;AAC5B,QAAM,WAAW,aAAa,KAAK;AAKnC,QAAM,OAAO,CAAC,MAAkB;AAC9B,QAAI,gBAAgB,EAAE,MAAM,EAAG;AAC/B,MAAE,eAAe;AAAA,EACnB;AAEA,QAAM,WAAW,CAAC,SAAmB,CAAC,MAAkB;AACtD,MAAE,eAAe;AACjB,eAAW,IAAI;AAAA,EACjB;AAEA,QAAM,gBAAgB,CAAC,MAAqC;AAC1D,YAAQ,SAAS,EAAE,OAAO,KAAK;AAAA,EACjC;AAEA,QAAM,oBAAoB,CAAC,MAAqC;AAC9D,YAAQ,aAAa,EAAE,OAAO,KAAK;AAAA,EACrC;AAEA,QAAM,qBAAqB,CAAC,MAAsC;AAChE,UAAM,IAAI,EAAE,OAAO;AACnB,QAAI,MAAM,GAAI,YAAW,YAAY;AAAA,QAChC,SAAQ,cAAc,CAAC;AAAA,EAC9B;AAEA,QAAM,mBAAmB,CAAC,MAAsC;AAC9D,UAAM,IAAI,EAAE,OAAO;AACnB,QAAI,MAAM,GAAI,YAAW,UAAU;AAAA,QAC9B,SAAQ,YAAY,CAAC;AAAA,EAC5B;AAEA,QAAM,gBAAgB,CAAC,MAAsC;AAC3D,UAAM,MAAM,cAAc,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK;AAChE,QAAI,CAAC,IAAK;AACV,iBAAa,IAAI,MAAM,IAAI,UAAU,SAAY,EAAE,OAAO,IAAI,MAAM,IAAI,MAAS;AAAA,EACnF;AAEA,QAAM,UAAU,CAAC,UAAsB,CAAC,MAAkB;AACxD,MAAE,eAAe;AAIjB,UAAMC,iBAAgB,OAAO,iBAAiB;AAC9C,QAAIA,kBAAiB,mBAAmB,IAAI,KAAK,GAAG;AAClD,aAAO;AAAA,QACLA,eAAc;AAAA,QACdA,eAAc;AAAA,QACd,EAAE,OAAO,MAAqC;AAAA,QAC9CA,eAAc;AAAA,MAChB;AACA;AAAA,IACF;AACA,aAAS,KAAK;AAAA,EAChB;AAEA,QAAM,yBAAqB;AAAA,IACzB,CAAC,MAAkB;AACjB,QAAE,eAAe;AACjB,oBAAc,SAAS,MAAM;AAAA,IAC/B;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,yBAAqB;AAAA,IACzB,CAAC,MAAqC;AACpC,YAAM,OAAO,EAAE,OAAO,QAAQ,CAAC;AAC/B,UAAI,KAAM,MAAK,OAAO,oBAAoB,IAAI;AAC9C,QAAE,OAAO,QAAQ;AAAA,IACnB;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,oBAAgB,uBAAyB,IAAI;AAEnD,QAAM,cAAc,CAAC,MAAkB;AACrC,MAAE,eAAe;AACjB,UAAM,OAAiB,aAAa,QAAQ,QAAQ;AACpD,iBAAa,OAAO,IAAI;AAAA,EAC1B;AAEA,QAAM,aAAa,aAAa,MAAM;AACtC,QAAM,kBAAc;AAAA,IAClB,CAAC,MAAkB;AACjB,QAAE,eAAe;AACjB,YAAM,cACJ,OAAO,QAAQ,OAAO,OAAO,SAAS,YAAY,UAAU,OAAO,OAC9D,OAAO,KAAK,OACb;AACN,WAAK,OAAO,YAAY,WAAW,EAAE,KAAK,CAAC,SAAS;AAClD,YAAI,SAAS,KAAM;AACnB,YAAI,SAAS,GAAI,YAAW,MAAM;AAAA,YAC7B,SAAQ,QAAQ,EAAE,KAAK,CAAC;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,IACA,CAAC,OAAO,MAAM,QAAQ,YAAY,OAAO;AAAA,EAC3C;AAEA,QAAM,aAAa,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAErE,QAAM,iBAAiB,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AACjF,QAAM,kBAAkB,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;AACpF,QAAM,gBAAgB,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;AAC9E,QAAM,aAAa,iBAAiB,WAAW,UAAU;AAIzD,QAAM,gBAAgB,OAAO,iBAAiB;AAC9C,QAAM,qBAAkC,gBACnC,cAAc,MAAM,UAAU,WAC/B;AAEJ,QAAM,qBAAqB,CAAC,WAAwB,CAAC,MAAkB;AACrE,MAAE,eAAe;AACjB,UAAM,MAAM,OAAO,iBAAiB;AACpC,QAAI,CAAC,IAAK;AAGV,UAAM,QACJ,YAAY,IAAI,MAAM,UAAU,YAC5B,EAAE,SAAS,GAAG,SAAS,EAAE,IACzB,CAAC;AACP,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,EAAE,QAAQ,GAAG,MAAM;AAAA,MACnB,IAAI;AAAA,IACN;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,aAAa;AAAA,MACxB,MAAK;AAAA,MACL,cAAW;AAAA,MACX,aAAa;AAAA,MAEb;AAAA,sDAAC,SACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,YACP,UAAU;AAAA,YACV,aAAa;AAAA,YACb,cAAW;AAAA,YAEV;AAAA,6BAAe,MAAM,8CAAC,YAAO,OAAM,IAAG,4BAAS;AAAA,cAC/C,cAAc,IAAI,CAAC,MAClB,8CAAC,YAAqB,OAAO,EAAE,OAAQ,YAAE,SAA5B,EAAE,KAAgC,CAChD;AAAA;AAAA;AAAA,QACH,GACF;AAAA,QAEA,+CAAC,SACC;AAAA,wDAAC,cAAW,OAAM,qBAAe,SAAS,aAAa,MAAM,GAAG,SAAS,SAAS,MAAM,GACtF,wDAAC,YAAO,eAAC,GACX;AAAA,UACA,8CAAC,cAAW,OAAM,wBAAe,SAAS,aAAa,QAAQ,GAAG,SAAS,SAAS,QAAQ,GAC1F,wDAAC,QAAG,eAAC,GACP;AAAA,UACA,8CAAC,cAAW,OAAM,wBAAkB,SAAS,aAAa,WAAW,GAAG,SAAS,SAAS,WAAW,GACnG,wDAAC,OAAE,eAAC,GACN;AAAA,UACA,8CAAC,cAAW,OAAM,2BAAgB,SAAS,aAAa,QAAQ,GAAG,SAAS,SAAS,QAAQ,GAC3F,wDAAC,OAAE,eAAC,GACN;AAAA,WACF;AAAA,QAEA,+CAAC,SACC;AAAA,yDAAC,WAAM,WAAU,oBACf;AAAA,0DAAC,UAAK,WAAU,qBAAoB,eAAW,MAAC,OAAO,EAAE,YAAY,WAAW,GAAG;AAAA,YACnF,8CAAC,WAAM,MAAK,SAAQ,OAAO,YAAY,UAAU,eAAe,aAAa,MAAM,cAAW,gBAAe;AAAA,aAC/G;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AACjB,2BAAW,OAAO;AAAA,cACpB;AAAA,cACD;AAAA;AAAA,UAED;AAAA,UACA,+CAAC,WAAM,WAAU,oBAAmB,OAAM,eACxC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,eAAW;AAAA,gBACX,OAAO,EAAE,YAAY,eAAe;AAAA;AAAA,YACtC;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU;AAAA,gBACV,aAAa;AAAA,gBACb,cAAW;AAAA;AAAA,YACb;AAAA,aACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AACjB,2BAAW,WAAW;AAAA,cACxB;AAAA,cACD;AAAA;AAAA,UAED;AAAA,WACF;AAAA,QAEA,+CAAC,SACC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,cACP,UAAU;AAAA,cACV,aAAa;AAAA,cACb,cAAW;AAAA,cAEX;AAAA,8DAAC,YAAO,OAAM,IAAG,yBAAM;AAAA,gBACtB,cAAc,IAAI,CAAC,MAClB,8CAAC,YAAe,OAAO,GAAI,eAAd,CAAgB,CAC9B;AAAA;AAAA;AAAA,UACH;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,cACP,UAAU;AAAA,cACV,aAAa;AAAA,cACb,cAAW;AAAA,cAEX;AAAA,8DAAC,YAAO,OAAM,IAAG,2BAAQ;AAAA,gBACxB,WAAW,IAAI,CAAC,MACf,8CAAC,YAAe,OAAO,GAAI,eAAd,CAAgB,CAC9B;AAAA;AAAA;AAAA,UACH;AAAA,WACF;AAAA,QAEA,8CAAC,SACE,wBAAc,IAAI,CAAC,MAClB;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,OAAO,EAAE;AAAA,YACT,gBAAc,eAAe,EAAE;AAAA,YAC/B,aAAa;AAAA,YACb,SAAS,QAAQ,EAAE,KAAK;AAAA,YAEvB,YAAE;AAAA;AAAA,UARE,EAAE;AAAA,QAST,CACD,GACH;AAAA,QAEA,8CAAC,SACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,gBAAc,aAAa;AAAA,YAC3B,OAAO,aAAa,QAAQ,mBAAmB;AAAA,YAC/C,aAAa;AAAA,YACb,SAAS;AAAA,YAER,uBAAa,QAAQ,QAAQ;AAAA;AAAA,QAChC,GACF;AAAA,QAEA,+CAAC,SACC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,gBAAc,aAAa,QAAQ;AAAA,cACnC,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AACjB,2BAAW,QAAQ;AAAA,cACrB;AAAA,cACD;AAAA;AAAA,UAED;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,gBAAc,aAAa,SAAS;AAAA,cACpC,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AACjB,2BAAW,SAAS;AAAA,cACtB;AAAA,cACD;AAAA;AAAA,UAED;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AACjB,2BAAW;AAAA,cACb;AAAA,cACD;AAAA;AAAA,UAED;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AACjB,2BAAW;AAAA,cACb;AAAA,cACD;AAAA;AAAA,UAED;AAAA,WACF;AAAA,QAEA,8CAAC,SACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,gBAAc;AAAA,YACd,OAAO,aAAa,2BAAsB;AAAA,YAC1C,aAAa;AAAA,YACb,SAAS;AAAA,YACV;AAAA;AAAA,QAED,GACF;AAAA,QAEA,+CAAC,SACC;AAAA,wDAAC,aAAU;AAAA,UACX,8CAAC,mBAAgB;AAAA,WACnB;AAAA,QAEA,+CAAC,SACC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS;AAAA,cACV;AAAA;AAAA,UAED;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,QAAO;AAAA,cACP,QAAM;AAAA,cACN,UAAU;AAAA;AAAA,UACZ;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AAOjB,qBAAK,OACF,eAAe,EACf,KAAK,CAAC,MAAO,IAAI,OAAO,cAAc,EAAE,OAAO,EAAE,OAAO,IAAI,MAAU,EACtE,MAAM,CAAC,QAAQ;AACd,0BAAQ,MAAM,gCAA6B,GAAG;AAAA,gBAChD,CAAC;AAAA,cACL;AAAA,cACD;AAAA;AAAA,UAED;AAAA,WACF;AAAA,QAEC,iBACC,+CAAC,SACE;AAAA,+BAAqB,IAAI,CAAC,QACzB;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAO,IAAI;AAAA,cACX,gBAAc,uBAAuB,IAAI;AAAA,cACzC,aAAa;AAAA,cACb,SAAS,mBAAmB,IAAI,KAAK;AAAA,cAEpC,cAAI;AAAA;AAAA,YARA,IAAI;AAAA,UASX,CACD;AAAA,cACA,8BAAe,cAAc,KAAK,IACjC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AAQjB,oBAAI,KAAC,8BAAe,cAAc,KAAK,EAAG;AAC1C,sBAAM,IAAI,cAAc,MAAM;AAK9B,qBAAK,OAAO,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM;AACzD,sBAAI,CAAC,EAAG;AACR,uBAAK,OAAO,cAAc,EAAE,OAAO,EAAE,OAAO;AAAA,gBAC9C,CAAC;AAAA,cACH;AAAA,cACD;AAAA;AAAA,UAED,IAEA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAM;AAAA,cACN,gBAAc,CAAC,CAAC,cAAc,MAAM;AAAA,cACpC,aAAa;AAAA,cACb,SAAS,CAAC,MAAM;AACd,kBAAE,eAAe;AACjB,sBAAM,iBAAiB,cAAc,MAAM,WAAW;AACtD,sBAAM,eAAe,cAAc,MAAM,gBAAgB;AACzD,qBAAK,OAAO,oBAAoB,gBAAgB,YAAY,EAAE,KAAK,CAAC,WAAW;AAC7E,sBAAI,WAAW,KAAM;AACrB,wBAAM,QAAQ,OAAO,QAAQ,WAAW;AACxC,yBAAO;AAAA,oBACL,cAAc;AAAA,oBACd,cAAc;AAAA,oBACd;AAAA,sBACE,SAAS,QAAQ,SAAY,OAAO;AAAA,sBACpC,cAAc,QAAQ,SAAY,OAAO;AAAA,oBAC3C;AAAA,oBACA,cAAc;AAAA,kBAChB;AAAA,gBACF,CAAC;AAAA,cACH;AAAA,cACD;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA,QAGF,8CAAC,SACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,OAAM;AAAA,YACN,aAAa;AAAA,YACb,SAAS,CAAC,MAAM;AACd,gBAAE,eAAe;AACjB,mBAAK,OAAO,kBAAkB;AAAA,YAChC;AAAA,YACD;AAAA;AAAA,QAED,GACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAUA,SAAS,kBAA+B;AACtC,QAAM,SAAS,iBAAiB;AAChC,QAAM,CAAC,MAAM,OAAO,QAAI,yBAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,QAAI,yBAAS,CAAC;AACpC,QAAM,CAAC,SAAS,UAAU,QAAI,yBAA4B,GAAG;AAC7D,QAAM,cAAU,uBAAuB,IAAI;AAE3C,gCAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,UAAM,SAAS,CAAC,MAA6B;AAC3C,UAAI,CAAC,QAAQ,QAAS;AACtB,UAAI,QAAQ,QAAQ,SAAS,EAAE,MAAc,EAAG;AAChD,cAAQ,KAAK;AAAA,IACf;AACA,aAAS,iBAAiB,aAAa,MAAM;AAC7C,WAAO,MAAM,SAAS,oBAAoB,aAAa,MAAM;AAAA,EAC/D,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,+BAAiB,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC;AAE7E,SACE,+CAAC,SAAI,KAAK,SAAS,WAAU,wBAC3B;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAM;AAAA,QACN,gBAAc;AAAA,QACd,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,QACrC,SAAS,CAAC,MAAM;AACd,YAAE,eAAe;AACjB,kBAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,QACnB;AAAA,QACD;AAAA;AAAA,IAED;AAAA,IACC,QACC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,aAAa,CAAC,MAAM;AAClB,cAAI,CAAC,kBAAkB,EAAE,MAAM,EAAG,GAAE,eAAe;AAAA,QACrD;AAAA,QAEA;AAAA,yDAAC,WAAM,WAAU,oBAAmB;AAAA;AAAA,YAElC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,KAAK;AAAA,gBACL,KAAK;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU,CAAC,MAAM,SAAS,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,gBAChD,cAAW;AAAA;AAAA,YACb;AAAA,aACF;AAAA,UACA,+CAAC,WAAM,WAAU,oBAAmB;AAAA;AAAA,YAElC;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO,OAAO,OAAO;AAAA,gBACrB,UAAU,CAAC,MAAM,WAAW,OAAO,EAAE,OAAO,KAAK,CAAsB;AAAA,gBACvE,cAAW;AAAA,gBAEX;AAAA,gEAAC,YAAO,OAAM,KAAI,qBAAO;AAAA,kBACzB,8CAAC,YAAO,OAAM,OAAM,iBAAG;AAAA,kBACvB,8CAAC,YAAO,OAAM,KAAI,mBAAK;AAAA;AAAA;AAAA,YACzB;AAAA,aACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM;AACb,wBAAQ,KAAK;AACb,uBAAO,kBAAkB,SAAS,OAAO;AAAA,cAC3C;AAAA,cACD;AAAA;AAAA,gBACU;AAAA,gBAAQ;AAAA,gBAAO,UAAU,IAAI,MAAM;AAAA;AAAA;AAAA,UAC9C;AAAA;AAAA;AAAA,IACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,YAAyB;AAChC,QAAM,SAAS,iBAAiB;AAChC,QAAM,UAAU,OAAO,UAAU;AACjC,QAAM,CAAC,MAAM,OAAO,QAAI,yBAAS,KAAK;AACtC,QAAM,CAAC,aAAa,cAAc,QAAI,yBAAS,KAAK;AACpD,QAAM,cAAU,uBAAuB,IAAI;AAE3C,gCAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,UAAM,SAAS,CAAC,MAA6B;AAC3C,UAAI,CAAC,QAAQ,QAAS;AACtB,UAAI,QAAQ,QAAQ,SAAS,EAAE,MAAc,EAAG;AAChD,cAAQ,KAAK;AAAA,IACf;AACA,aAAS,iBAAiB,aAAa,MAAM;AAC7C,WAAO,MAAM,SAAS,oBAAoB,aAAa,MAAM;AAAA,EAC/D,GAAG,CAAC,IAAI,CAAC;AAGT,gCAAU,MAAM;AACd,QAAI,CAAC,KAAM,gBAAe,KAAK;AAAA,EACjC,GAAG,CAAC,IAAI,CAAC;AAET,SACE,+CAAC,SAAI,KAAK,SAAS,WAAU,wBAC3B;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAM;AAAA,QACN,gBAAc;AAAA,QACd,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,QACrC,SAAS,CAAC,MAAM;AACd,YAAE,eAAe;AACjB,kBAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,QACnB;AAAA,QACD;AAAA;AAAA,IAED;AAAA,IACC,QACC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,aAAa,CAAC,MAAM;AAElB,cAAI,CAAC,kBAAkB,EAAE,MAAM,EAAG,GAAE,eAAe;AAAA,QACrD;AAAA,QAEA;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,QAAQ,CAAC,MAAM,SAAS;AACtB,wBAAQ,KAAK;AACb,uBAAO,YAAY,MAAM,IAAI;AAAA,cAC/B;AAAA;AAAA,UACF;AAAA,UACC,WACC,+CAAC,SAAI,WAAU,oBACb;AAAA,0DAAC,QAAG;AAAA,YACJ,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,eAAe;AAAA,YAAG,GAAG,iCAEnF;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,eAAe;AAAA,YAAG,GAAG,kCAEnF;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,iBAAiB;AAAA,YAAG,GAAG,0CAErF;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,kBAAkB;AAAA,YAAG,GAAG,yCAEtF;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,iBAAiB;AAAA,YAAG,GAAG,2BAErF;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,oBAAoB;AAAA,YAAG,GAAG,4BAExF;AAAA,YACA,8CAAC,QAAG;AAAA,YACH,OAAO,sBAAsB,KAC5B;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM;AAAE,0BAAQ,KAAK;AAAG,yBAAO,eAAe;AAAA,gBAAG;AAAA,gBAC3D;AAAA;AAAA,YAED;AAAA,YAEF,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,WAAW;AAAA,YAAG,GAAG,gDAE/E;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,UAAU;AAAA,YAAG,GAAG,0CAE9E;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,UAAU;AAAA,YAAG,GAAG,0CAE9E;AAAA,YACA,8CAAC,QAAG;AAAA,YACJ,+CAAC,WAAM,WAAU,qCACf;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,eAAW;AAAA,kBACX,OAAO,EAAE,YAAY,OAAO,kBAAkB,KAAK,UAAU;AAAA;AAAA,cAC/D;AAAA,cAAE;AAAA,cAEF;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,OAAO,OAAO,kBAAkB,KAAK;AAAA,kBACrC,UAAU,CAAC,MAAM,OAAO,kBAAkB,EAAE,OAAO,KAAK;AAAA,kBACxD,cAAW;AAAA;AAAA,cACb;AAAA,eACF;AAAA,YACA,8CAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAQ,KAAK;AAAG,qBAAO,kBAAkB,IAAI;AAAA,YAAG,GAAG,kCAE1F;AAAA,YACA,8CAAC,QAAG;AAAA,YACJ;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,gBAAc;AAAA,gBACd,iBAAe;AAAA,gBACf,OAAM;AAAA,gBACN,cAAW;AAAA,gBACX,SAAS,CAAC,MAAM;AACd,oBAAE,eAAe;AACjB,iCAAe,CAAC,MAAM,CAAC,CAAC;AAAA,gBAC1B;AAAA,gBAEC;AAAA;AAAA,YACH;AAAA,YACC,eACC;AAAA,cAAC;AAAA;AAAA,gBACC,QAAQ,OAAO,qBAAqB;AAAA,gBACpC,OAAO,OAAO,oBAAoB,KAAK;AAAA,gBACvC,UAAU,CAAC,MAAM,OAAO,qBAAqB,CAAC;AAAA,gBAC9C,SAAS,CAAC,MAAM,OAAO,oBAAoB,CAAC;AAAA,gBAC5C,cAAc,MAAM,OAAO,oBAAoB,IAAI;AAAA;AAAA,YACrD;AAAA,YAEF,8CAAC,QAAG;AAAA,YACJ;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,MAAM;AAAE,0BAAQ,KAAK;AAAG,yBAAO,YAAY;AAAA,gBAAG;AAAA,gBACxD;AAAA;AAAA,YAED;AAAA,aACF;AAAA;AAAA;AAAA,IAEJ;AAAA,KAEJ;AAEJ;AAEA,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AAMxB,SAAS,gBAAgB,EAAE,OAAO,GAAsC;AACtE,QAAM,CAAC,OAAO,QAAQ,QAAI,yBAA0C,IAAI;AACxE,QAAM,QAAQ,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAM,MAAM,IAAI,CAAC,KAAK;AAC1D,SACE,+CAAC,SAAI,WAAU,mBACb;AAAA,kDAAC,SAAI,WAAU,yBAAyB,iBAAM;AAAA,IAC9C;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,qBAAqB,UAAU,eAAe;AAAA,UAC9C,kBAAkB,UAAU,eAAe;AAAA,QAC7C;AAAA,QACA,cAAc,MAAM,SAAS,IAAI;AAAA,QAEhC,gBAAM;AAAA,UAAK,EAAE,QAAQ,gBAAgB;AAAA,UAAG,CAAC,GAAG,MAC3C,MAAM,KAAK,EAAE,QAAQ,gBAAgB,GAAG,CAACC,IAAG,MAAM;AAChD,kBAAM,SAAS,SAAS,QAAQ,KAAK,MAAM,KAAK,KAAK,MAAM;AAC3D,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,WAAW,uBAAuB,SAAS,YAAY,EAAE;AAAA,gBACzD,cAAc,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC;AAAA,gBACrC,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,gBACrC,SAAS,CAAC,MAAM;AACd,oBAAE,eAAe;AACjB,yBAAO,IAAI,GAAG,IAAI,CAAC;AAAA,gBACrB;AAAA,gBACA,cAAY,GAAG,IAAI,CAAC,SAAM,IAAI,CAAC;AAAA;AAAA,cAT1B,GAAG,CAAC,IAAI,CAAC;AAAA,YAUhB;AAAA,UAEJ,CAAC;AAAA,QACH;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,SAAS,iBACP,MACA,OACQ;AACR,MAAI,SAAS,QAAS,QAAO;AAC7B,MAAI,SAAS,WAAW;AACtB,QAAI,OAAO,UAAU,YAAY,SAAS,KAAK,SAAS,EAAG,QAAO,IAAI,KAAK;AAC3E,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,MAAM,EAAE,SAAS,GAAyC;AACjE,SAAO,8CAAC,SAAI,WAAU,oBAAoB,UAAS;AACrD;AASA,SAAS,WAAW,EAAE,OAAO,SAAS,SAAS,SAAS,GAAiC;AACvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,gBAAc;AAAA,MACd;AAAA,MACA,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,MACrC;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":["import_react","import_core","import_core","import_core","import_jsx_runtime","import_react","import_react","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_react","import_core","import_react","import_core","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_jsx_runtime","clampLevel","_","import_react","currentPage","import_react","import_react","import_core","yText","cmdToggleMark","cmdSetMark","cmdRemoveMark","cmdSetBlockType","cmdSetBlockAttr","cmdSetCellAttr","cmdSetBlockAttrAtIndex","cmdToggleList","cmdIndentList","cmdDedentList","cmdSplitListItem","cmdInsertTable","cmdInsertAnswerLines","cmdDeleteTable","cmdInsertRow","cmdDeleteRow","cmdInsertColumn","cmdDeleteColumn","cmdMoveNextCell","cmdMovePrevCell","cmdMergeRight","cmdMergeDown","cmdSplitCell","cmdSetColumnBoundary","cmdSetTableWidth","cmdSetRowHeight","cmdSetRowHeights","cmdMergeSelection","cmdInsertImage","cmdSetImageAttrs","cmdMoveEmbedAnchor","import_jsx_runtime","selectedEmbed","clampListLevel","import_react","import_core","import_jsx_runtime","import_jsx_runtime","selectedEmbed","_"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Editor.tsx","../src/dom-bridge.ts","../src/pastePlan.ts","../src/htmlToSlice.ts","../src/imageConstraints.ts","../src/resolvePastedImages.ts","../src/BehindImageSelectAffordance.tsx","../src/LinkHoverTooltip.tsx","../src/platform.ts","../src/EditorContext.tsx","../src/LinkDialog.tsx","../src/dialogCenterStyle.ts","../src/PageConfigDialog.tsx","../src/ImageCaptionDialog.tsx","../src/FormulaDialog.tsx","../src/formulaSnippet.ts","../src/formulaGuarda.ts","../src/ImageResizeOverlay.tsx","../src/RemoteCursorsOverlay.tsx","../src/NodeView.tsx","../src/renderInline.tsx","../src/sliceDelta.ts","../src/TableResizeOverlay.tsx","../src/usePagination.ts","../src/useEditor.ts","../src/usePageSettings.ts","../src/Toolbar.tsx","../src/TableBorderPanel.tsx"],"sourcesContent":["export { Editor } from \"./Editor\";\nexport type { EditorProps, PageRenderContext, PageRenderProp } from \"./Editor\";\nexport { RemoteCursorsOverlay } from \"./RemoteCursorsOverlay\";\nexport { useEditor } from \"./useEditor\";\nexport type {\n LinkRequest,\n PendingMarks,\n UseEditorOptions,\n UseEditorResult,\n} from \"./useEditor\";\nexport { LinkDialog } from \"./LinkDialog\";\nexport { PageConfigDialog } from \"./PageConfigDialog\";\nexport { ImageCaptionDialog } from \"./ImageCaptionDialog\";\nexport { FormulaDialog } from \"./FormulaDialog\";\nexport { usePageSettings } from \"./usePageSettings\";\nexport type {\n CaptionAlign,\n ImageCaptionRequest,\n ImageCaptionResult,\n PageConfigRequest,\n} from \"./useEditor\";\nexport { NodeView } from \"./NodeView\";\nexport { renderInline } from \"./renderInline\";\nexport { EditorProvider, useEditorContext } from \"./EditorContext\";\nexport type { EditorProviderProps } from \"./EditorContext\";\nexport { Toolbar } from \"./Toolbar\";\nexport type { ToolbarProps } from \"./Toolbar\";\nexport {\n A4_PAGE,\n contentHeight,\n contentWidth,\n defaultPageLayout,\n usePageLayout,\n} from \"./usePagination\";\nexport type {\n BlockFragment,\n PageEntry,\n PageGeometry,\n PageLayout,\n PageLayoutResult,\n PageSlot,\n PaginationPhase,\n TableRowFragment,\n} from \"./usePagination\";\nexport { sliceDelta, deltaLength } from \"./sliceDelta\";\nexport type { NodeViewFragment } from \"./NodeView\";\nexport * from \"./dom-bridge\";\nexport { htmlToSlice } from \"./htmlToSlice\";\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n type KeyboardEvent,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport {\n collapsedSelection,\n deleteBackward,\n deleteForward,\n deleteSelection,\n encodeSelection,\n insertParagraph,\n insertSlice,\n insertText,\n isEmbedAdjacentToCaret,\n isFormulaEmbed,\n serializeSelection,\n sliceToText,\n SOFER_MIME,\n type CommandContext,\n type ListKind,\n type MarkName,\n type SerializedBlock,\n} from \"@sofereditor/core\";\nimport { applyDomSelection, isTableRectSelection, readDomSelection, selectionsEqual } from \"./dom-bridge\";\nimport { planPaste } from \"./pastePlan\";\nimport { resolvePastedImageUploads, sliceHasDataImageEmbeds } from \"./resolvePastedImages\";\nimport { BehindImageSelectAffordance } from \"./BehindImageSelectAffordance\";\nimport { LinkHoverTooltip } from \"./LinkHoverTooltip\";\nimport { EditorProvider } from \"./EditorContext\";\nimport { ImageResizeOverlay } from \"./ImageResizeOverlay\";\nimport {\n RemoteCursorsOverlay,\n type AwarenessLike,\n} from \"./RemoteCursorsOverlay\";\nimport { NodeView, type NodeViewFragment } from \"./NodeView\";\nimport {\n A4_PAGE,\n defaultPageLayout,\n usePageLayout,\n type PageGeometry,\n type PageLayout,\n type PageSlot,\n} from \"./usePagination\";\nimport { useEditor, type UseEditorResult } from \"./useEditor\";\nimport { usePageSettings } from \"./usePageSettings\";\n\nexport interface PageRenderContext {\n pageNumber: number;\n pageCount: number;\n}\n\nexport type PageRenderProp = (ctx: PageRenderContext) => ReactNode;\n\nexport interface EditorProps {\n editor?: UseEditorResult;\n className?: string;\n /**\n * Page geometry override. When **omitted**, the editor reads page settings\n * from `editor.doc` (the `docSettings` Y.Map populated via `setPageSettings`\n * or `import-docx`). When **set**, this value wins for rendering — but is\n * NOT written back to the Y.Doc (view-only override; useful when the host\n * app must pin a size regardless of doc state, e.g. a prova generator).\n * Pass **null** to disable pagination entirely (flat flow).\n */\n pageGeometry?: PageGeometry | null;\n /**\n * Custom page header. Receives `{ pageNumber, pageCount }` and renders inside\n * the top margin of every page (above the content area). Pass `null` to omit.\n */\n renderPageHeader?: PageRenderProp | null;\n /**\n * Custom page footer. Receives `{ pageNumber, pageCount }`. Defaults to\n * `\"<n> / <total>\"` centered. Pass `null` to omit.\n */\n renderPageFooter?: PageRenderProp | null;\n /**\n * Awareness do Yjs (de `useCollab().binding.awareness`). Quando presente,\n * o editor publica a seleção local e desenha os cursores/seleções dos outros\n * usuários (presença colaborativa). Omitir = sem cursores remotos.\n */\n awareness?: AwarenessLike;\n}\n\nconst defaultFooter: PageRenderProp = ({ pageNumber, pageCount }) =>\n `${pageNumber} / ${pageCount}`;\n\nexport function Editor({\n editor: providedEditor,\n className,\n pageGeometry,\n renderPageHeader,\n renderPageFooter = defaultFooter,\n awareness,\n}: EditorProps): JSX.Element {\n const internal = useEditor();\n const editor = providedEditor ?? internal;\n const { doc, history, snapshot, getSelection, setSelection } = editor;\n\n const rootRef = useRef<HTMLDivElement>(null);\n const isComposingRef = useRef(false);\n // B4: cobre a colagem de HTML com imagem `data:` pendente de upload (ver\n // `onPaste`, ramo \"html\"). `true` do instante em que o upload começa até a\n // colagem terminar de ser inserida. Sem isso, uma segunda colagem disparada\n // DURANTE a janela do upload (comum: rede escolar lenta + \"não aconteceu\n // nada visualmente\" faz o professor colar de novo) capturaria a MESMA\n // posição que a primeira, e ao resolver empurraria a primeira colagem —\n // sem nenhum indicador de carregamento pra explicar por que. Descarta a\n // segunda em vez de enfileirar: mais simples, e uma colagem perdida com\n // aviso no console é melhor que duas colagens se atropelando na mesma\n // posição do documento.\n const pasteInFlightRef = useRef(false);\n // The `behind` image the pointer is currently hovering (by geometry), so we\n // can show a click affordance to select it through the text on top of it.\n const [hoveredBehind, setHoveredBehind] = useState<{\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n } | null>(null);\n const hoveredBehindRef = useRef<typeof hoveredBehind>(null);\n // The link the pointer is hovering — drives the URL tooltip (rect in viewport\n // coords). Tracked by element identity to avoid re-render churn on every move.\n const [hoveredLink, setHoveredLink] = useState<{\n href: string;\n rect: { top: number; left: number; bottom: number; width: number };\n } | null>(null);\n const hoveredLinkElRef = useRef<HTMLAnchorElement | null>(null);\n const suppressSelectionSyncRef = useRef(false);\n\n // Presença colaborativa: publica a seleção local no awareness (throttle por\n // rAF). Outros editores desenham nosso caret via RemoteCursorsOverlay.\n useEffect(() => {\n if (!awareness) return;\n const root = rootRef.current;\n if (!root) return;\n let raf: number | null = null;\n const publish = () => {\n raf = null;\n const sel = readDomSelection(root);\n awareness.setLocalStateField(\n \"cursor\",\n sel ? encodeSelection(doc, sel) ?? null : null,\n );\n };\n const onSelChange = () => {\n const s = document.getSelection();\n if (!s || s.rangeCount === 0) return;\n // Só publica quando a seleção está dentro do nosso editor.\n if (s.anchorNode && !root.contains(s.anchorNode)) return;\n if (raf != null) return;\n raf = requestAnimationFrame(publish);\n };\n const onBlur = () => awareness.setLocalStateField(\"cursor\", null);\n document.addEventListener(\"selectionchange\", onSelChange);\n root.addEventListener(\"blur\", onBlur, true);\n return () => {\n document.removeEventListener(\"selectionchange\", onSelChange);\n root.removeEventListener(\"blur\", onBlur, true);\n if (raf != null) cancelAnimationFrame(raf);\n awareness.setLocalStateField(\"cursor\", null);\n };\n }, [awareness, doc]);\n\n const ctxRef = useRef<CommandContext>({ doc, getSelection, setSelection });\n ctxRef.current = { doc, getSelection, setSelection };\n const historyRef = useRef(history);\n historyRef.current = history;\n const editorRef = useRef(editor);\n editorRef.current = editor;\n\n // Run after every render — not just when selection or snapshot change — so\n // we re-apply the caret after pagination's two-phase render cycle (which\n // mutates `layout` state without touching `editor.selection` or `snapshot`,\n // and tears down/rebuilds the text nodes the prior DOM selection was attached\n // to). The early-return when DOM already matches the model keeps this cheap.\n useLayoutEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n if (isComposingRef.current) return;\n if (document.activeElement !== root) return;\n const current = readDomSelection(root);\n if (current && selectionsEqual(current, editor.selection)) return;\n if (isTableRectSelection(editor.selection)) {\n // Cross-cell (rectangular) table selection: the native DOM selection is\n // intentionally empty — TableView paints the highlight via CSS classes.\n // Keep it empty but do NOT arm the suppress guard. The guard exists to\n // ignore render-collapsed carets while TYPING; for a rect selection the\n // empty native selection is the steady state, so arming it would make the\n // selectionchange handler ignore a genuine click-away — the rect would\n // stick in the model and the next keystroke would overwrite the selected\n // cells instead of writing at the clicked caret.\n applyDomSelection(root, editor.selection);\n suppressSelectionSyncRef.current = false;\n return;\n }\n // DOM caret diverged from the model — typically a re-render replaced the\n // text node and collapsed the native caret to offset 0. Suppress the\n // selectionchange handler until the DOM settles back to the model; otherwise\n // a render-induced selectionchange writes the collapsed caret over a newer\n // keystroke's selection and the caret jumps backward when typing fast.\n // Cleared deterministically in the selectionchange handler once the DOM\n // settles to the live model — NOT on a timer, because timers race the event.\n suppressSelectionSyncRef.current = true;\n applyDomSelection(root, editor.selection);\n });\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const handler = () => {\n if (isComposingRef.current) return;\n if (document.activeElement !== root) {\n // Focus left the editor — nothing to capture here. Release any pending\n // suppression so a stuck flag can't swallow the next genuine change.\n suppressSelectionSyncRef.current = false;\n return;\n }\n const modelSel = readDomSelection(root);\n if (suppressSelectionSyncRef.current) {\n // Mid-reconciliation: a render moved the caret. Resume capturing user\n // selection only once the DOM caret has settled to match the LIVE model\n // (our applyDomSelection landed). Transient render-collapsed carets\n // (DOM != model) are ignored — those are the spurious events that used\n // to clobber the model selection and make the caret jump back.\n if (modelSel && selectionsEqual(modelSel, editorRef.current.getSelection())) {\n suppressSelectionSyncRef.current = false;\n }\n return;\n }\n if (modelSel) setSelection(modelSel);\n };\n document.addEventListener(\"selectionchange\", handler);\n return () => document.removeEventListener(\"selectionchange\", handler);\n }, [setSelection]);\n\n // ---------- Sub-phase 4.5: rectangular table-cell drag ----------\n //\n // mousedown captures the anchor cell. mousemove flips into \"rect mode\" the\n // moment the pointer enters a DIFFERENT cell of the same table, at which\n // point we suppress the native cross-cell selection and write a rectangular\n // selection to the model. The visual highlight is drawn by TableView via\n // CSS classes (see isTableRectSelection in dom-bridge).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n interface DragState {\n blockIndex: number;\n anchorCellIndex: number;\n anchorTableEl: HTMLTableElement;\n rectActive: boolean;\n }\n let drag: DragState | null = null;\n\n function cellFromTarget(target: EventTarget | null): {\n blockIndex: number;\n cellIndex: number;\n tableEl: HTMLTableElement;\n } | null {\n if (!(target instanceof Node)) return null;\n let n: Node | null = target;\n let cellEl: HTMLElement | null = null;\n let tableEl: HTMLTableElement | null = null;\n while (n && n !== root) {\n if (n.nodeType === Node.ELEMENT_NODE) {\n const e = n as HTMLElement;\n if (!cellEl && e.dataset.cellIndex != null) cellEl = e;\n if (e.tagName === \"TABLE\" && e.dataset.blockIndex != null) {\n tableEl = e as HTMLTableElement;\n break;\n }\n }\n n = n.parentNode;\n }\n if (!cellEl || !tableEl) return null;\n const cellIndex = Number.parseInt(cellEl.dataset.cellIndex!, 10);\n const blockIndex = Number.parseInt(tableEl.dataset.blockIndex!, 10);\n return { blockIndex, cellIndex, tableEl };\n }\n\n const onPointerDown = (e: PointerEvent) => {\n if (e.button !== 0) return;\n const hit = cellFromTarget(e.target);\n if (!hit) {\n drag = null;\n return;\n }\n drag = {\n blockIndex: hit.blockIndex,\n anchorCellIndex: hit.cellIndex,\n anchorTableEl: hit.tableEl,\n rectActive: false,\n };\n };\n\n const onPointerMove = (e: PointerEvent) => {\n if (!drag) return;\n // Only treat as drag when the primary button is still pressed.\n if ((e.buttons & 1) === 0) {\n drag = null;\n return;\n }\n const hit = cellFromTarget(e.target);\n if (!hit) return;\n if (hit.blockIndex !== drag.blockIndex) return; // never cross tables\n if (hit.cellIndex === drag.anchorCellIndex && !drag.rectActive) return;\n\n if (!drag.rectActive) {\n drag.rectActive = true;\n // Kill any in-progress native cross-cell selection.\n window.getSelection()?.removeAllRanges();\n }\n e.preventDefault();\n ctxRef.current.setSelection({\n anchor: { blockIndex: drag.blockIndex, cellIndex: drag.anchorCellIndex, offset: 0 },\n focus: { blockIndex: drag.blockIndex, cellIndex: hit.cellIndex, offset: 0 },\n });\n };\n\n const onPointerUp = () => {\n drag = null;\n };\n\n root.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"pointermove\", onPointerMove);\n document.addEventListener(\"pointerup\", onPointerUp);\n return () => {\n root.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"pointermove\", onPointerMove);\n document.removeEventListener(\"pointerup\", onPointerUp);\n };\n }, []);\n\n // ---------- Fase 5: drag-drop de imagens ----------\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onDragOver = (e: DragEvent) => {\n if (!e.dataTransfer) return;\n const hasFiles = Array.from(e.dataTransfer.items ?? []).some(\n (i) => i.kind === \"file\",\n );\n if (!hasFiles) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"copy\";\n };\n const onDrop = (e: DragEvent) => {\n if (!e.dataTransfer) return;\n const images = Array.from(e.dataTransfer.files).filter((f) =>\n f.type.startsWith(\"image/\"),\n );\n if (images.length === 0) return;\n e.preventDefault();\n // Position the caret at the drop point so the insertion lands there.\n const range = caretRangeFromPoint(e.clientX, e.clientY);\n if (range) {\n const dom = window.getSelection();\n dom?.removeAllRanges();\n dom?.addRange(range);\n const sel = readDomSelection(root);\n if (sel) ctxRef.current.setSelection(sel);\n }\n void (async () => {\n for (const f of images) await editorRef.current.insertImageFromFile(f);\n })();\n };\n root.addEventListener(\"dragover\", onDragOver);\n root.addEventListener(\"drop\", onDrop);\n return () => {\n root.removeEventListener(\"dragover\", onDragOver);\n root.removeEventListener(\"drop\", onDrop);\n };\n }, []);\n\n // ---------- Fase 5: clique numa imagem seleciona o embed ----------\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onPointerDown = (e: PointerEvent) => {\n if (e.button !== 0) return;\n const target = e.target;\n if (!(target instanceof Element)) return;\n const img = target.closest('img[data-embed=\"image\"]') as HTMLElement | null;\n if (!img) return;\n const blockEl = img.closest(\"[data-block-index]\") as HTMLElement | null;\n if (!blockEl) return;\n const blockIndex = Number.parseInt(blockEl.dataset.blockIndex!, 10);\n const offsetAttr = img.dataset.embedOffset;\n if (offsetAttr == null) return;\n const localOffset = Number.parseInt(offsetAttr, 10);\n const fragmentStart = Number.parseInt(blockEl.dataset.fragmentStart ?? \"0\", 10) || 0;\n const cellEl = img.closest(\"[data-cell-index]\") as HTMLElement | null;\n const cellIndex = cellEl\n ? Number.parseInt(cellEl.dataset.cellIndex!, 10)\n : undefined;\n const offset = fragmentStart + localOffset;\n e.preventDefault();\n ctxRef.current.setSelection({\n anchor: { blockIndex, cellIndex, offset },\n focus: { blockIndex, cellIndex, offset: offset + 1 },\n });\n };\n root.addEventListener(\"pointerdown\", onPointerDown);\n return () => root.removeEventListener(\"pointerdown\", onPointerDown);\n }, []);\n\n // Duplo clique numa fórmula reabre o modal com o LaTeX guardado. Em imagem\n // comum não faz nada — `isFormulaEmbed` é o que separa os dois.\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onDoubleClick = (e: MouseEvent) => {\n const target = e.target;\n if (!(target instanceof Element)) return;\n if (!target.closest('img[data-embed=\"image\"]')) return;\n const ed = editorRef.current;\n const sel = ed.getSelectedEmbed();\n if (!sel || !isFormulaEmbed(sel.embed)) return;\n e.preventDefault();\n const { latex, display } = sel.embed.formula;\n // A restauração da seleção do embed através do round-trip do modal\n // (bug #6) mora dentro de `insertFormula` (`useEditor.ts`) — ela é\n // quem tem o `await`, então é a única que consegue segurar a seleção\n // através do gap assíncrono. Fazer isso aqui de novo seria duplicação\n // sem efeito (e enganosa: pareceria proteger o gap que só o hook vê).\n void ed.requestFormula(latex, display).then((r) => {\n if (!r) return;\n void ed.insertFormula(r.latex, r.display);\n });\n };\n root.addEventListener(\"dblclick\", onDoubleClick);\n return () => root.removeEventListener(\"dblclick\", onDoubleClick);\n }, []);\n\n // Hover detection for `behind` images: they sit at z-index:-1, so the text on\n // top intercepts clicks and the image can't be selected by clicking there. On\n // pointer move we find — BY GEOMETRY — the behind image under the pointer and\n // expose a select affordance (rendered below). Throttled to one check/frame.\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const same = (\n a: typeof hoveredBehind,\n b: typeof hoveredBehind,\n ): boolean =>\n a === b ||\n (!!a &&\n !!b &&\n a.blockIndex === b.blockIndex &&\n a.offset === b.offset &&\n (a.cellIndex ?? -1) === (b.cellIndex ?? -1));\n const set = (next: typeof hoveredBehind) => {\n if (same(next, hoveredBehindRef.current)) return;\n hoveredBehindRef.current = next;\n setHoveredBehind(next);\n };\n let raf = 0;\n const onMove = (e: PointerEvent) => {\n if (raf) return;\n const x = e.clientX;\n const y = e.clientY;\n raf = requestAnimationFrame(() => {\n raf = 0;\n const imgs = root.querySelectorAll<HTMLImageElement>(\n 'img[data-embed=\"image\"][data-embed-layout=\"behind\"]',\n );\n let hit: HTMLImageElement | null = null;\n for (const img of imgs) {\n const r = img.getBoundingClientRect();\n if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) {\n hit = img;\n break;\n }\n }\n if (!hit) {\n set(null);\n return;\n }\n const blockEl = hit.closest<HTMLElement>(\"[data-block-index]\");\n const offsetAttr = hit.dataset.embedOffset;\n if (!blockEl || offsetAttr == null) {\n set(null);\n return;\n }\n const blockIndex = Number.parseInt(blockEl.dataset.blockIndex!, 10);\n const fragmentStart = Number.parseInt(blockEl.dataset.fragmentStart ?? \"0\", 10) || 0;\n const cellEl = hit.closest<HTMLElement>(\"[data-cell-index]\");\n const cellIndex = cellEl\n ? Number.parseInt(cellEl.dataset.cellIndex!, 10)\n : undefined;\n set({ blockIndex, offset: fragmentStart + Number.parseInt(offsetAttr, 10), cellIndex });\n });\n };\n const onLeave = () => set(null);\n root.addEventListener(\"pointermove\", onMove);\n root.addEventListener(\"pointerleave\", onLeave);\n return () => {\n root.removeEventListener(\"pointermove\", onMove);\n root.removeEventListener(\"pointerleave\", onLeave);\n if (raf) cancelAnimationFrame(raf);\n };\n }, []);\n\n // Link hover tooltip: show the URL while the pointer is over a link. Uses\n // `mouseover` delegation (fires on element enter, far cheaper than mousemove)\n // and tracks the hovered <a> by identity to avoid re-render churn. Cleared on\n // leave/scroll (the cached viewport rect would otherwise go stale).\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const clear = () => {\n if (hoveredLinkElRef.current) {\n hoveredLinkElRef.current = null;\n setHoveredLink(null);\n }\n };\n const onOver = (e: MouseEvent) => {\n const target = e.target as Element | null;\n const a = target?.closest?.(\"a[href]\") as HTMLAnchorElement | null;\n if (a && root.contains(a)) {\n if (hoveredLinkElRef.current === a) return;\n hoveredLinkElRef.current = a;\n const r = a.getBoundingClientRect();\n setHoveredLink({\n href: a.getAttribute(\"href\") ?? \"\",\n rect: { top: r.top, left: r.left, bottom: r.bottom, width: r.width },\n });\n } else {\n clear();\n }\n };\n root.addEventListener(\"mouseover\", onOver);\n root.addEventListener(\"mouseleave\", clear);\n root.addEventListener(\"scroll\", clear, true);\n window.addEventListener(\"scroll\", clear, true);\n return () => {\n root.removeEventListener(\"mouseover\", onOver);\n root.removeEventListener(\"mouseleave\", clear);\n root.removeEventListener(\"scroll\", clear, true);\n window.removeEventListener(\"scroll\", clear, true);\n };\n }, []);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n const handler = (e: InputEvent) => {\n const ctx = ctxRef.current;\n const hist = historyRef.current;\n const ed = editorRef.current;\n const type = e.inputType;\n\n if (isComposingRef.current && type === \"insertCompositionText\") return;\n\n switch (type) {\n case \"insertText\": {\n const data = e.data ?? \"\";\n if (data.length > 0) {\n e.preventDefault();\n insertText(ctx, data, ed.consumePendingMarks());\n }\n return;\n }\n case \"insertLineBreak\": {\n // Shift+Enter inside a listItem inserts a soft line break (`\\n`)\n // INSIDE the item instead of splitting it into a new numbered entry\n // — useful for spacing between question lines without bumping the\n // numbering. Other blocks fall through to the regular paragraph\n // split (= same as Enter), because TipTap-style \"soft break\" inside\n // a paragraph isn't part of this model.\n e.preventDefault();\n if (ed.getBlockType() === \"listItem\") {\n insertText(ctx, \"\\n\", ed.consumePendingMarks());\n return;\n }\n // Fall through to the same path as insertParagraph.\n const selEmbed = ed.getSelectedEmbed();\n if (selEmbed) {\n const off = selEmbed.offset + 1;\n ctx.setSelection({\n anchor: {\n blockIndex: selEmbed.blockIndex,\n cellIndex: selEmbed.cellIndex,\n offset: off,\n },\n focus: {\n blockIndex: selEmbed.blockIndex,\n cellIndex: selEmbed.cellIndex,\n offset: off,\n },\n });\n }\n if (ed.splitListItem()) return;\n insertParagraph(ctx);\n return;\n }\n case \"insertParagraph\": {\n e.preventDefault();\n // If an embed is the entire selection, drop the caret RIGHT AFTER it\n // before splitting — otherwise the default \"delete range then insert\n // paragraph\" path would remove the image.\n const selectedEmbed = ed.getSelectedEmbed();\n if (selectedEmbed) {\n const off = selectedEmbed.offset + 1;\n ctx.setSelection({\n anchor: {\n blockIndex: selectedEmbed.blockIndex,\n cellIndex: selectedEmbed.cellIndex,\n offset: off,\n },\n focus: {\n blockIndex: selectedEmbed.blockIndex,\n cellIndex: selectedEmbed.cellIndex,\n offset: off,\n },\n });\n }\n if (ed.splitListItem()) return;\n insertParagraph(ctx);\n return;\n }\n case \"insertReplacementText\": {\n const text = e.data ?? e.dataTransfer?.getData(\"text/plain\") ?? \"\";\n if (text.length > 0) {\n e.preventDefault();\n insertText(ctx, text, ed.consumePendingMarks());\n }\n return;\n }\n case \"insertFromPaste\": {\n // Paste é tratado pelo handler `paste` (useEffect de clipboard). Quando\n // ele NÃO faz preventDefault (ex.: clipboard só com text/html, ou sem\n // clipboardData), o browser cai aqui — este no-op bloqueia a inserção\n // crua no DOM em vez de corromper o documento virtual.\n e.preventDefault();\n return;\n }\n case \"deleteContentBackward\":\n case \"deleteWordBackward\":\n case \"deleteSoftLineBackward\":\n case \"deleteHardLineBackward\": {\n e.preventDefault();\n deleteBackward(ctx);\n return;\n }\n case \"deleteContentForward\":\n case \"deleteWordForward\":\n case \"deleteSoftLineForward\":\n case \"deleteHardLineForward\": {\n e.preventDefault();\n deleteForward(ctx);\n return;\n }\n case \"historyUndo\": {\n e.preventDefault();\n hist.undo();\n return;\n }\n case \"historyRedo\": {\n e.preventDefault();\n hist.redo();\n return;\n }\n default: {\n e.preventDefault();\n return;\n }\n }\n };\n\n root.addEventListener(\"beforeinput\", handler);\n return () => root.removeEventListener(\"beforeinput\", handler);\n }, []);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n const writeSlice = (e: ClipboardEvent): boolean => {\n const ctx = ctxRef.current;\n const slice = serializeSelection(ctx.doc, ctx.getSelection());\n if (!slice || !e.clipboardData) return false;\n e.clipboardData.setData(SOFER_MIME, JSON.stringify(slice));\n e.clipboardData.setData(\"text/plain\", sliceToText(slice));\n return true;\n };\n\n const onCopy = (e: ClipboardEvent) => {\n if (writeSlice(e)) e.preventDefault();\n };\n\n const onCut = (e: ClipboardEvent) => {\n // Always block the native cut: it mutates the contenteditable DOM directly\n // and would write its own clipboard serialization. When we have a slice we\n // delete via the model; an unsupported selection (writeSlice false) becomes\n // a clean no-op instead of a \"cut that doesn't delete\".\n e.preventDefault();\n if (writeSlice(e)) deleteSelection(ctxRef.current);\n };\n\n const onPaste = (e: ClipboardEvent) => {\n const ctx = ctxRef.current;\n const cd = e.clipboardData;\n if (!cd) return;\n // A escolha do ramo (e sobretudo a ORDEM deles) vive em `planPaste`, para\n // ser testável sem montar o editor. Ver o comentário lá sobre por que o\n // HTML precisa vir antes dos arquivos de imagem.\n const plan = planPaste(cd);\n if (plan.kind === \"none\") return;\n e.preventDefault();\n switch (plan.kind) {\n case \"sofer\":\n insertSlice(ctx, plan.slice);\n return;\n case \"html\": {\n if (!sliceHasDataImageEmbeds(plan.slice)) {\n insertSlice(ctx, plan.slice);\n return;\n }\n // B4: upload de imagem já em andamento. Sem NENHUM indicador de\n // carregamento na UI, \"não aconteceu nada visualmente\" (rede\n // escolar lenta) é exatamente o gatilho que leva o professor a\n // colar de novo — e a segunda colagem capturaria a MESMA posição\n // abaixo, empurrando a primeira ao resolver. Descarta com aviso em\n // vez de enfileirar: enfileirar só adiaria a pergunta de cima de\n // QUAL posição (já potencialmente obsoleta) encadear a próxima.\n if (pasteInFlightRef.current) {\n console.warn(\n \"[sofereditor] colagem com imagem ainda subindo — descartando esta colagem concorrente para não corromper a posição de inserção\",\n );\n return;\n }\n pasteInFlightRef.current = true;\n // Slice tem embed(s) `data:` (Google Docs) — sobe pro storage\n // configurado (`uploadImage`) ANTES de inserir, pra não gravar\n // ~300KB de base64 direto no Y.Doc. `htmlToSlice` continua pura: só\n // emite o `data:`, a subida é feita aqui (ver `resolvePastedImages.ts`).\n const capturedSelection = ctx.getSelection();\n void (async () => {\n try {\n const resolved = await resolvePastedImageUploads(plan.slice, editorRef.current.uploadImage);\n // A subida é assíncrona — o mundo pode ter mudado nesse meio\n // tempo. O editor é colaborativo: quem deslocou tudo pode ter\n // sido o próprio professor OU um par remoto via Hocuspocus\n // editando acima, não só digitação local. Dois riscos (B5),\n // tratados separadamente:\n //\n // (a) o bloco capturado pode ter sumido (Ctrl+A+Backspace local,\n // ou uma edição remota que apagou/moveu blocos). `insertSlice`\n // faz um `return` silencioso quando o bloco alvo não existe mais\n // — a colagem desapareceria sem erro, sem log, e o `data:`\n // original já foi descartado junto. Em vez de deixar sumir\n // calado: loga e insere no fim do documento como último\n // recurso.\n //\n // (b) restaurar `capturedSelection` INCONDICIONALMENTE depois de\n // inserir é o dano em si: se o professor se moveu enquanto\n // esperava (clicou em outro lugar, ou está digitando — local ou\n // remotamente), arrancar o caret de volta pro ponto da colagem\n // no meio da digitação dele é pior que o problema original. Por\n // isso a seleção viva é lida ANTES de mexer em qualquer coisa, e\n // só é restaurada quando de fato mudou da capturada; do\n // contrário `insertSlice` já deixa o caret logo depois do\n // conteúdo colado — o comportamento esperado do caso comum (o\n // professor esperou o upload terminar sem se mover).\n //\n // Limitação conhecida (mesma classe da anterior): a seleção\n // restaurada é uma posição ABSOLUTA, não uma `Y.RelativePosition`\n // — se a inserção deslocou blocos ANTES da posição do usuário, o\n // índice restaurado pode ficar levemente errado. Migrar pra\n // RelativePosition fecharia isso; fora de escopo aqui.\n const liveSelection = ctxRef.current.getSelection();\n const userMovedAway = !selectionsEqual(liveSelection, capturedSelection);\n\n const { blockIndex, cellIndex } = capturedSelection.focus;\n const targetText = ctxRef.current.doc.textAt(blockIndex, cellIndex);\n let insertAt = capturedSelection;\n if (!targetText) {\n console.error(\n \"[sofereditor] bloco de destino da colagem sumiu durante o upload da imagem — inserindo no fim do documento em vez de descartar a colagem\",\n );\n const lastIndex = Math.max(0, ctxRef.current.doc.blockCount() - 1);\n const lastText = ctxRef.current.doc.getBlockText(lastIndex);\n insertAt = collapsedSelection({ blockIndex: lastIndex, offset: lastText ? lastText.length : 0 });\n }\n\n ctxRef.current.setSelection(insertAt);\n insertSlice(ctxRef.current, resolved);\n if (userMovedAway) {\n ctxRef.current.setSelection(liveSelection);\n }\n } finally {\n pasteInFlightRef.current = false;\n }\n })();\n return;\n }\n case \"images\":\n void (async () => {\n for (const f of plan.files) await editorRef.current.insertImageFromFile(f);\n })();\n return;\n case \"text\": {\n const lines = plan.text.split(/\\r\\n|\\r|\\n/);\n lines.forEach((line, i) => {\n if (i > 0) insertParagraph(ctx);\n if (line.length > 0) insertText(ctx, line);\n });\n return;\n }\n }\n };\n\n root.addEventListener(\"copy\", onCopy);\n root.addEventListener(\"cut\", onCut);\n root.addEventListener(\"paste\", onPaste);\n return () => {\n root.removeEventListener(\"copy\", onCopy);\n root.removeEventListener(\"cut\", onCut);\n root.removeEventListener(\"paste\", onPaste);\n };\n }, []);\n\n const onKeyDown = useCallback((e: KeyboardEvent<HTMLDivElement>) => {\n const ed = editorRef.current;\n const mod = e.metaKey || e.ctrlKey;\n\n if (e.key === \"Tab\") {\n e.preventDefault();\n // Inside a table, Tab/Shift+Tab navigates between cells; at the last\n // cell Tab appends a new row.\n if (ed.isInTable()) {\n if (e.shiftKey) ed.moveToPrevCell();\n else ed.moveToNextCell();\n return;\n }\n if (e.shiftKey) ed.dedentList();\n else ed.indentList();\n return;\n }\n\n // Home/End: move the caret to the visual start/end of the current line.\n // The default contenteditable behavior is inconsistent with paginated\n // fragments — Chrome occasionally jumps to the start/end of the block\n // element instead of the visual line. We explicitly use the native\n // `Selection.modify(\"lineboundary\")` API (Chrome/Edge/Safari) which\n // respects visual line wrapping inside any contenteditable.\n //\n // `Cmd/Ctrl+Home/End` keep the default browser behavior (document scroll).\n if (!mod && (e.key === \"Home\" || e.key === \"End\")) {\n const root = rootRef.current;\n const sel = window.getSelection();\n const sm = (sel as unknown as { modify?: (alter: string, direction: string, granularity: string) => void })?.modify;\n if (root && sel && sel.rangeCount > 0 && typeof sm === \"function\") {\n e.preventDefault();\n const alter = e.shiftKey ? \"extend\" : \"move\";\n const direction = e.key === \"Home\" ? \"left\" : \"right\";\n sm.call(sel, alter, direction, \"lineboundary\");\n const modelSel = readDomSelection(root);\n if (modelSel) ctxRef.current.setSelection(modelSel);\n }\n return;\n }\n\n // Backspace/Delete quando um embed (imagem) está selecionado ou\n // adjacente ao caret colapsado. O `<figure>` do embed é\n // `contenteditable=false`; quando a seleção do DOM pousa nele — o que\n // acontece depois de inserir ou clicar numa imagem — o navegador NUNCA\n // dispara `beforeinput`, então o caminho normal de delete (tratado em\n // `beforeinput`, no outro useEffect) fica mudo pra esse caso. Decidido\n // pelo MODELO (`getSelectedEmbed`/`isEmbedAdjacentToCaret`), não pela\n // seleção do DOM — determinístico e não depende de onde cada navegador\n // decide pousar o caret ao redor de um elemento não-editável.\n //\n // Não intercepta o caso normal (cursor em texto, sem embed adjacente):\n // aí as duas funções devolvem false/null e o handler cai no `return`\n // sem `preventDefault`, deixando `beforeinput` cuidar disso como sempre\n // (ver o teste \"não intercepta\" em `Editor.keydown.test.ts`).\n if (!mod && (e.key === \"Backspace\" || e.key === \"Delete\")) {\n // Composição ativa (ex.: acento morto `´` no layout ABC-Extended dispara\n // `compositionstart`) — o modelo ainda não recebeu o texto em\n // composição, só o DOM sabe. Decidir por `getSelectedEmbed`/\n // `isEmbedAdjacentToCaret` aqui enxergaria o embed adjacente do MODELO\n // (que não avançou) e apagaria a imagem em vez de deixar o Backspace\n // corrigir a composição — e ainda travaria a composição no meio, porque\n // `preventDefault` interrompe o IME. Mesma guarda que as linhas 173,\n // 205 e 520 já usam pros outros caminhos de input.\n if (isComposingRef.current) return;\n const direction = e.key === \"Backspace\" ? \"backward\" : \"forward\";\n const shouldIntercept =\n ed.getSelectedEmbed() != null || isEmbedAdjacentToCaret(ctxRef.current, direction);\n if (shouldIntercept) {\n e.preventDefault();\n if (direction === \"backward\") deleteBackward(ctxRef.current);\n else deleteForward(ctxRef.current);\n }\n return;\n }\n\n if (!mod) return;\n const key = e.key.toLowerCase();\n\n if (!e.shiftKey && !e.altKey && key === \"z\") {\n e.preventDefault();\n historyRef.current.undo();\n return;\n }\n if (!e.altKey && (key === \"y\" || (e.shiftKey && key === \"z\"))) {\n e.preventDefault();\n historyRef.current.redo();\n return;\n }\n\n if (e.altKey && !e.shiftKey && /^[0-6]$/.test(key)) {\n e.preventDefault();\n const n = Number(key);\n if (n === 0) ed.setBlockType(\"paragraph\");\n else ed.setBlockType(\"heading\", { level: n as 1 | 2 | 3 | 4 | 5 | 6 });\n return;\n }\n\n if (e.shiftKey && !e.altKey && (key === \"7\" || key === \"8\")) {\n e.preventDefault();\n ed.toggleList(key === \"7\" ? \"ordered\" : \"bullet\");\n return;\n }\n\n const markToggle = !e.shiftKey && !e.altKey\n ? (BOOLEAN_SHORTCUTS[key] as MarkName | undefined)\n : undefined;\n if (markToggle) {\n e.preventDefault();\n ed.toggleMark(markToggle);\n return;\n }\n if (e.shiftKey && !e.altKey && key === \"x\") {\n e.preventDefault();\n ed.toggleMark(\"strike\");\n return;\n }\n\n if (!e.altKey && !e.shiftKey && key === \"k\") {\n e.preventDefault();\n const active = ed.getActiveMarks();\n const currentHref =\n active.link && typeof active.link === \"object\" && \"href\" in active.link\n ? (active.link.href as string)\n : \"\";\n void ed.requestLink(currentHref).then((href) => {\n if (href === null) return;\n if (href === \"\") ed.removeMark(\"link\");\n else ed.setMark(\"link\", { href });\n });\n }\n }, []);\n\n const onCompositionStart = useCallback(() => {\n isComposingRef.current = true;\n }, []);\n\n const onCompositionEnd = useCallback((e: React.CompositionEvent<HTMLDivElement>) => {\n isComposingRef.current = false;\n if (e.data && e.data.length > 0) {\n insertText(ctxRef.current, e.data);\n }\n }, []);\n\n const topLevels = useMemo(() => groupTopLevels(snapshot.blocks), [snapshot]);\n // Chave estrutural dos topLevels: muda só quando a SHAPE muda (tipos,\n // contagem de itens em lists, list kind/style/start), não quando o texto\n // dentro de um block é editado. Usada como dep do `usePageLayout` em\n // lugar do array `topLevels` (cuja referência muda a cada keystroke), o\n // que cortava repaginações desnecessárias — antes 1.3M DOM mutations\n // por ~10 teclas viraram zero. O array `topLevels` em si continua fresh\n // pra `renderSlot` ler o texto atualizado de cada block.\n const topLevelsStructuralKey = useMemo(\n () =>\n topLevels\n .map((t) =>\n t.kind === \"list\"\n ? `L:${t.listKind}:${t.items.length}:${t.ordinalStart}`\n : `B:${t.block.type}:${t.block.attrs.level ?? \"\"}`,\n )\n .join(\"|\"),\n [topLevels],\n );\n // Page geometry sources, in precedence order:\n // 1. `pageGeometry` prop when explicitly set (view-only override — does NOT\n // write back to the Y.Doc; useful for apps that force a fixed size, e.g.\n // prova generator pinned to A4).\n // 2. `editor.doc` page settings (Y.Doc-backed, sync-aware).\n // 3. `A4_PAGE` fallback when both unavailable.\n // `pageGeometry={null}` continues to disable pagination entirely.\n const docSettings = usePageSettings(editor.doc);\n const propIsSet = pageGeometry !== undefined;\n const geom: PageGeometry = propIsSet ? (pageGeometry ?? A4_PAGE) : docSettings;\n const paginated = pageGeometry !== null;\n // Reads from snapshot + selection; recomputed on every render.\n const selectedEmbed = editor.getSelectedEmbed();\n\n const { layout, phase: paginationPhase } = usePageLayout(\n rootRef as RefObject<HTMLElement>,\n geom,\n topLevels.length,\n // Usar a chave estrutural (string) em vez do array `topLevels` direto:\n // o array reference muda a cada keystroke, mas a chave só muda quando\n // a SHAPE dos top-levels muda. `usePageLayout` compara deps via\n // `shallowArrayEqual` — string equality cobre isso.\n [topLevelsStructuralKey, pageGeometry],\n );\n\n const effectiveLayout: PageLayout = paginated ? layout : defaultPageLayout(topLevels.length);\n\n // pageStyle memoizado: CSS custom properties são HERDADAS, então qualquer\n // toggle de `style` no `.ed-root` invalidava estilos em todos os\n // descendentes (~165k elementos no caso do bug histórico) e alimentava\n // style recalc pesado a cada keystroke. Memo aqui mantém a mesma\n // referência enquanto width/height/margens não mudam.\n const pageStyle = useMemo(\n () =>\n paginated\n ? ({\n \"--ed-page-width\": `${geom.width}px`,\n \"--ed-page-height\": `${geom.height}px`,\n \"--ed-page-margin-top\": `${geom.marginTop}px`,\n \"--ed-page-margin-bottom\": `${geom.marginBottom}px`,\n \"--ed-page-margin-left\": `${geom.marginLeft}px`,\n \"--ed-page-margin-right\": `${geom.marginRight}px`,\n } as React.CSSProperties)\n : undefined,\n [\n paginated,\n geom.width,\n geom.height,\n geom.marginTop,\n geom.marginBottom,\n geom.marginLeft,\n geom.marginRight,\n ],\n );\n\n const body = (\n <div\n ref={rootRef}\n contentEditable\n suppressContentEditableWarning\n spellCheck\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n role=\"textbox\"\n aria-multiline=\"true\"\n className={\n (className ?? \"ed-root\") + (paginated ? \" ed-root--paged\" : \" ed-root--flat\")\n }\n data-pagination-phase={paginated ? paginationPhase : undefined}\n style={pageStyle}\n onKeyDown={onKeyDown}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n >\n {paginated\n ? effectiveLayout.pages.map((page, pi) => {\n const ctx: PageRenderContext = {\n pageNumber: pi + 1,\n pageCount: effectiveLayout.pages.length,\n };\n // Apenas a pág 1 recebe o `firstPageExtraTop` adicional. Inline\n // style sobrescreve a CSS var herdada do `.ed-root` (que vale para\n // as páginas seguintes).\n const pageInlineStyle =\n pi === 0 && geom.firstPageExtraTop\n ? ({\n \"--ed-page-margin-top\": `${geom.marginTop + geom.firstPageExtraTop}px`,\n } as React.CSSProperties)\n : undefined;\n return (\n <div\n key={`page-${pi}`}\n className=\"ed-page\"\n data-page-number={ctx.pageNumber}\n data-page-total={ctx.pageCount}\n style={pageInlineStyle}\n >\n {renderPageHeader && (\n <div className=\"ed-page-header\" contentEditable={false}>\n {renderPageHeader(ctx)}\n </div>\n )}\n <div className=\"ed-page-content\">\n {page.slots.map((slot) => renderSlot(slot, topLevels))}\n </div>\n {renderPageFooter && (\n <div className=\"ed-page-footer\" contentEditable={false}>\n {renderPageFooter(ctx)}\n </div>\n )}\n </div>\n );\n })\n : topLevels.map((t) => renderTopLevel(t))}\n {selectedEmbed && (\n <ImageResizeOverlay\n rootRef={rootRef}\n blockIndex={selectedEmbed.blockIndex}\n offset={selectedEmbed.offset}\n cellIndex={selectedEmbed.cellIndex}\n embed={selectedEmbed.embed}\n onCommit={(w, h) =>\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n { width: w, height: h },\n selectedEmbed.cellIndex,\n )\n }\n onCommitMove={(ox, oy) =>\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n { offsetX: ox, offsetY: oy },\n selectedEmbed.cellIndex,\n )\n }\n onReanchor={(to, ox, oy) =>\n editor.moveEmbedAnchor(\n {\n blockIndex: selectedEmbed.blockIndex,\n offset: selectedEmbed.offset,\n cellIndex: selectedEmbed.cellIndex,\n },\n to,\n ox,\n oy,\n )\n }\n />\n )}\n {hoveredBehind &&\n !(\n selectedEmbed &&\n selectedEmbed.blockIndex === hoveredBehind.blockIndex &&\n selectedEmbed.offset === hoveredBehind.offset &&\n (selectedEmbed.cellIndex ?? -1) === (hoveredBehind.cellIndex ?? -1)\n ) && (\n <BehindImageSelectAffordance\n rootRef={rootRef}\n blockIndex={hoveredBehind.blockIndex}\n offset={hoveredBehind.offset}\n cellIndex={hoveredBehind.cellIndex}\n onSelect={() =>\n setSelection({\n anchor: {\n blockIndex: hoveredBehind.blockIndex,\n cellIndex: hoveredBehind.cellIndex,\n offset: hoveredBehind.offset,\n },\n focus: {\n blockIndex: hoveredBehind.blockIndex,\n cellIndex: hoveredBehind.cellIndex,\n offset: hoveredBehind.offset + 1,\n },\n })\n }\n />\n )}\n {hoveredLink && hoveredLink.href && (\n <LinkHoverTooltip href={hoveredLink.href} rect={hoveredLink.rect} />\n )}\n {awareness && (\n <RemoteCursorsOverlay\n rootRef={rootRef}\n doc={doc}\n awareness={awareness}\n revision={snapshot}\n />\n )}\n </div>\n );\n\n return <EditorProvider editor={editor}>{body}</EditorProvider>;\n}\n\n// ---------- Top-level grouping (data, not JSX) ----------\n\ninterface ListEntry {\n block: SerializedBlock;\n index: number;\n}\n\ntype TopLevel =\n | { kind: \"block\"; block: SerializedBlock; blockIndex: number; key: number }\n | {\n kind: \"list\";\n items: ListEntry[];\n listKind: ListKind;\n /**\n * Number to seed the `<ol>` `start` attribute. Pulled from the first\n * item's `listStart` attr, defaulting to 1. Only meaningful for\n * `listKind === \"ordered\"`.\n */\n ordinalStart: number;\n /**\n * Optional CSS `list-style-type` override for this group. Pulled from\n * the first item's `listStyle` attr. Undefined means \"use the cascade\n * cycle from sofer-editor.css\".\n */\n listStyle?: string;\n key: number;\n };\n\nexport function groupTopLevels(snapshot: SerializedBlock[]): TopLevel[] {\n const out: TopLevel[] = [];\n let i = 0;\n while (i < snapshot.length) {\n const block = snapshot[i];\n if (block.type === \"listItem\") {\n const groupStart = i;\n const items: ListEntry[] = [];\n const kind: ListKind = block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const leaderLevel = clampListLevel(block.attrs.listLevel);\n const groupStyle = block.attrs.listStyle as string | undefined;\n // A `listStart` on the FIRST item of the group seeds the `<ol start>`.\n // A `listStart` or `listStyle` on a LATER item breaks the group at that\n // item — its first item becomes the seed of the next group.\n while (i < snapshot.length) {\n const b = snapshot[i];\n if (b.type !== \"listItem\") break;\n // A type change breaks the group whenever the item sits AT OR ABOVE\n // the group leader's level — that's a genuine sibling list of a\n // different type (or a return to a shallower level, which\n // `buildListTree`'s stack treats as a new root, same as the leader).\n // Only a type change STRICTLY DEEPER than the leader stays in the\n // group: that's a sublist (e.g. a bullet sub-list under a numbered\n // item pasted from Word), and `buildListTree` nests it under its\n // parent instead of making it a root.\n const bLevel = clampListLevel(b.attrs.listLevel);\n if (bLevel <= leaderLevel && (b.attrs.listKind ?? \"bullet\") !== kind) {\n break;\n }\n if (i > groupStart) {\n // Continuation: break the group ONLY when this item EXPLICITLY\n // declares a renumbering. An undefined `listStart` / `listStyle`\n // means \"inherit from the current group leader\" — that's the\n // common case (user only tags the first item of the new sequence).\n if (typeof b.attrs.listStart === \"number\") break;\n const itemStyle = b.attrs.listStyle as string | undefined;\n if (itemStyle !== undefined && itemStyle !== groupStyle) break;\n }\n items.push({ block: b, index: i });\n i++;\n }\n const firstAttrs = items[0].block.attrs;\n const ordinalStart =\n typeof firstAttrs.listStart === \"number\" && firstAttrs.listStart > 0\n ? firstAttrs.listStart\n : 1;\n out.push({\n kind: \"list\",\n items,\n listKind: kind,\n ordinalStart,\n listStyle: groupStyle,\n key: groupStart,\n });\n continue;\n }\n out.push({ kind: \"block\", block, blockIndex: i, key: i });\n i++;\n }\n return out;\n}\n\nfunction renderSlot(slot: PageSlot, topLevels: TopLevel[]): JSX.Element | null {\n const top = topLevels[slot.topLevelIndex];\n if (!top) return null;\n if (slot.fragment && top.kind === \"block\") {\n const f: NodeViewFragment = {\n index: slot.fragment.index,\n start: slot.fragment.start,\n end: slot.fragment.end,\n };\n return (\n <NodeView\n key={`${top.key}-f${f.index}`}\n block={top.block}\n index={top.blockIndex}\n fragment={f}\n />\n );\n }\n if (slot.tableFragment && top.kind === \"block\" && top.block.type === \"table\") {\n return (\n <NodeView\n key={`${top.key}-tf${slot.tableFragment.index}`}\n block={top.block}\n index={top.blockIndex}\n tableFragment={slot.tableFragment}\n />\n );\n }\n if (slot.listFragment && top.kind === \"list\") {\n return renderListFragment(top, slot.listFragment);\n }\n return renderTopLevel(top);\n}\n\nexport function renderTopLevel(top: TopLevel): JSX.Element {\n if (top.kind === \"list\") {\n const tree = buildListTree(top.items);\n return renderListTree(tree, top, top.ordinalStart, `list-${top.key}`);\n }\n return <NodeView key={top.key} block={top.block} index={top.blockIndex} />;\n}\n\n/**\n * Render a sub-range of a list group's top-level items, as produced by\n * `placeFragmentedList`. The continuation `<ol>` carries an adjusted `start`\n * so numbering remains contiguous across the page break.\n */\nexport function renderListFragment(top: TopLevel & { kind: \"list\" }, frag: { index: number; itemStart: number; itemEnd: number }): JSX.Element {\n const ranges = topLevelItemRanges(top.items);\n const startRange = ranges[frag.itemStart];\n const endRange = ranges[frag.itemEnd - 1];\n if (!startRange || !endRange) return renderTopLevel(top);\n const sliced = top.items.slice(startRange[0], endRange[1]);\n // Continuation start = group's seed + count of top-level items already on prior pages.\n const ordinalStart = top.ordinalStart + frag.itemStart;\n const key = `list-${top.key}-f${frag.index}`;\n const slicedTop: TopLevel & { kind: \"list\" } = { ...top, items: sliced };\n const tree = buildListTree(sliced);\n return renderListTree(tree, slicedTop, ordinalStart, key);\n}\n\n/**\n * Index ranges within a flat `ListEntry[]` corresponding to each top-level\n * (level-0) item plus its nested descendants. Used by `renderListFragment` to\n * slice the flat list on a top-level boundary that matches what the paginator\n * measured against direct `<li>` children.\n */\nexport function topLevelItemRanges(items: ListEntry[]): Array<[number, number]> {\n const out: Array<[number, number]> = [];\n let currentStart = -1;\n items.forEach((it, idx) => {\n const level = clampListLevel(it.block.attrs.listLevel);\n if (level === 0) {\n if (currentStart >= 0) out.push([currentStart, idx]);\n currentStart = idx;\n }\n });\n if (currentStart >= 0) out.push([currentStart, items.length]);\n return out;\n}\n\ninterface ListNode {\n entry: ListEntry;\n children: ListNode[];\n}\n\nfunction buildListTree(items: ListEntry[]): ListNode[] {\n const roots: ListNode[] = [];\n const stack: { children: ListNode[]; level: number }[] = [{ children: roots, level: -1 }];\n for (const it of items) {\n const level = clampListLevel(it.block.attrs.listLevel);\n while (stack.length > 1 && stack[stack.length - 1].level >= level) stack.pop();\n const node: ListNode = { entry: it, children: [] };\n stack[stack.length - 1].children.push(node);\n stack.push({ children: node.children, level });\n }\n return roots;\n}\n\nfunction renderListTree(\n tree: ListNode[],\n top: TopLevel & { kind: \"list\" },\n start: number,\n key: string,\n): JSX.Element {\n const className = `ed-list ed-list-${top.listKind}`;\n const style: React.CSSProperties | undefined = top.listStyle\n ? { listStyleType: top.listStyle }\n : undefined;\n const items = tree.map((node) => (\n <NodeView key={node.entry.index} block={node.entry.block} index={node.entry.index}>\n {node.children.length > 0\n ? renderNestedListTree(node.children, `nested-${node.entry.index}`)\n : null}\n </NodeView>\n ));\n if (top.listKind === \"ordered\") {\n return (\n <ol\n key={key}\n className={className}\n data-list-kind=\"ordered\"\n start={start > 1 ? start : undefined}\n style={style}\n >\n {items}\n </ol>\n );\n }\n return (\n <ul key={key} className={className} data-list-kind=\"bullet\" style={style}>\n {items}\n </ul>\n );\n}\n\n/**\n * Nested sub-lists never carry an explicit `start`/`listStyle` from a parent\n * item: the cascade in `sofer-editor.css` handles the per-level style cycle\n * (decimal → lower-alpha → lower-roman). Only the TOP-level `<ol>` for a\n * group needs an explicit `start`/`listStyleType`.\n *\n * The sublist's own `listKind` comes from ITS first item, never from the\n * parent/ancestor group. A numbered item pasted from Word can have a bullet\n * sub-list underneath (or vice-versa) — `groupTopLevels` now keeps those\n * together in one group since the type change happens below the group's\n * level-0 leader, so this function must not impose the ancestor's kind on\n * every descendant level.\n */\nfunction renderNestedListTree(tree: ListNode[], key: string): JSX.Element {\n const kind: ListKind = tree[0]?.entry.block.attrs.listKind === \"ordered\" ? \"ordered\" : \"bullet\";\n const Tag = kind === \"ordered\" ? \"ol\" : \"ul\";\n return (\n <Tag key={key} className={`ed-list ed-list-${kind}`} data-list-kind={kind}>\n {tree.map((node) => (\n <NodeView key={node.entry.index} block={node.entry.block} index={node.entry.index}>\n {node.children.length > 0\n ? renderNestedListTree(node.children, `nested-${node.entry.index}`)\n : null}\n </NodeView>\n ))}\n </Tag>\n );\n}\n\nfunction clampListLevel(l: unknown): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\nconst BOOLEAN_SHORTCUTS: Record<string, MarkName> = {\n b: \"bold\",\n i: \"italic\",\n u: \"underline\",\n};\n\ninterface CaretRangeFromPoint {\n caretRangeFromPoint(x: number, y: number): Range | null;\n}\ninterface CaretPositionFromPoint {\n caretPositionFromPoint(\n x: number,\n y: number,\n ): { offsetNode: Node; offset: number } | null;\n}\n\nfunction caretRangeFromPoint(x: number, y: number): Range | null {\n const doc = document as unknown as Partial<CaretRangeFromPoint & CaretPositionFromPoint>;\n if (typeof doc.caretRangeFromPoint === \"function\") {\n return doc.caretRangeFromPoint(x, y);\n }\n if (typeof doc.caretPositionFromPoint === \"function\") {\n const pos = doc.caretPositionFromPoint(x, y);\n if (!pos) return null;\n const range = document.createRange();\n range.setStart(pos.offsetNode, pos.offset);\n range.collapse(true);\n return range;\n }\n return null;\n}\n","import type { Position, Selection } from \"@sofereditor/core\";\n\n/**\n * Mapping between the model and the contenteditable DOM.\n *\n * Each block is rendered as one or more elements carrying `data-block-index`.\n * Unfragmented blocks have a single such element. A paragraph fragmented across\n * pages (Sub-phase 3.2) has multiple elements with the same `data-block-index`,\n * each carrying `data-fragment-start` / `data-fragment-end` indicating the\n * character offsets within the block's text that the element covers.\n *\n * Table blocks (Sub-phase 4.1) render the `<table>` itself with `data-block-index`.\n * Each `<td>` cell carries `data-cell-index` (flat row-major). Model positions\n * inside a table always include `cellIndex`; their `offset` is local to that\n * cell's text.\n */\n\n/** Walks up from `node` to the first ancestor that carries `data-block-index`. */\nfunction findContainingBlockElement(node: Node, root: HTMLElement): HTMLElement | null {\n let cur: Node | null = node;\n while (cur && cur !== root) {\n if (cur.nodeType === Node.ELEMENT_NODE) {\n const el = cur as HTMLElement;\n if (el.dataset.blockIndex != null) return el;\n }\n cur = cur.parentNode;\n }\n return null;\n}\n\n/** Walks up to the first ancestor carrying `data-cell-index`, stopping at the block element. */\nfunction findContainingCellElement(\n node: Node,\n blockEl: HTMLElement,\n): HTMLElement | null {\n let cur: Node | null = node;\n while (cur && cur !== blockEl) {\n if (cur.nodeType === Node.ELEMENT_NODE) {\n const el = cur as HTMLElement;\n if (el.dataset.cellIndex != null) return el;\n }\n cur = cur.parentNode;\n }\n return null;\n}\n\nexport function findBlockIndex(node: Node, root: HTMLElement): number | null {\n const el = findContainingBlockElement(node, root);\n if (!el) return null;\n return Number.parseInt(el.dataset.blockIndex!, 10);\n}\n\n/**\n * Returns the first DOM element for `blockIndex`. For a fragmented block this\n * is the first fragment. Prefer `getFragmentForOffset` when you need the\n * fragment that contains a specific offset.\n */\nexport function getBlockElement(root: HTMLElement, blockIndex: number): HTMLElement | null {\n return root.querySelector<HTMLElement>(`[data-block-index=\"${blockIndex}\"]`);\n}\n\nfunction readFragmentStart(el: HTMLElement): number {\n const s = el.dataset.fragmentStart;\n if (s == null) return 0;\n const n = Number.parseInt(s, 10);\n return Number.isFinite(n) ? n : 0;\n}\n\nfunction readFragmentEnd(el: HTMLElement): number | undefined {\n const s = el.dataset.fragmentEnd;\n if (s == null) return undefined;\n const n = Number.parseInt(s, 10);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Returns the DOM element responsible for `offset` inside `blockIndex`.\n * - Unfragmented block (single element): returns it.\n * - Fragmented block: returns the fragment whose `[start, end)` covers `offset`.\n * For the end-of-block offset, returns the last fragment.\n */\nexport function getFragmentForOffset(\n root: HTMLElement,\n blockIndex: number,\n offset: number,\n): HTMLElement | null {\n const candidates = root.querySelectorAll<HTMLElement>(`[data-block-index=\"${blockIndex}\"]`);\n if (candidates.length === 0) return null;\n if (candidates.length === 1) return candidates[0];\n let last: HTMLElement | null = null;\n for (const el of candidates) {\n last = el;\n const start = readFragmentStart(el);\n const end = readFragmentEnd(el);\n if (end == null) return el;\n if (offset >= start && offset < end) return el;\n }\n return last; // end-of-block offset falls on the last fragment\n}\n\n/**\n * Returns the `<td>` element for `(blockIndex, cellIndex)`. Tables are\n * unfragmented in 4.1 so there's at most one block element to search inside.\n */\nexport function getCellElement(\n root: HTMLElement,\n blockIndex: number,\n cellIndex: number,\n): HTMLElement | null {\n const blockEl = getBlockElement(root, blockIndex);\n if (!blockEl) return null;\n return blockEl.querySelector<HTMLElement>(`[data-cell-index=\"${cellIndex}\"]`);\n}\n\n/**\n * `true` when `el` is an image embed (a length-1 atomic insert in the model).\n * Image embeds participate in offset arithmetic as 1 char each. Intentionally\n * not a type predicate — narrowing `n is HTMLElement` would collapse the\n * \"already an HTMLElement\" path to `never` and break downstream code.\n */\nfunction isImgEmbed(n: Node): boolean {\n return (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embed === \"image\"\n );\n}\n\n/**\n * `true` when `el` is the float-wrap anchor `<img>` rendered at the START of a\n * block (so CSS float can wrap text around it). The model places this embed at\n * its original offset — represented in the DOM by a zero-width phantom span —\n * so the anchor itself must be SKIPPED during offset counting.\n */\nfunction isWrapAnchorImg(n: Node): boolean {\n return (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.wrapAnchor === \"true\"\n );\n}\n\n/**\n * `true` when `el` is the zero-width sentinel left at the model offset of a\n * float-wrap image. Counts as 1 char in offset arithmetic, mirroring how the\n * embed counts in the model.\n */\nfunction isPhantomEmbed(n: Node): boolean {\n return (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embedPhantom === \"true\"\n );\n}\n\n/**\n * `true` for a `<li>` of célula-lista that is NOT the first item of the list.\n *\n * The cell is a plain `Y.Text`: `\"um\\ndois\"` becomes two `<li>`, and the `\\n`\n * disappears from the DOM's text. Every `<li>` from the second on therefore\n * consumes one character of the model that has no corresponding DOM text —\n * the same situation as embeds, handled the same way in both directions.\n */\nfunction isCellLineBoundary(n: Node): boolean {\n if (n.nodeType !== Node.ELEMENT_NODE) return false;\n const line = (n as HTMLElement).dataset.cellLine;\n return line != null && Number(line) > 0;\n}\n\n/** Index of `n` within its parent's `childNodes`. */\nfunction indexInParent(n: Node): number {\n const parent = n.parentNode;\n if (!parent) return 0;\n let i = 0;\n for (const c of Array.from(parent.childNodes)) {\n if (c === n) return i;\n i++;\n }\n return 0;\n}\n\n/**\n * `true` when the subtree rooted at `el` should be skipped during a\n * container-scoped walk — i.e. it belongs to a different block or to a sibling\n * cell when scoping a cell container.\n */\nfunction isRejectedSubtree(el: HTMLElement, container: HTMLElement): boolean {\n if (el === container) return false;\n if (el.dataset.blockIndex != null) return true;\n if (container.dataset.cellIndex != null && el.dataset.cellIndex != null) return true;\n return false;\n}\n\n/**\n * Sum length of all text nodes plus 1 for each image embed inside `container`,\n * up to the boundary defined by `(node, offsetInNode)`. Mirrors how the model\n * counts characters: embeds are atomic length-1 inserts.\n */\nexport function textOffsetWithin(\n container: HTMLElement,\n node: Node,\n offsetInNode: number,\n): number {\n // Normalize an anchor pointing AT an image embed to (parent, indexOfImg).\n let target: Node = node;\n let targetOff = offsetInNode;\n if (isImgEmbed(target) && target.parentNode) {\n targetOff = indexInParent(target);\n target = target.parentNode as Node;\n }\n\n let offset = 0;\n let done = false;\n\n function visit(n: Node): void {\n if (done) return;\n // Fires for EVERY visit of a boundary `<li>`, whether it's the target\n // itself or just a node the walk passes through on the way to a\n // descendant target. This must run before the `n === target` check\n // below: `readDomSelection` feeds real browser selections into this\n // function, and a click into an empty line's `<li><br data-empty></li>`\n // (no text node to anchor into) lands the browser's anchor directly ON\n // that `<li>` — i.e. target CAN BE the boundary element itself, not\n // just an ancestor passed through. Checking only in the pass-through\n // branch (below the target check) silently drops the +1 for exactly\n // that anchor shape — cursor lands one line too early, invisibly, until\n // the next keystroke inserts the character on the wrong line.\n if (isCellLineBoundary(n)) offset += 1;\n if (n === target) {\n if (n.nodeType === Node.TEXT_NODE) {\n offset += targetOff;\n done = true;\n return;\n }\n // Element target: consume childNodes[0..targetOff-1] recursively.\n const children = n.childNodes;\n for (let i = 0; i < targetOff && i < children.length; i++) {\n visit(children[i]);\n if (done) return;\n }\n done = true;\n return;\n }\n if (n.nodeType === Node.TEXT_NODE) {\n offset += (n.textContent ?? \"\").length;\n return;\n }\n if (n.nodeType !== Node.ELEMENT_NODE) return;\n const el = n as HTMLElement;\n if (isRejectedSubtree(el, container)) return;\n // Float-wrap anchor `<img>` is rendered at the block start as a pure\n // visual; its model position lives in the phantom span downstream.\n if (isImgEmbed(el) && isWrapAnchorImg(el)) return;\n if (isImgEmbed(el) || isPhantomEmbed(el)) {\n offset += 1;\n return;\n }\n for (const child of Array.from(el.childNodes)) {\n visit(child);\n if (done) return;\n }\n }\n\n visit(container);\n return offset;\n}\n\ninterface ResolvedAnchor {\n blockIdx: number;\n cellIdx: number | undefined;\n /** The element whose text content offsets are computed against. */\n container: HTMLElement;\n /** Number to add to within-container offsets — non-zero only for fragmented blocks. */\n containerStart: number;\n}\n\nfunction resolveAnchorElement(\n node: Node,\n root: HTMLElement,\n): ResolvedAnchor | null {\n const blockEl = findContainingBlockElement(node, root);\n if (!blockEl) return null;\n const blockIdx = Number.parseInt(blockEl.dataset.blockIndex!, 10);\n const blockType = blockEl.dataset.blockType;\n if (blockType === \"table\") {\n const cellEl = findContainingCellElement(node, blockEl);\n if (!cellEl) {\n // Click landed inside the table but not in any cell — snap to first cell.\n const firstCell = blockEl.querySelector<HTMLElement>(\"[data-cell-index]\");\n if (!firstCell) return null;\n return {\n blockIdx,\n cellIdx: 0,\n container: firstCell,\n containerStart: 0,\n };\n }\n const cellIdx = Number.parseInt(cellEl.dataset.cellIndex!, 10);\n return {\n blockIdx,\n cellIdx,\n container: cellEl,\n containerStart: 0,\n };\n }\n return {\n blockIdx,\n cellIdx: undefined,\n container: blockEl,\n containerStart: readFragmentStart(blockEl),\n };\n}\n\nexport function readDomSelection(root: HTMLElement): Selection | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n if (!sel.anchorNode || !sel.focusNode) return null;\n if (!root.contains(sel.anchorNode) || !root.contains(sel.focusNode)) return null;\n\n const a = resolveAnchorElement(sel.anchorNode, root);\n const f = resolveAnchorElement(sel.focusNode, root);\n if (!a || !f) return null;\n\n const anchor: Position = {\n blockIndex: a.blockIdx,\n cellIndex: a.cellIdx,\n offset: a.containerStart + textOffsetWithin(a.container, sel.anchorNode, sel.anchorOffset),\n };\n const focus: Position = {\n blockIndex: f.blockIdx,\n cellIndex: f.cellIdx,\n offset: f.containerStart + textOffsetWithin(f.container, sel.focusNode, sel.focusOffset),\n };\n return { anchor, focus };\n}\n\ninterface DomPoint {\n node: Node;\n offset: number;\n}\n\nexport function locatePoint(root: HTMLElement, pos: Position): DomPoint | null {\n let container: HTMLElement | null;\n let containerStart = 0;\n\n if (pos.cellIndex != null) {\n container = getCellElement(root, pos.blockIndex, pos.cellIndex);\n if (!container) return null;\n } else {\n container = getFragmentForOffset(root, pos.blockIndex, pos.offset);\n if (!container) return null;\n containerStart = readFragmentStart(container);\n }\n\n let remaining = Math.max(0, pos.offset - containerStart);\n let result: DomPoint | null = null;\n let lastImg: HTMLElement | null = null;\n let lastBoundary: HTMLElement | null = null;\n let lastText: Text | null = null;\n\n function visit(n: Node): void {\n if (result) return;\n if (n.nodeType === Node.TEXT_NODE) {\n const len = (n.textContent ?? \"\").length;\n if (remaining <= len) {\n result = { node: n, offset: remaining };\n return;\n }\n remaining -= len;\n lastImg = null;\n lastBoundary = null;\n lastText = n as Text;\n return;\n }\n if (n.nodeType !== Node.ELEMENT_NODE) return;\n const el = n as HTMLElement;\n if (isRejectedSubtree(el, container!)) return;\n // Skip the float-wrap anchor — its slot in the model lives on the phantom.\n if (isImgEmbed(el) && isWrapAnchorImg(el)) return;\n // A `<li>` boundary (`data-cell-line > 0`) consumes one model char with\n // no DOM text. `remaining === 0` entering it resolves now:\n // - previous line ended in an embed: resolve right after it, via\n // `lastImg`.\n // - previous line was empty: resolve at its start, via `lastBoundary`,\n // falling back to `previousElementSibling` when `lastBoundary` is\n // null.\n // See domBridgeCelulaLista.test.ts for the covered cases (\"\\na\", \"\\n\",\n // \"a\\n\", and the lines ending in an embed).\n //\n // Known gap: with multiple sibling `<ul>`/`<ol>` in one cell, neither\n // `lastBoundary` nor `previousElementSibling` has an answer and the\n // resolved offset is invalid. Inert while each cell renders its lines\n // in a single `<ul>` (the Task 4 contract).\n if (isCellLineBoundary(el)) {\n if (remaining === 0) {\n if (lastImg) {\n const parent = (lastImg as HTMLElement).parentNode;\n if (parent) {\n result = { node: parent, offset: indexInParent(lastImg) + 1 };\n return;\n }\n }\n const prevLine = lastBoundary ?? el.previousElementSibling;\n if (prevLine) {\n result = { node: prevLine, offset: 0 };\n return;\n }\n }\n remaining -= 1;\n lastBoundary = el;\n lastImg = null;\n }\n if (isImgEmbed(el) || isPhantomEmbed(el)) {\n if (remaining === 0) {\n const parent = el.parentNode;\n if (parent) {\n result = { node: parent, offset: indexInParent(el) };\n return;\n }\n }\n remaining -= 1;\n lastImg = el;\n lastBoundary = null;\n return;\n }\n for (const child of Array.from(el.childNodes)) {\n visit(child);\n if (result) return;\n }\n }\n\n visit(container);\n if (result) return result;\n\n // Walk finished without resolving `result` — clamp to the end of what\n // the DOM has, preferring `lastBoundary` (inside it, offset 0), then\n // `lastImg` (right after it), then `lastText` (end of it). See\n // domBridgeCelulaLista.test.ts (\"clamp de overflow...\") for the covered\n // cases: trailing embed, trailing empty line, plain text overflow.\n if (lastBoundary) {\n return { node: lastBoundary, offset: 0 };\n }\n if (lastImg) {\n const parent = (lastImg as HTMLElement).parentNode;\n if (parent) return { node: parent, offset: indexInParent(lastImg) + 1 };\n }\n // Offset overflowed the container's content (the model selection is\n // transiently AHEAD of the rendered DOM — insertText commits the new\n // selection one React commit before the new text). Clamp to the END of the\n // last text node so the caret stays at the end of the line. Collapsing to\n // the container's start instead would paint the caret at the start of the\n // line for one frame before the next commit corrects it (bug #1 flash).\n if (lastText) {\n const t = lastText as Text;\n return { node: t, offset: (t.textContent ?? \"\").length };\n }\n return { node: container, offset: 0 };\n}\n\nexport function selectionsEqual(a: Selection, b: Selection): boolean {\n return (\n a.anchor.blockIndex === b.anchor.blockIndex &&\n (a.anchor.cellIndex ?? -1) === (b.anchor.cellIndex ?? -1) &&\n a.anchor.offset === b.anchor.offset &&\n a.focus.blockIndex === b.focus.blockIndex &&\n (a.focus.cellIndex ?? -1) === (b.focus.cellIndex ?? -1) &&\n a.focus.offset === b.focus.offset\n );\n}\n\n/**\n * `true` when the selection is a rectangular table-cell range — both endpoints\n * inside cells of the same block, addressing different cells. The visual\n * highlight comes from CSS classes applied by `<TableView>`; we deliberately\n * leave the native DOM selection empty so the browser doesn't draw a confusing\n * cross-`<td>` range.\n */\nexport function isTableRectSelection(sel: Selection): boolean {\n if (sel.anchor.blockIndex !== sel.focus.blockIndex) return false;\n if (sel.anchor.cellIndex == null || sel.focus.cellIndex == null) return false;\n return sel.anchor.cellIndex !== sel.focus.cellIndex;\n}\n\nexport function applyDomSelection(root: HTMLElement, modelSel: Selection): void {\n const domSel = window.getSelection();\n if (!domSel) return;\n if (isTableRectSelection(modelSel)) {\n // Don't paint a native range — TableView highlights the rect via classes.\n domSel.removeAllRanges();\n return;\n }\n const anchorPt = locatePoint(root, modelSel.anchor);\n const focusPt = locatePoint(root, modelSel.focus);\n if (!anchorPt || !focusPt) return;\n\n // Already on the resolved points? Don't touch the live range. `removeAllRanges`\n // momentarily clears the selection (the caret blinks out), so re-applying an\n // identical selection is a visible no-op flash. This matters when the offset\n // clamped to the end of the line (model transiently ahead of the DOM): the\n // caret is already there, so we must leave it alone (bug #1 flash).\n if (\n domSel.anchorNode === anchorPt.node &&\n domSel.anchorOffset === anchorPt.offset &&\n domSel.focusNode === focusPt.node &&\n domSel.focusOffset === focusPt.offset\n ) {\n return;\n }\n\n try {\n domSel.removeAllRanges();\n domSel.setBaseAndExtent(anchorPt.node, anchorPt.offset, focusPt.node, focusPt.offset);\n } catch {\n // ignore — node may not be attached yet\n }\n}\n","import type { ClipboardSlice } from \"@sofereditor/core\";\nimport { SOFER_MIME } from \"@sofereditor/core\";\nimport { htmlToSlice } from \"./htmlToSlice\";\n\n/**\n * O que fazer com uma colagem, decidido só a partir do clipboard.\n *\n * Vive separado do handler para que a ORDEM dos ramos seja testável sem montar\n * o editor inteiro. A ordem já regrediu uma vez em produção (ver `planPaste`).\n */\nexport type PastePlan =\n /** Slice do próprio editor (cópia interna). */\n | { kind: \"sofer\"; slice: ClipboardSlice }\n /** HTML externo (Word, Google Docs, navegador) com conteúdo aproveitável. */\n | { kind: \"html\"; slice: ClipboardSlice }\n /** Arquivos de imagem do sistema operacional. */\n | { kind: \"images\"; files: File[] }\n /** Texto puro; `text` nunca é vazio. */\n | { kind: \"text\"; text: string }\n /** Nada aproveitável — o handler não deve nem chamar preventDefault. */\n | { kind: \"none\" };\n\n/**\n * Decide o ramo da colagem.\n *\n * **A ordem é o ponto deste módulo.** HTML vem ANTES de arquivos de imagem: ao\n * copiar TEXTO, o Word coloca no clipboard o HTML completo E um PNG com o\n * trecho renderizado. Medido num caso real (2026-08-19): `types` =\n * `[\"text/plain\",\"text/html\",\"text/rtf\",\"Files\"]`, HTML de 40.517 bytes e\n * `image.png` de 7.392 bytes. Com arquivos na frente, o texto do professor era\n * colado como FIGURA e a formatação nunca era lida. O Google Docs não manda\n * arquivo — por isso só o Word sofria.\n *\n * Inverter é seguro porque `htmlToSlice` ignora imagens e devolve `null` quando\n * não sobra conteúdo: colar uma imagem (do Word, de um site ou do Finder) cai\n * no ramo de arquivos exatamente como antes.\n */\nexport function planPaste(cd: DataTransfer): PastePlan {\n // 1) Slice do próprio editor — sempre vence, é a representação mais fiel.\n const raw = cd.getData(SOFER_MIME);\n if (raw) {\n try {\n const slice = JSON.parse(raw) as ClipboardSlice;\n if (slice && Array.isArray(slice.blocks)) return { kind: \"sofer\", slice };\n } catch {\n // JSON corrompido — segue para os ramos externos.\n }\n }\n\n // 2) HTML externo, ANTES dos arquivos. Ver o comentário acima.\n const html = cd.getData(\"text/html\");\n if (html) {\n const slice = htmlToSlice(html);\n if (slice) return { kind: \"html\", slice };\n }\n\n // 3) Arquivos de imagem.\n //\n // Tentativa abandonada (2026-08-20): tentamos anexar, no final do trecho\n // colado, o arquivo de imagem solto que o Word deixa em `cd.files` quando\n // o HTML não referencia nenhuma figura (nem `<img>`, nem VML, nem `data:`).\n // A ideia era usar o grupo `\\pict` do RTF como sinal de que uma figura de\n // verdade tinha sido copiada, distinguindo isso do PNG \"screenshot\" que o\n // Word também anexa em TODA colagem de texto (ver o comentário acima).\n //\n // Verificado com clipboard real do Word para Mac — o discriminador não\n // existe. Duas colagens medidas no navegador:\n // colagem 1 (SÓ TEXTO, sem figura): \\pict: 0 \\shppict: false PNG: 55.814 bytes <img>: 0\n // colagem 2 (TEXTO + FIGURA de verdade): \\pict: 0 \\shppict: false PNG: 24.902 bytes <img>: 0\n // Nenhuma das duas tem `\\pict` no RTF, nenhuma tem `<img>` no HTML, as duas\n // trazem um PNG — e o tamanho não separa (a colagem só-texto rendeu um\n // arquivo MAIOR que a com figura). O PNG que o Word anexa é sempre uma\n // renderização da seleção, nunca a figura em si — não há sinal nenhum no\n // clipboard que distinga os dois casos.\n const files = Array.from(cd.files ?? []).filter((f) => f.type.startsWith(\"image/\"));\n if (files.length > 0) return { kind: \"images\", files };\n\n // 4) Texto puro.\n const text = cd.getData(\"text/plain\");\n if (text.length > 0) return { kind: \"text\", text };\n\n return { kind: \"none\" };\n}\n","import type {\n AlignValue,\n BlockAttrs,\n BlockType,\n ClipboardSlice,\n DeltaOp,\n ListKind,\n MarkAttrs,\n SerializedBlock,\n} from \"@sofereditor/core\";\nimport { isImageEmbed, MAX_LIST_LEVEL } from \"@sofereditor/core\";\nimport { MAX_INSERT_WIDTH } from \"./imageConstraints\";\n\n/**\n * Converte o HTML do clipboard (Word / Google Docs / navegador) num\n * `ClipboardSlice` que o editor já sabe inserir via `insertSlice`.\n *\n * Sempre bloco-inteiro: `openStart`/`openEnd` são `false` e `blockLevel` é\n * `true` — o conteúdo colado de fora não é um fragmento de um dos blocos do\n * editor, é uma sequência de blocos novos. `blockLevel: true` é o que diz a\n * `insertSlice` (em `@sofereditor/core`) para adotar type/attrs do bloco\n * colado em vez de tratá-lo como fragmento de seleção — ver o comentário em\n * `ClipboardSlice.blockLevel`.\n *\n * Retorna `null` quando não sobra nada aproveitável, para o chamador (`Editor.tsx`)\n * cair no ramo de texto puro em vez de engolir a colagem.\n *\n * Duas armadilhas reais que este módulo trata explicitamente (ver testes):\n * - Google Docs embrulha o documento inteiro num `<b style=\"font-weight:normal\"\n * id=\"docs-internal-guid-...\">` — uma tag inline por fora de blocos inteiros.\n * `hasBlockDescendant` detecta isso e trata o wrapper como transparente\n * (propaga as marcas resolvidas, mas não achata os blocos internos).\n * - Word não usa `<ul>/<ol>/<li>` — usa `<p style=\"mso-list:l0 level1 ...\">`\n * com o marcador (número/símbolo) como texto literal dentro de um span\n * `mso-list:Ignore`. `parseWordListLevel` + a exclusão desse span tratam isso.\n *\n * fontFamily é deliberadamente NUNCA emitida: a escola padroniza uma fonte só,\n * definida no CSS (`.ed-root`) e cada documento tem sua própria tipografia\n * (mesma decisão de `import-docx`, que descarta `w:rFonts`). fontSize É\n * preservado (ver `parseFontSizePt`): o modelo já suporta a mark, a barra já\n * tem controle de tamanho, e descartar o\n * tamanho perdia informação real (título de 37.5pt colado do Google Docs saía\n * do tamanho do corpo). Unidades que dependem do contexto de herança do\n * documento de origem (em/rem/%/larger/smaller/medium) não são resolvíveis\n * aqui e por isso não emitem marca — sem tamanho é melhor que tamanho errado.\n */\nexport function htmlToSlice(html: string): ClipboardSlice | null {\n if (!html || html.trim().length === 0) return null;\n let doc: Document;\n try {\n doc = new DOMParser().parseFromString(html, \"text/html\");\n } catch {\n return null;\n }\n const body = doc.body;\n if (!body) return null;\n\n const blocks: SerializedBlock[] = [];\n try {\n walkBlockLevel(body, {}, blocks);\n } catch {\n // M7: aninhamento extremo (milhares de <span> aninhados, por exemplo)\n // estoura a pilha de chamadas em `walkInlineNode`/`walkBlockLevel`, que\n // são recursivas. O try/catch original só cobria o `DOMParser`, não o\n // walk — a exceção subia por `planPaste` e a colagem não fazia nada, nem\n // caía no fallback de texto puro. Devolvendo `null` aqui, o chamador\n // degrada para esse fallback em vez de engolir a colagem em silêncio.\n return null;\n }\n if (blocks.length === 0) return null;\n\n // I4: se o professor copia a partir de um sub-item, o primeiro listItem da\n // colagem pode não ser nível 0 (forma irmã do Docs, ou um `mso-list …\n // level2` sozinho/em sequência). O renderizador (`topLevelItemRanges`,\n // `Editor.tsx`) só abre um range de página em `level === 0` — sem esse\n // nível presente, o fatiamento de página perde um item num fragmento e\n // duplica a lista inteira noutro. Não é conserto na paginação (fora de\n // escopo aqui): fecha o caminho normalizando cada sequência contígua de\n // listItem para que o primeiro item sempre comece em 0.\n normalizeListLevels(blocks);\n\n // Nada aproveitável = TODO bloco sem texto E sem embed de imagem. O Word\n // nunca escreve `<img>` no HTML do clipboard (nem VML, nem `data:` — ver o\n // comentário em `imageEmbedFromElement`), então uma imagem do Word sempre\n // produz um parágrafo vazio aqui: `<p class=MsoNormal><img ...><o:p></o:p></p>`\n // sem `<img>` nenhum sobrevivendo ao parser do navegador com atributos\n // aproveitáveis. Devolver esse parágrafo vazio faria `planPaste` escolher o\n // ramo de HTML e a imagem seria DESCARTADA — colar imagem do Word passaria a\n // inserir uma linha em branco. Devolvendo null, a colagem segue para o ramo\n // de arquivos, que insere a imagem (ver `pastePlan.ts`).\n //\n // O Google Docs, ao contrário, ESCREVE `<img src=\"data:image/...\">` no HTML\n // — por isso um bloco pode ter embed e nenhum texto (colar só a imagem, sem\n // nenhuma palavra ao redor). Esse caso agora conta como aproveitável.\n //\n // Parágrafo vazio NO MEIO de conteúdo real é linha em branco de propósito e\n // continua preservado — a regra só descarta quando não sobrou texto NEM\n // embed nenhum.\n const temConteudo = blocks.some(\n (b) => b.text.trim().length > 0 || b.delta.some((op) => isImageEmbed(op.insert)),\n );\n if (!temConteudo) return null;\n\n // `blockLevel: true` diz a `insertSlice` para ADOTAR o type/attrs do bloco\n // colado em vez de fazer splice inline (ver o comentário em `insertSlice`).\n // Isso só vale a pena quando o resultado tem mais de um bloco (é\n // inequivocamente uma sequência de blocos externa) OU quando o único bloco\n // NÃO é um parágrafo simples (heading/listItem/blockquote — adotar o tipo é\n // o ganho real). Um ÚNICO parágrafo — a forma mais comum de colagem curta,\n // vinda de nós inline soltos (site/Chrome `<span>`, Word `<p class=\n // MsoNormal><span>`, Docs `<b style=\"font-weight:normal\">`) — NÃO é uma\n // \"colagem de bloco inteiro\" no sentido que importa aqui: marcar\n // `blockLevel` nesse caso faz `insertSlice` tratar até um trecho de duas\n // palavras como bloco externo, partindo o parágrafo do professor em três\n // (Dhead / bloco colado / tail) em vez de um splice inline, e — pior —\n // colar no offset 0 de um heading fazia `am.clear()` nos attrs do bloco\n // alvo, rebaixando o título a `paragraph`. Sem `blockLevel`, esse caso cai\n // no ramo de splice inline (comportamento de antes da branch); um bloco\n // único que É heading/listItem/blockquote continua adotando o tipo — o\n // ganho que motivou esta branch.\n const blockLevel = blocks.length > 1 || blocks[0].type !== \"paragraph\";\n return { blocks, openStart: false, openEnd: false, ...(blockLevel ? { blockLevel: true as const } : {}) };\n}\n\n// ---------------------------------------------------------------------------\n// Classificação de tags\n// ---------------------------------------------------------------------------\n\n/** Tags cujo conteúdo nunca vira texto (script/style vazam pra dentro do body\n * no HTML que o Google Docs cola; o:p/w:.../v:... são namespaces do Word). */\nconst IGNORED_TAGS = new Set([\"script\", \"style\", \"meta\", \"link\", \"head\", \"title\", \"xml\"]);\n\n/** Tags \"block-ish\": sem filhos de bloco viram UM parágrafo; com filhos de\n * bloco são tratadas como container transparente (ver `hasBlockDescendant`). */\nconst BLOCK_LEVEL_TAGS = new Set([\n \"p\",\n \"div\",\n \"section\",\n \"article\",\n \"header\",\n \"footer\",\n \"main\",\n \"figure\",\n \"figcaption\",\n \"pre\",\n \"address\",\n]);\n\n/** Tags que, se aparecerem como descendente direto ou indireto de um elemento\n * não tratado explicitamente, forçam esse elemento a ser um container\n * transparente em vez de uma corrida inline (caso do wrapper `<b>` do Docs). */\nconst BLOCK_TRIGGER_TAGS = new Set([\n \"p\",\n \"div\",\n \"ul\",\n \"ol\",\n \"li\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"blockquote\",\n \"table\",\n \"tr\",\n \"td\",\n \"th\",\n]);\n\nfunction isNamespacedTag(tag: string): boolean {\n return tag.includes(\":\");\n}\n\nfunction hasBlockDescendant(el: Element): boolean {\n const all = el.querySelectorAll(\"*\");\n for (const e of Array.from(all)) {\n if (BLOCK_TRIGGER_TAGS.has(e.tagName.toLowerCase())) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Walker de nível de bloco\n// ---------------------------------------------------------------------------\n\nfunction walkBlockLevel(container: Element, marks: MarkAttrs, blocks: SerializedBlock[]): void {\n let loose: ChildNode[] = [];\n const flushLoose = () => {\n if (loose.length === 0) return;\n const nodes = loose;\n loose = [];\n const delta = inlineDeltaFromNodes(nodes, marks);\n if (delta.length > 0) blocks.push(makeBlock(\"paragraph\", delta, {}));\n };\n\n for (const node of Array.from(container.childNodes)) {\n if (node.nodeType === Node.COMMENT_NODE) continue;\n if (node.nodeType === Node.TEXT_NODE) {\n loose.push(node);\n continue;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\n const el = node as Element;\n const tag = el.tagName.toLowerCase();\n if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) continue;\n\n switch (tag) {\n case \"ul\":\n case \"ol\": {\n flushLoose();\n handleList(el, tag === \"ol\" ? \"ordered\" : \"bullet\", 0, marks, blocks);\n break;\n }\n case \"h1\":\n case \"h2\":\n case \"h3\":\n case \"h4\":\n case \"h5\":\n case \"h6\": {\n flushLoose();\n const level = Number(tag[1]) as 1 | 2 | 3 | 4 | 5 | 6;\n const attrs: BlockAttrs = { level };\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"heading\", delta, attrs));\n break;\n }\n case \"blockquote\": {\n flushLoose();\n handleBlockquote(el, marks, blocks);\n break;\n }\n case \"table\": {\n flushLoose();\n handleTable(el, marks, blocks);\n break;\n }\n case \"li\": {\n // <li> fora de <ul>/<ol> (defensivo) — trata como bullet nível 0.\n // Mesma exclusão de listas aninhadas do <li> normal (handleList):\n // sem isso, um <li> defensivo com filho <ul> vazaria o texto do\n // filho pro pai e perderia a estrutura de listItem do filho.\n flushLoose();\n const nestedLists = topLevelListDescendants(el);\n const clone = el.cloneNode(true) as Element;\n for (const l of topLevelListDescendants(clone)) l.remove();\n const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);\n blocks.push(makeBlock(\"listItem\", delta, { listKind: \"bullet\", listLevel: 0 }));\n for (const nested of nestedLists) {\n const nestedKind: ListKind = nested.tagName.toLowerCase() === \"ol\" ? \"ordered\" : \"bullet\";\n handleList(nested, nestedKind, 1, marks, blocks);\n }\n break;\n }\n case \"img\":\n // <img> como filho direto do container (sem <p> ao redor) — trata\n // como conteúdo solto, igual a <br>, pra juntar com texto vizinho no\n // mesmo parágrafo em vez de sempre virar um bloco próprio. A emissão\n // de fato (checagem de `src`/dimensões) acontece em `walkInlineNode`,\n // que processa `loose` no flush.\n loose.push(node);\n break;\n case \"br\":\n loose.push(node);\n break;\n default: {\n const styleAttr = el.getAttribute(\"style\") ?? \"\";\n const wordListLevel = parseWordListLevel(styleAttr);\n if (wordListLevel != null && !hasBlockDescendant(el)) {\n flushLoose();\n const marker = findWordListMarker(el);\n const listKind: ListKind = marker ? detectWordListKind(marker) : \"bullet\";\n const attrs: BlockAttrs = { listKind, listLevel: wordListLevel };\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"listItem\", delta, attrs));\n break;\n }\n if (hasBlockDescendant(el)) {\n // Container transparente: tag inline (ou div) por fora de blocos\n // inteiros — caso clássico do wrapper do Google Docs. Propaga as\n // marcas resolvidas do próprio elemento para os blocos internos,\n // sem achatar a estrutura.\n flushLoose();\n let nextMarks = applyTagMarks(tag, marks);\n nextMarks = applyStyleMarks(el.getAttribute(\"style\"), nextMarks);\n walkBlockLevel(el, nextMarks, blocks);\n break;\n }\n if (BLOCK_LEVEL_TAGS.has(tag)) {\n flushLoose();\n const attrs: BlockAttrs = {};\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"paragraph\", delta, attrs));\n break;\n }\n // Tag inline \"solta\" (span/b/i/a/font/...) sem filhos de bloco — junta\n // ao parágrafo corrente.\n loose.push(node);\n }\n }\n }\n flushLoose();\n}\n\n/**\n * Listas do Google Docs: `<ul>/<ol>` reais aninhados; nível = profundidade.\n *\n * Duas formas de aninhamento coexistem na prática e as duas têm que\n * funcionar:\n * - Canônica: `<li>texto<ul>...</ul></li>` — a lista filha é descendente do\n * `<li>` (direta, ou dentro de outro container como `<div>`).\n * - Real do Google Docs: `<li>texto</li><ul>...</ul>` — a lista filha é\n * IRMÃ do `<li>` dentro do mesmo `<ul>/<ol>` pai, não descendente dele.\n * Sem tratar essa forma, o item aninhado inteiro some (não aparece nem\n * como filho de outro bloco nem como block próprio).\n */\nfunction handleList(\n listEl: Element,\n kind: ListKind,\n level: number,\n marks: MarkAttrs,\n blocks: SerializedBlock[],\n): void {\n for (const child of Array.from(listEl.children)) {\n const tag = child.tagName.toLowerCase();\n if (tag === \"ul\" || tag === \"ol\") {\n // Forma \"irmã\": lista aninhada emitida como irmã do <li> anterior, não\n // dentro dele. Semanticamente ela pertence ao item anterior — sobe um\n // nível a partir do nível da lista atual.\n const siblingKind: ListKind = tag === \"ol\" ? \"ordered\" : \"bullet\";\n handleList(child, siblingKind, level + 1, marks, blocks);\n continue;\n }\n if (tag !== \"li\") continue;\n\n // Acha as listas aninhadas descendentes do <li> — diretas (forma\n // canônica) ou dentro de outro container (`<div>` etc; `<p>` nunca\n // aninha `<ul>` porque o parser HTML fecha o `<p>` antes). Só pega as\n // \"de fora para dentro\": uma lista aninhada dentro de outra lista já\n // encontrada não conta de novo aqui — ela é tratada recursivamente\n // quando a lista externa for percorrida.\n const nestedLists = topLevelListDescendants(child);\n\n // Clona o <li> e remove as listas aninhadas correspondentes do clone\n // antes de extrair o delta inline — sem isso, o texto do item filho\n // vaza para dentro do item pai (ex.: \"B1\" + \"B2\" viram \"B1B2\" quando a\n // lista aninhada está dentro de um <div> em vez de direta no <li>).\n const clone = child.cloneNode(true) as Element;\n for (const l of topLevelListDescendants(clone)) l.remove();\n const delta = inlineDeltaFromNodes(Array.from(clone.childNodes), marks);\n\n const attrs: BlockAttrs = { listKind: kind, listLevel: clampLevel(level) };\n const align = readAlign(child);\n if (align) attrs.align = align;\n blocks.push(makeBlock(\"listItem\", delta, attrs));\n for (const nested of nestedLists) {\n const nestedKind: ListKind = nested.tagName.toLowerCase() === \"ol\" ? \"ordered\" : \"bullet\";\n handleList(nested, nestedKind, level + 1, marks, blocks);\n }\n }\n}\n\n/** `<ul>/<ol>` descendentes de `root` que não estão, por sua vez, dentro de\n * outro `<ul>/<ol>` descendente de `root` — ou seja, o nível mais externo\n * de listas aninhadas dentro de `root`, não importa quantos containers\n * (`<div>`, `<p>`...) estejam no meio. Não atravessa fronteira de tabela:\n * uma lista dentro de uma `<td>`/`<th>` de uma `<table>` que por sua vez\n * esteja dentro de `root` pertence à célula, não ao `<li>` que contém a\n * tabela — não é \"promovida\" a listItem irmão (edge case raro, tabela\n * dentro de item de lista; ver `handleTable`, que não é chamado a partir\n * daqui, então o conteúdo da tabela continua achatado como antes). */\nfunction topLevelListDescendants(root: Element): Element[] {\n const all = Array.from(root.querySelectorAll(\"ul, ol\"));\n return all.filter((el) => {\n let p = el.parentElement;\n while (p && p !== root) {\n const t = p.tagName.toLowerCase();\n if (t === \"ul\" || t === \"ol\" || t === \"table\" || t === \"td\" || t === \"th\") return false;\n p = p.parentElement;\n }\n return true;\n });\n}\n\n/** `<blockquote>` — se tiver filhos de bloco (ex.: `<p>` internos), cada um\n * vira seu próprio bloco `blockquote`; senão, um único bloco a partir do\n * conteúdo inline. */\nfunction handleBlockquote(el: Element, marks: MarkAttrs, blocks: SerializedBlock[]): void {\n if (!hasBlockDescendant(el)) {\n const attrs: BlockAttrs = {};\n const align = readAlign(el);\n if (align) attrs.align = align;\n const delta = inlineDeltaFromNodes(Array.from(el.childNodes), marks);\n blocks.push(makeBlock(\"blockquote\", delta, attrs));\n return;\n }\n const nested: SerializedBlock[] = [];\n walkBlockLevel(el, marks, nested);\n for (const b of nested) {\n blocks.push(b.type === \"paragraph\" ? { ...b, type: \"blockquote\" } : b);\n }\n}\n\n/** Tabelas ficam de fora do modelo: o texto de cada célula sobrevive como\n * parágrafo(s), em ordem de leitura (linha a linha, célula a célula). Nunca\n * descarta texto — só a estrutura de grade é perdida, de propósito. */\nfunction handleTable(table: Element, marks: MarkAttrs, blocks: SerializedBlock[]): void {\n for (const row of directRows(table)) {\n const cells = Array.from(row.children).filter((c) =>\n [\"td\", \"th\"].includes(c.tagName.toLowerCase()),\n );\n for (const cell of cells) {\n const cellBlocks: SerializedBlock[] = [];\n walkBlockLevel(cell, marks, cellBlocks);\n for (const b of cellBlocks) {\n const attrs: BlockAttrs = {};\n if (b.attrs.align) attrs.align = b.attrs.align;\n blocks.push(makeBlock(\"paragraph\", b.delta, attrs));\n }\n }\n }\n}\n\nfunction directRows(table: Element): Element[] {\n const rows: Element[] = [];\n for (const child of Array.from(table.children)) {\n const tag = child.tagName.toLowerCase();\n if (tag === \"tr\") {\n rows.push(child);\n } else if (tag === \"tbody\" || tag === \"thead\" || tag === \"tfoot\") {\n for (const r of Array.from(child.children)) {\n if (r.tagName.toLowerCase() === \"tr\") rows.push(r);\n }\n }\n }\n return rows;\n}\n\n// ---------------------------------------------------------------------------\n// Word: `mso-list` (sem <ul>/<ol>/<li> reais)\n// ---------------------------------------------------------------------------\n\n/**\n * `mso-list:l0 level2 lfo1` → nível 1 (level é 1-based no Word).\n *\n * Isso sozinho não basta: o Word pode usar duas listas mso-list *separadas*\n * para pai e filho (`l0`/`lfo1` vs `l1`/`lfo2`) e marcar as duas como\n * `level1` — a profundidade real então só aparece no `margin-left` do\n * parágrafo, não no `levelN`. Por isso o nível final é o MAIOR entre o que\n * vem do `levelN` e o que vem da indentação (`nivelPelaMargem`): nenhum dos\n * dois caminhos é descartado, porque cada um cobre um caso que o outro não\n * cobre (levelN real sem margin-left; margin-left real com level1 repetido).\n */\nfunction parseWordListLevel(styleAttr: string): number | null {\n const m = /mso-list\\s*:\\s*l\\d+\\s+level(\\d+)/i.exec(styleAttr);\n if (!m) return null;\n const n = Number.parseInt(m[1], 10);\n const fromLevelN = Number.isFinite(n) ? Math.max(0, n - 1) : 0;\n const fromMargin = wordListLevelFromMarginLeft(marginLeftPt(styleAttr));\n return clampLevel(Math.max(fromLevelN, fromMargin));\n}\n\n/** `margin-left` do parágrafo, convertido para pt. Aceita `pt`/`cm`/`in`/`px`;\n * ausente, `auto`, negativo ou não-parseável conta como 0 (nunca reduz o\n * nível abaixo do que `levelN` já indicava). Ignora `text-indent` de\n * propósito — é o recuo pendente do marcador (`-18pt` típico), não a\n * profundidade do item. */\nfunction marginLeftPt(styleAttr: string): number {\n const raw = parseStyle(styleAttr)[\"margin-left\"];\n if (!raw) return 0;\n const m = /^(-?[\\d.]+)\\s*(pt|cm|in|px)$/i.exec(raw.trim());\n if (!m) return 0;\n const val = Number.parseFloat(m[1]);\n if (!Number.isFinite(val) || val < 0) return 0;\n switch (m[2].toLowerCase()) {\n case \"pt\":\n return val;\n case \"cm\":\n return val * 28.3465;\n case \"in\":\n return val * 72;\n case \"px\":\n return val * 0.75;\n default:\n return 0;\n }\n}\n\n/** 36pt (0,5\") é o passo padrão de indentação de lista do Word: primeiro\n * nível de recuo (36pt) é nível 0, cada 36pt adicional soma 1 nível. */\nfunction wordListLevelFromMarginLeft(pt: number): number {\n if (pt <= 0) return 0;\n return Math.max(0, Math.round(pt / 36) - 1);\n}\n\n/** Acha o span `mso-list:Ignore` que embrulha o marcador (número/símbolo +\n * o espaço de tabulação até o texto real). */\nfunction findWordListMarker(el: Element): Element | null {\n const all = el.querySelectorAll(\"*\");\n for (const e of Array.from(all)) {\n const style = (e.getAttribute(\"style\") ?? \"\").toLowerCase().replace(/\\s+/g, \"\");\n if (style.includes(\"mso-list:ignore\")) return e;\n }\n return null;\n}\n\n/** Marcador com dígito/letra seguido de `.`/`)` → ordered; símbolo (Symbol/\n * Wingdings ou glifo não alfanumérico) → bullet. */\nfunction detectWordListKind(markerEl: Element): ListKind {\n const all = [markerEl, ...Array.from(markerEl.querySelectorAll(\"*\"))];\n for (const e of all) {\n const style = (e.getAttribute(\"style\") ?? \"\").toLowerCase();\n if (style.includes(\"symbol\") || style.includes(\"wingdings\")) return \"bullet\";\n }\n const text = (markerEl.textContent ?? \"\").replace(/ /g, \" \").trim();\n if (/^[0-9]+[.)]/.test(text)) return \"ordered\";\n if (/^[a-zA-Z][.)]/.test(text)) return \"ordered\";\n return \"bullet\";\n}\n\n// ---------------------------------------------------------------------------\n// Walker inline (marcas + texto)\n// ---------------------------------------------------------------------------\n\nfunction inlineDeltaFromNodes(nodes: ChildNode[], marks: MarkAttrs): DeltaOp[] {\n const ops: DeltaOp[] = [];\n for (const n of nodes) walkInlineNode(n, marks, ops);\n return trimEdgeWhitespace(collapseRepeatedSpaces(mergeAdjacentOps(ops)));\n}\n\n/** Runs de espaço adjacentes em nós de texto distintos (ex.: \"antes \" + <img>\n * removida + \" depois\") colapsam pra um único espaço quando as marcas batem\n * e por isso já viraram um único op em `mergeAdjacentOps`. */\nfunction collapseRepeatedSpaces(ops: DeltaOp[]): DeltaOp[] {\n return ops.map((op) =>\n typeof op.insert === \"string\" ? { ...op, insert: op.insert.replace(/ {2,}/g, \" \") } : op,\n );\n}\n\nfunction walkInlineNode(node: ChildNode, marks: MarkAttrs, ops: DeltaOp[]): void {\n if (node.nodeType === Node.COMMENT_NODE) return;\n if (node.nodeType === Node.TEXT_NODE) {\n const text = normalizeWhitespace(node.textContent ?? \"\");\n if (text.length > 0) pushOp(ops, text, marks);\n return;\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return;\n const el = node as Element;\n const tag = el.tagName.toLowerCase();\n\n if (IGNORED_TAGS.has(tag) || isNamespacedTag(tag)) return;\n if (tag === \"img\") {\n const op = imageEmbedFromElement(el);\n if (op) ops.push(op);\n return;\n }\n if (tag === \"br\") {\n // \"\\n\" literal só é legítimo dentro de célula de tabela (ver\n // `core/src/commands.ts`, com `white-space: pre-wrap` no CSS da célula).\n // `.ed-block` não tem `pre-wrap`, então um \"\\n\" aqui virava espaço visual\n // mas continuava sendo um caractere invisível que contava offset e\n // travava o caret ao passar por ele. Correção mínima pra esta release:\n // emitir \" \" em vez de quebrar em blocos de verdade — quebrar em blocos\n // mudaria o contrato de `inlineDeltaFromNodes` (que hoje devolve um delta\n // pra um bloco só). Fica como trabalho futuro.\n ops.push({ insert: \" \" });\n return;\n }\n\n const styleAttr = (el.getAttribute(\"style\") ?? \"\").toLowerCase().replace(/\\s+/g, \"\");\n if (styleAttr.includes(\"mso-list:ignore\")) return; // marcador de lista do Word.\n\n let nextMarks = applyTagMarks(tag, marks);\n nextMarks = applyStyleMarks(el.getAttribute(\"style\"), nextMarks);\n if (tag === \"a\") {\n const href = el.getAttribute(\"href\");\n if (href) nextMarks = { ...nextMarks, link: { href } };\n }\n\n for (const child of Array.from(el.childNodes)) walkInlineNode(child, nextMarks, ops);\n}\n\n/**\n * `<img>` → `DeltaOp` de embed de imagem, ou `null` quando não dá pra\n * aproveitar. Duas condições, as duas obrigatórias:\n *\n * - `src` começando com `data:image/`. `http(s):` exigiria download\n * cross-origin (CORS, latência, falha silenciosa) e `file:` (Word no\n * Windows referencia a imagem por caminho local do disco do autor) é\n * inalcançável daqui — nos dois casos a imagem continua sendo ignorada,\n * como sempre foi.\n * - Dimensões resolvíveis: atributo `width`/`height` (número em px, é o que\n * o Google Docs manda), senão `style=\"width:...;height:...\"`. Sem\n * dimensão o layout quebra (o embed não tem tamanho intrínseco confiável\n * pra render), então sem NENHUMA das duas fontes a imagem é ignorada —\n * mesmo comportamento de antes desta mudança.\n *\n * O Word nunca chega aqui: ele não escreve `<img>` no HTML do clipboard de\n * jeito nenhum (nem VML, nem condicional `<![if !vml]>`, nem `data:` — ver o\n * comentário em `htmlToSlice`), então esta função só tem efeito prático para\n * fontes que de fato emitem `<img>` com `data:` (Google Docs é o caso medido).\n */\n/** Piso de bytes decodificados abaixo do qual um embed `data:image/` é\n * descartado — ver o comentário em `imageEmbedFromElement` (bug B2). */\nconst MIN_EMBED_BYTES = 1024;\n\n/** Tamanho aproximado, em bytes, do payload decodificado de uma `data:` URL —\n * sem decodificar de fato (nem `atob`, nem `decodeURIComponent` do payload\n * inteiro seria necessário para só medir tamanho), só o cálculo de tamanho a\n * partir do comprimento do texto. Cobre as duas formas de `data:` URL:\n * base64 (`;base64,`, o caso comum de imagem grande) e URL-encoded/percent-\n * encoded (`data:image/svg+xml,%3Csvg...`, comum em ícone inline). Payload\n * ausente ou URL sem vírgula → 0. */\nfunction decodedDataUrlByteLength(dataUrl: string): number {\n const commaIndex = dataUrl.indexOf(\",\");\n if (commaIndex === -1) return 0;\n const meta = dataUrl.slice(0, commaIndex);\n const payload = dataUrl.slice(commaIndex + 1);\n if (/;base64$/i.test(meta)) {\n const padding = payload.endsWith(\"==\") ? 2 : payload.endsWith(\"=\") ? 1 : 0;\n return Math.max(0, Math.floor((payload.length * 3) / 4) - padding);\n }\n // Forma URL-encoded: cada byte real ou é um caractere literal (1 byte) ou\n // um \"%XX\" (3 caracteres pra 1 byte) — decodeURIComponent dá o comprimento\n // certo em unidades UTF-16, aproximação suficiente pra um piso de tamanho.\n try {\n return decodeURIComponent(payload).length;\n } catch {\n return payload.length;\n }\n}\n\nfunction imageEmbedFromElement(el: Element): DeltaOp | null {\n const src = el.getAttribute(\"src\");\n if (!src || !src.startsWith(\"data:image/\")) return null;\n // Piso de bytes: o padrão de lazy-load mais comum da web é\n // `<img src=\"data:image/gif;base64,R0lGOD…\" data-src=\"real.jpg\" width=\"800\"\n // height=\"400\">` — um GIF 1×1 transparente usado só como placeholder\n // enquanto o `src` de verdade (`http`, ignorado por esta função) carrega.\n // Sem este piso, o 1×1 passa pelas duas checagens (é `data:image/`, tem\n // dimensões resolvíveis via atributo) e vira uma caixa em branco esticada\n // pro tamanho do placeholder — regressão: antes desta função existir,\n // TODO `<img>` era ignorado e nada aparecia. Um GIF/PNG 1×1 real tem\n // algumas dezenas de bytes; nenhuma figura de verdade de prova fica abaixo\n // de 1 KB decodificado.\n if (decodedDataUrlByteLength(src) < MIN_EMBED_BYTES) return null;\n const style = parseStyle(el.getAttribute(\"style\"));\n const width = readImageDimension(el, \"width\", style);\n const height = readImageDimension(el, \"height\", style);\n if (width == null || height == null) return null;\n // Mesmo teto de largura que insertImageFromFile/drag-drop já aplicam (área\n // de conteúdo de uma página A4) — sem isso, uma imagem colada que\n // declarasse largura maior estouraria a margem, e a mesma imagem inserida\n // via picker/drag ficaria menor que a colada (assimetria visível entre\n // dois caminhos que deveriam se comportar igual).\n let w = width;\n let h = height;\n if (w > MAX_INSERT_WIDTH) {\n h = (h * MAX_INSERT_WIDTH) / w;\n w = MAX_INSERT_WIDTH;\n }\n return { insert: { type: \"image\", src, width: Math.round(w), height: Math.round(h) } };\n}\n\n/** Lê uma dimensão do `<img>`: primeiro o atributo (`width`/`height`), senão\n * a propriedade `style` equivalente (convertida pra px via `cssLengthToPx`).\n *\n * O atributo só é aceito quando é um número cru (`\"568\"`, a forma que o\n * Google Docs manda) — NÃO quando tem sufixo, sobretudo `%`: `width=\"100%\"`\n * é HTML válido, e `Number.parseFloat` aceitaria `\"100%\"` como `100`, um\n * valor em PX completamente errado. Um atributo com sufixo cai pro `style`\n * em vez de ser mal-interpretado. */\nfunction readImageDimension(\n el: Element,\n attr: \"width\" | \"height\",\n style: Record<string, string>,\n): number | undefined {\n const raw = el.getAttribute(attr);\n if (raw && /^\\d+(\\.\\d+)?$/.test(raw.trim())) {\n const n = Number.parseFloat(raw);\n if (Number.isFinite(n) && n > 0) return n;\n }\n const fromStyle = style[attr];\n if (fromStyle) {\n const n = cssLengthToPx(fromStyle);\n if (n != null) return n;\n }\n return undefined;\n}\n\n/** CSS length → px. Aceita `px` (padrão quando a unidade falta), `pt`, `in`,\n * `cm`, `mm`. Unidades relativas ao contexto do documento de origem\n * (`%`/`em`/`rem`) não são resolvíveis aqui e devolvem `undefined` — mesma\n * filosofia de `parseFontSizePt`. */\nfunction cssLengthToPx(raw: string): number | undefined {\n const v = raw.trim();\n const m = /^(-?[\\d.]+)\\s*(px|pt|in|cm|mm)?$/i.exec(v);\n if (!m) return undefined;\n const num = Number.parseFloat(m[1]);\n if (!Number.isFinite(num) || num <= 0) return undefined;\n const unit = (m[2] ?? \"px\").toLowerCase();\n switch (unit) {\n case \"px\":\n return num;\n case \"pt\":\n return num * (96 / 72);\n case \"in\":\n return num * 96;\n case \"cm\":\n return num * (96 / 2.54);\n case \"mm\":\n return num * (96 / 25.4);\n default:\n return undefined;\n }\n}\n\n/** Negrito/itálico/sublinhado/tachado por NOME da tag. `style` explícito\n * (aplicado depois, em `applyStyleMarks`) tem prioridade e pode cancelar. */\nfunction applyTagMarks(tag: string, marks: MarkAttrs): MarkAttrs {\n switch (tag) {\n case \"b\":\n case \"strong\":\n return { ...marks, bold: true };\n case \"i\":\n case \"em\":\n return { ...marks, italic: true };\n case \"u\":\n return { ...marks, underline: true };\n case \"s\":\n case \"strike\":\n case \"del\":\n return { ...marks, strike: true };\n default:\n return marks;\n }\n}\n\nfunction parseStyle(styleAttr: string | null): Record<string, string> {\n const out: Record<string, string> = {};\n if (!styleAttr) return out;\n for (const decl of styleAttr.split(\";\")) {\n const idx = decl.indexOf(\":\");\n if (idx === -1) continue;\n const prop = decl.slice(0, idx).trim().toLowerCase();\n const val = decl.slice(idx + 1).trim();\n if (prop) out[prop] = val;\n }\n return out;\n}\n\n/**\n * Aplica overrides de `style` inline por cima das marcas herdadas/de tag.\n * A regra central: `font-weight`/`font-style`/`text-decoration` explícitos no\n * style VENCEM o nome da tag — é o que impede o wrapper `<b style=\"font-\n * weight:normal\">` do Google Docs de deixar o documento inteiro em negrito.\n *\n * `fontFamily` é intencionalmente NUNCA lida daqui — descartada sempre, junto\n * com `w:rFonts` no import-docx, pelo mesmo motivo (a escola padroniza uma\n * fonte só, definida no CSS (`.ed-root`)). `fontSize` É\n * lida e normalizada para `pt` via `parseFontSizePt` — ver o comentário no\n * topo do arquivo.\n */\nfunction applyStyleMarks(styleAttr: string | null, marks: MarkAttrs): MarkAttrs {\n const decls = parseStyle(styleAttr);\n if (Object.keys(decls).length === 0) return marks;\n const next: MarkAttrs = { ...marks };\n\n if (\"font-weight\" in decls) {\n const v = decls[\"font-weight\"].toLowerCase();\n if (v === \"bold\" || /^[5-9]00$/.test(v)) next.bold = true;\n else if (v === \"normal\" || /^[1-4]00$/.test(v)) delete next.bold;\n }\n\n if (\"font-style\" in decls) {\n const v = decls[\"font-style\"].toLowerCase();\n if (v === \"italic\" || v === \"oblique\") next.italic = true;\n else if (v === \"normal\") delete next.italic;\n }\n\n const deco = decls[\"text-decoration-line\"] ?? decls[\"text-decoration\"];\n if (deco !== undefined) {\n const v = deco.toLowerCase();\n delete next.underline;\n delete next.strike;\n if (v.includes(\"underline\")) next.underline = true;\n if (v.includes(\"line-through\")) next.strike = true;\n }\n\n if (\"color\" in decls) {\n const css = normalizeCssColor(decls.color);\n if (css && !isBlack(css)) next.color = css;\n else delete next.color;\n }\n\n const bg = decls[\"background-color\"] ?? decls.background;\n if (bg !== undefined) {\n const css = normalizeCssColor(bg);\n if (css && css !== \"#ffffff\") next.highlight = css;\n else delete next.highlight;\n }\n\n if (\"font-size\" in decls) {\n const pt = parseFontSizePt(decls[\"font-size\"]);\n // Só troca quando dá para resolver. Quando NÃO dá (em/%/larger/... ou\n // valor absurdo), o local não sobrescreve — mantém o que já veio herdado\n // do ancestral em vez de apagar um tamanho que já tínhamos resolvido\n // corretamente. Não é o mesmo padrão de `color`/`highlight` (que apagam\n // no valor não resolvível): lá um valor ausente/errado é positivamente\n // \"sem cor\"; aqui um `em`/`%` não resolvível não é \"sem tamanho\", é\n // \"tamanho relativo que não sabemos calcular\" — silenciar é melhor que\n // apagar informação que já tínhamos.\n if (pt != null) next.fontSize = formatFontSizePt(pt);\n }\n\n return next;\n}\n\n/** `font-size` do CSS → pt, ou `undefined` quando a unidade depende do\n * contexto de herança do documento de origem (que não temos) ou o valor é\n * absurdo. `px`/`in` convertem para pt; `pt` passa direto; `em`/`rem`/`%`/\n * palavras-chave relativas (`larger`/`smaller`) e `medium`/`small`/`large`\n * (que também dependem do tamanho herdado) são ignorados de propósito — ver\n * o comentário no topo do arquivo. */\nfunction parseFontSizePt(raw: string): number | undefined {\n const v = raw.trim().toLowerCase();\n const m = /^(-?[\\d.]+)\\s*(pt|px|in)$/.exec(v);\n if (!m) return undefined;\n const num = Number.parseFloat(m[1]);\n if (!Number.isFinite(num)) return undefined;\n let pt: number;\n switch (m[2]) {\n case \"pt\":\n pt = num;\n break;\n case \"px\":\n pt = num * 0.75;\n break;\n case \"in\":\n pt = num * 72;\n break;\n default:\n return undefined;\n }\n // O bound usa o valor JÁ ARREDONDADO pra uma casa decimal — é isso que vai\n // pro `formatFontSizePt`. Sem isso, algo como `0.04pt` passa no `pt > 0`\n // aqui mas arredonda pra `\"0pt\"` lá na frente, violando a regra de \"≤0 não\n // emite\" por trás do arredondamento em vez de antes dele.\n const rounded = Math.round(pt * 10) / 10;\n if (rounded <= 0 || rounded > 200) return undefined;\n return rounded;\n}\n\n/** No máximo uma casa decimal, sem zero à toa: `37.5pt`, `11pt` — nunca\n * `37.50pt` nem `11.0pt`. */\nfunction formatFontSizePt(pt: number): string {\n const rounded = Math.round(pt * 10) / 10;\n const str = Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);\n return `${str}pt`;\n}\n\nconst NAMED_COLORS: Record<string, string> = {\n black: \"#000000\",\n white: \"#ffffff\",\n red: \"#ff0000\",\n green: \"#008000\",\n blue: \"#0000ff\",\n yellow: \"#ffff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n gray: \"#808080\",\n grey: \"#808080\",\n orange: \"#ffa500\",\n purple: \"#800080\",\n};\n\n/** CSS color (hex/rgb(a)/nomeado) → hex normalizado, ou `undefined` quando\n * transparente/inválido/palavra-chave que não representa cor (inherit...). */\nfunction normalizeCssColor(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n const v = raw.trim().toLowerCase();\n if (!v || v === \"transparent\" || v === \"none\" || v === \"inherit\" || v === \"initial\") return undefined;\n if (v.startsWith(\"#\")) {\n let hex = v.slice(1);\n if (hex.length === 3) hex = hex.split(\"\").map((c) => c + c).join(\"\");\n return /^[0-9a-f]{6}$/.test(hex) ? `#${hex}` : undefined;\n }\n const rgbMatch = /^rgba?\\(([^)]+)\\)$/.exec(v);\n if (rgbMatch) {\n const parts = rgbMatch[1].split(\",\").map((s) => s.trim());\n if (parts.length < 3) return undefined;\n const alpha = parts[3] !== undefined ? Number.parseFloat(parts[3]) : 1;\n if (alpha === 0) return undefined;\n const toHex = (n: string) => {\n const num = Math.max(0, Math.min(255, Number.parseInt(n, 10) || 0));\n return num.toString(16).padStart(2, \"0\");\n };\n return `#${toHex(parts[0])}${toHex(parts[1])}${toHex(parts[2])}`;\n }\n return NAMED_COLORS[v];\n}\n\nfunction isBlack(hex: string): boolean {\n return hex.toLowerCase() === \"#000000\";\n}\n\nfunction readAlign(el: Element): AlignValue | undefined {\n const style = parseStyle(el.getAttribute(\"style\"));\n const v = style[\"text-align\"];\n if (!v) return undefined;\n switch (v.toLowerCase()) {\n case \"left\":\n case \"start\":\n return \"left\";\n case \"center\":\n return \"center\";\n case \"right\":\n case \"end\":\n return \"right\";\n case \"justify\":\n return \"justify\";\n default:\n return undefined;\n }\n}\n\nfunction clampLevel(n: number): number {\n if (!Number.isFinite(n)) return 0;\n return Math.max(0, Math.min(MAX_LIST_LEVEL, Math.trunc(n)));\n}\n\n/**\n * Normaliza `listLevel` de cada sequência CONTÍGUA de blocos `listItem` em\n * `blocks`, subtraindo o nível mínimo da sequência — o primeiro item da\n * colagem sempre acaba em nível 0, e as profundidades relativas entre os\n * itens são preservadas (ver I4 no comentário de `htmlToSlice`).\n *\n * Só desloca sequências com MAIS de um item. Um item sozinho não desloca:\n * o defeito de paginação que motiva esta normalização (`topLevelItemRanges`\n * em `Editor.tsx` só abrindo range em `level === 0`) exige pelo menos dois\n * itens pra se manifestar — com um item só, `topLevelItemRanges` devolve `[]`\n * e `renderListFragment` cai no fallback `renderTopLevel` (sem perda nem\n * duplicação). Deslocar um item sozinho SEMPRE daria nível 0 (o mínimo de um\n * conjunto de um elemento é o próprio elemento), apagando o nível real que\n * `parseWordListLevel` calculou a partir de `levelN`/`margin-left` — o que\n * destruiria informação sem nenhum ganho, porque o bug que isso fecha não\n * existe nesse caso.\n */\nfunction normalizeListLevels(blocks: SerializedBlock[]): void {\n let i = 0;\n while (i < blocks.length) {\n if (blocks[i].type !== \"listItem\") {\n i++;\n continue;\n }\n let j = i + 1;\n while (j < blocks.length && blocks[j].type === \"listItem\") j++;\n if (j - i > 1) {\n let min = Number.POSITIVE_INFINITY;\n for (let k = i; k < j; k++) {\n const lvl = (blocks[k].attrs.listLevel as number | undefined) ?? 0;\n if (lvl < min) min = lvl;\n }\n if (min > 0) {\n for (let k = i; k < j; k++) {\n const lvl = (blocks[k].attrs.listLevel as number | undefined) ?? 0;\n blocks[k].attrs.listLevel = clampLevel(lvl - min);\n }\n }\n }\n i = j;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Delta helpers\n// ---------------------------------------------------------------------------\n\nfunction pushOp(ops: DeltaOp[], text: string, marks: MarkAttrs): void {\n const hasMarks = Object.keys(marks).length > 0;\n ops.push(hasMarks ? { insert: text, attributes: { ...marks } } : { insert: text });\n}\n\n/** HTML colapsa espaço/tab/quebra-de-linha ASCII em um único espaço; `&nbsp;`\n * (já decodificado pelo DOMParser em U+00A0) é preservado literalmente — NÃO\n * vira espaço normal (I2). \"Nome:&nbsp;&nbsp;…&nbsp;Turma:\" (a linha mais\n * comum de uma prova) e os spans `mso-tab-count` do Word (corridas de\n * `&nbsp;`) dependem disso: colapsar U+00A0 pra espaço ASCII destrói o\n * espaçamento intencional do cabeçalho. As regexes de colapso\n * (`collapseRepeatedSpaces`) e trim (`trimEdgeWhitespace`) abaixo são\n * ASCII-only de propósito — `/ {2,}/` e `/[ \\t\\r\\n]+/` — então o nbsp passa\n * por elas intacto. */\nfunction normalizeWhitespace(raw: string): string {\n return raw.replace(/[ \\t\\r\\n]+/g, \" \");\n}\n\n/** Remove espaço em branco nas bordas do bloco — HTML não renderiza\n * espaço/quebra de linha logo após abrir ou logo antes de fechar um bloco. */\nfunction trimEdgeWhitespace(ops: DeltaOp[]): DeltaOp[] {\n let out = ops;\n if (out.length > 0 && typeof out[0].insert === \"string\") {\n const trimmed = out[0].insert.replace(/^ +/, \"\");\n if (trimmed.length === 0) out = out.slice(1);\n else if (trimmed !== out[0].insert) out = [{ ...out[0], insert: trimmed }, ...out.slice(1)];\n }\n if (out.length > 0 && typeof out[out.length - 1].insert === \"string\") {\n const last = out[out.length - 1];\n const trimmed = (last.insert as string).replace(/ +$/, \"\");\n if (trimmed.length === 0) out = out.slice(0, -1);\n else if (trimmed !== last.insert) out = [...out.slice(0, -1), { ...last, insert: trimmed }];\n }\n return out;\n}\n\nfunction mergeAdjacentOps(ops: DeltaOp[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n for (const op of ops) {\n const prev = out[out.length - 1];\n if (\n prev &&\n typeof prev.insert === \"string\" &&\n typeof op.insert === \"string\" &&\n sameMarks(prev.attributes, op.attributes)\n ) {\n prev.insert = prev.insert + op.insert;\n continue;\n }\n out.push({ ...op });\n }\n return out;\n}\n\nfunction sameMarks(a: MarkAttrs | undefined, b: MarkAttrs | undefined): boolean {\n const ak = a ? Object.keys(a).sort() : [];\n const bk = b ? Object.keys(b).sort() : [];\n if (ak.length !== bk.length) return false;\n for (let i = 0; i < ak.length; i++) {\n if (ak[i] !== bk[i]) return false;\n const av = (a as Record<string, unknown>)[ak[i]];\n const bv = (b as Record<string, unknown>)[bk[i]];\n if (typeof av === \"object\" && av !== null && typeof bv === \"object\" && bv !== null) {\n if (JSON.stringify(av) !== JSON.stringify(bv)) return false;\n } else if (av !== bv) {\n return false;\n }\n }\n return true;\n}\n\nfunction deltaToText(delta: DeltaOp[]): string {\n let s = \"\";\n for (const op of delta) if (typeof op.insert === \"string\") s += op.insert;\n return s;\n}\n\nfunction makeBlock(type: BlockType, delta: DeltaOp[], attrs: BlockAttrs): SerializedBlock {\n return { type, text: deltaToText(delta), delta, attrs };\n}\n","/**\n * Max display width (CSS px) when inserting an image, no matter the source\n * (drag/drop, file picker, `insertImageFromFile`, or an embed parsed from\n * pasted HTML in `htmlToSlice`). Matches the A4 content area, so an image\n * never overflows the page margin. Larger sources are scaled down keeping\n * aspect ratio.\n *\n * Shared constant so every insertion path clamps the same way — without it,\n * an image pasted from HTML (which carries its own declared width/height)\n * could overflow while the exact same image dropped as a file would be\n * clamped, a visible asymmetry between two paths that should behave\n * identically.\n */\nexport const MAX_INSERT_WIDTH = 600;\n","import { isImageEmbed, type ClipboardSlice, type DeltaOp } from \"@sofereditor/core\";\n\n/**\n * Alguma imagem do slice ainda está embutida como `data:` — candidata a subir\n * pro storage (`uploadImage`) antes de `insertSlice`. Usado pelo handler de\n * paste em `Editor.tsx` para decidir se vale a pena esperar por upload\n * (assíncrono) ou se pode inserir o slice na hora (o caso comum — colagem sem\n * imagem nenhuma).\n */\nexport function sliceHasDataImageEmbeds(slice: ClipboardSlice): boolean {\n return slice.blocks.some((b) =>\n b.delta.some((op) => isImageEmbed(op.insert) && op.insert.src.startsWith(\"data:image/\")),\n );\n}\n\n/**\n * Troca cada embed de imagem com `src` `data:` pela URL devolvida por\n * `upload`, num slice novo (não muta `slice`).\n *\n * `htmlToSlice` é deliberadamente pura (ver o comentário lá) e só emite\n * `data:` — subir pro storage é responsabilidade de quem tem o contexto do\n * editor, não do parser de HTML. Uma imagem do Google Docs em base64 tem\n * ~340KB; gravar isso direto no Y.Doc incha o documento e fura o storage.\n *\n * `upload` ausente (sem `uploadImage` configurado — caso do playground)\n * devolve o slice inalterado, mesmo comportamento que `insertImageFromFile`\n * já tem hoje.\n *\n * Falha de upload de UMA imagem não aborta a colagem inteira: mantém o\n * `data:` daquela imagem específica e segue para as outras. Registra no\n * console — falha silenciosa em produção seria pior que o log.\n */\nexport async function resolvePastedImageUploads(\n slice: ClipboardSlice,\n upload: ((file: File) => Promise<string>) | undefined,\n): Promise<ClipboardSlice> {\n if (!upload) return slice;\n const blocks = await Promise.all(\n slice.blocks.map(async (block) => {\n let changed = false;\n const delta: DeltaOp[] = await Promise.all(\n block.delta.map(async (op): Promise<DeltaOp> => {\n if (!isImageEmbed(op.insert) || !op.insert.src.startsWith(\"data:image/\")) return op;\n try {\n const file = await dataUrlToFile(op.insert.src);\n const newSrc = await upload(file);\n changed = true;\n return { ...op, insert: { ...op.insert, src: newSrc } };\n } catch (err) {\n console.error(\n \"[sofereditor] falha ao subir imagem colada para o storage — mantendo a imagem embutida (data:) nesta colagem\",\n err,\n );\n return op;\n }\n }),\n );\n return changed ? { ...block, delta } : block;\n }),\n );\n return { ...slice, blocks };\n}\n\n/** `data:<mime>[;base64],<...>` → `File`, com nome/extensão derivados do MIME.\n *\n * Decodifica via `fetch(dataUrl).then(r => r.blob())` em vez de um regex +\n * `atob` manual. Dois motivos, o segundo é o que importa:\n *\n * - `fetch` é nativo e trata as DUAS formas de `data:` URL — base64 (o caso\n * comum de imagem grande, tipo Google Docs) e URL-encoded/percent-encoded\n * (`data:image/svg+xml,%3Csvg...`, o padrão dominante de ícone inline em\n * sites). O regex anterior só reconhecia `;base64,`; contra uma forma\n * URL-encoded ele simplesmente não casava, e o MIME caía num default\n * inventado (`image/png`) com um `File` de 0 bytes — silencioso, sem\n * lançar, sem proteção do try/catch do chamador. Ver bug B1.\n * - o MIME do `File` sai do `Blob` que o próprio `fetch` decodificou, nunca\n * de um valor chutado — um jpeg do Google Docs enviado como `.png` (ou\n * pior, um SVG rotulado `image/png`) é uma falha de upload plausível em\n * APIs que validam extensão x content-type no servidor.\n *\n * Um `data:` malformado (ou que o `fetch` não consiga decodificar) REJEITA a\n * promise — não inventa um `File` vazio. O chamador (`resolvePastedImageUploads`)\n * já captura isso e preserva o `data:` original na colagem em vez de subir lixo. */\nexport async function dataUrlToFile(dataUrl: string): Promise<File> {\n const blob = await fetch(dataUrl).then((r) => r.blob());\n const mime = blob.type;\n return new File([blob], `imagem-colada-${Date.now()}.${extensionForMime(mime)}`, { type: mime });\n}\n\nconst MIME_EXTENSIONS: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/jpg\": \"jpg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n \"image/bmp\": \"bmp\",\n \"image/svg+xml\": \"svg\",\n};\n\nfunction extensionForMime(mime: string): string {\n return MIME_EXTENSIONS[mime.toLowerCase()] ?? \"png\";\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useState,\n type CSSProperties,\n type JSX,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport { getFragmentForOffset } from \"./dom-bridge\";\n\ninterface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\ninterface BehindImageSelectAffordanceProps {\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n rootRef: RefObject<HTMLDivElement>;\n /** Select the embed (shows the resize/move overlay). */\n onSelect: () => void;\n}\n\nconst ACCENT = \"#3b82f6\";\n\n/**\n * Hover affordance to select a `behind` image (z-index:-1) even when text sits\n * on top of it. The image's own box doesn't receive the click there — the text\n * /block does — so the overlay can't appear from a plain click. On hover we draw\n * a dashed outline (pointer-events:none, so it never steals a text click) plus a\n * clickable badge that selects the image. Lives in the un-clipped `.ed-root`\n * layer, positioned over the image (same measure pattern as ImageResizeOverlay).\n * Inline styles only — the package ships no CSS.\n */\nexport function BehindImageSelectAffordance({\n blockIndex,\n offset,\n cellIndex,\n rootRef,\n onSelect,\n}: BehindImageSelectAffordanceProps): JSX.Element | null {\n const [rect, setRect] = useState<Rect | null>(null);\n\n const locateImage = useCallback((): HTMLImageElement | null => {\n const root = rootRef.current;\n if (!root) return null;\n let container: HTMLElement | null = null;\n if (cellIndex != null) {\n container = root.querySelector<HTMLElement>(\n `[data-block-index=\"${blockIndex}\"] [data-cell-index=\"${cellIndex}\"]`,\n );\n } else {\n container = getFragmentForOffset(root, blockIndex, offset);\n }\n if (!container) return null;\n const fragmentStart = container.dataset.fragmentStart\n ? Number.parseInt(container.dataset.fragmentStart, 10) || 0\n : 0;\n const localOffset = offset - fragmentStart;\n return container.querySelector<HTMLImageElement>(\n `img[data-embed=\"image\"][data-embed-offset=\"${localOffset}\"]`,\n );\n }, [rootRef, blockIndex, offset, cellIndex]);\n\n const measure = useCallback(() => {\n const img = locateImage();\n const root = rootRef.current;\n if (!img || !root) {\n setRect((prev) => (prev === null ? prev : null));\n return;\n }\n const r = img.getBoundingClientRect();\n const rr = root.getBoundingClientRect();\n const next: Rect = {\n top: r.top - rr.top + root.scrollTop,\n left: r.left - rr.left + root.scrollLeft,\n width: r.width,\n height: r.height,\n };\n setRect((prev) =>\n prev &&\n prev.top === next.top &&\n prev.left === next.left &&\n prev.width === next.width &&\n prev.height === next.height\n ? prev\n : next,\n );\n }, [locateImage, rootRef]);\n\n useLayoutEffect(() => {\n measure();\n let cancelled = false;\n const remeasure = () => {\n if (!cancelled) measure();\n };\n const id =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(remeasure)\n : (queueMicrotask(remeasure), 0);\n return () => {\n cancelled = true;\n if (typeof cancelAnimationFrame === \"function\" && typeof id === \"number\" && id > 0) {\n cancelAnimationFrame(id);\n }\n };\n }, [measure]);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onScroll = () => measure();\n window.addEventListener(\"resize\", onScroll);\n root.addEventListener(\"scroll\", onScroll);\n return () => {\n window.removeEventListener(\"resize\", onScroll);\n root.removeEventListener(\"scroll\", onScroll);\n };\n }, [measure, rootRef]);\n\n if (!rect) return null;\n\n const outlineStyle: CSSProperties = {\n position: \"absolute\",\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n border: `1.5px dashed ${ACCENT}`,\n borderRadius: 2,\n boxSizing: \"border-box\",\n pointerEvents: \"none\",\n zIndex: 4,\n };\n\n const badgeStyle: CSSProperties = {\n position: \"absolute\",\n top: rect.top - 2,\n left: rect.left - 2,\n width: 24,\n height: 24,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: ACCENT,\n color: \"#fff\",\n borderRadius: 4,\n cursor: \"pointer\",\n pointerEvents: \"auto\",\n boxShadow: \"0 1px 3px rgba(0,0,0,0.3)\",\n zIndex: 5,\n };\n\n const onBadgeDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return;\n e.preventDefault();\n e.stopPropagation();\n onSelect();\n };\n\n return (\n <>\n <div style={outlineStyle} contentEditable={false} aria-hidden />\n <div\n style={badgeStyle}\n contentEditable={false}\n onPointerDown={onBadgeDown}\n role=\"button\"\n aria-label=\"Selecionar imagem\"\n title=\"Selecionar imagem\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M5 9l-3 3 3 3M9 5l3-3 3 3M15 19l-3 3-3-3M19 9l3 3-3 3M2 12h20M12 2v20\" />\n </svg>\n </div>\n </>\n );\n}\n","import { type CSSProperties, type JSX } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { MOD_SYMBOL } from \"./platform\";\n\ninterface LinkHoverTooltipProps {\n href: string;\n /** Link bounding box in viewport coordinates (from getBoundingClientRect). */\n rect: { top: number; left: number; bottom: number; width: number };\n}\n\nconst MAX_WIDTH = 360;\nconst GAP = 6;\n\n/**\n * Small floating tooltip shown while hovering a link in the editor. Displays the\n * link URL plus a hint that Cmd/Ctrl+click opens it. Lives at `position: fixed`\n * (viewport coords from the link's rect) so it isn't clipped by the paginated\n * page boxes; `pointerEvents: none` so it never steals the hover/click from the\n * link underneath. The package ships no CSS — all styling is inline.\n */\nexport function LinkHoverTooltip({ href, rect }: LinkHoverTooltipProps): JSX.Element {\n // Prefer above the link; flip below when too close to the viewport top.\n const above = rect.top > 48;\n const viewportW = typeof window !== \"undefined\" ? window.innerWidth : MAX_WIDTH + 16;\n const left = Math.max(8, Math.min(rect.left, viewportW - MAX_WIDTH - 8));\n\n const style: CSSProperties = {\n position: \"fixed\",\n left,\n top: above ? rect.top - GAP : rect.bottom + GAP,\n transform: above ? \"translateY(-100%)\" : undefined,\n maxWidth: MAX_WIDTH,\n background: \"#1f2937\",\n color: \"#fff\",\n fontSize: 12,\n lineHeight: 1.4,\n padding: \"6px 9px\",\n borderRadius: 6,\n boxShadow: \"0 4px 14px rgba(0,0,0,0.28)\",\n zIndex: 60,\n pointerEvents: \"none\",\n };\n\n const urlStyle: CSSProperties = {\n display: \"block\",\n maxWidth: MAX_WIDTH - 18,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n fontWeight: 600,\n };\n\n const hintStyle: CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n marginTop: 3,\n fontSize: 11,\n opacity: 0.85,\n };\n\n // Keycap look (the package ships no CSS): a small key-shaped chip around the\n // modifier glyph and the mouse-click glyph.\n const keycapStyle: CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minWidth: 16,\n height: 16,\n padding: \"0 4px\",\n fontSize: 11,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n background: \"rgba(255,255,255,0.16)\",\n border: \"1px solid rgba(255,255,255,0.4)\",\n borderRadius: 4,\n boxShadow: \"inset 0 -1px 0 rgba(0,0,0,0.25)\",\n };\n\n // Render into <body> so `position: fixed` stays viewport-relative even if the\n // editor is nested under a transformed/filtered ancestor (which would turn it\n // into the containing block for fixed elements). SSR-safe: no body, no portal.\n if (typeof document === \"undefined\") return <></>;\n\n return createPortal(\n <div style={style} contentEditable={false} aria-hidden role=\"tooltip\">\n <span style={urlStyle}>{href}</span>\n <span style={hintStyle}>\n <kbd style={keycapStyle}>{MOD_SYMBOL}</kbd>\n <span style={{ opacity: 0.8 }}>+ clique para abrir</span>\n </span>\n </div>,\n document.body,\n );\n}\n","/**\n * Platform detection for keyboard-modifier conventions. On macOS the \"command\"\n * modifier is ⌘ (`metaKey`); elsewhere it's Ctrl (`ctrlKey`). Computed once.\n */\nexport const IS_MAC: boolean = (() => {\n if (typeof navigator === \"undefined\") return false;\n // NB: case-INSENSITIVE. Chrome's `navigator.userAgentData.platform` returns\n // \"macOS\" (lowercase m), while `navigator.platform` returns \"MacIntel\" — a\n // `/Mac/` (case-sensitive) test silently missed \"macOS\", so Macs were treated\n // as non-Mac (showed \"Ctrl\", and Cmd+click didn't open).\n const platform =\n (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ||\n navigator.platform ||\n navigator.userAgent ||\n \"\";\n return /mac|iphone|ipad|ipod/i.test(platform);\n})();\n\n/** Text label for the platform's primary command modifier (\"Cmd\" / \"Ctrl\"). */\nexport const MOD_LABEL: string = IS_MAC ? \"Cmd\" : \"Ctrl\";\n\n/**\n * Keyboard GLYPH for the platform's command modifier: ⌘ (Command) on macOS,\n * ⌃ (Control) elsewhere. Used for keycap-style hints instead of the text label.\n */\nexport const MOD_SYMBOL: string = IS_MAC ? \"⌘\" : \"⌃\";\n\n/** True when the platform's \"open in new tab\" modifier is held during a click. */\nexport function hasOpenModifier(e: { metaKey: boolean; ctrlKey: boolean }): boolean {\n return IS_MAC ? e.metaKey : e.ctrlKey;\n}\n","import { createContext, useContext, type JSX, type ReactNode } from \"react\";\nimport { LinkDialog } from \"./LinkDialog\";\nimport { PageConfigDialog } from \"./PageConfigDialog\";\nimport { ImageCaptionDialog } from \"./ImageCaptionDialog\";\nimport { FormulaDialog } from \"./FormulaDialog\";\nimport type { UseEditorResult } from \"./useEditor\";\n\nconst EditorContext = createContext<UseEditorResult | null>(null);\n\nexport interface EditorProviderProps {\n editor: UseEditorResult;\n children: ReactNode;\n /**\n * De onde servir as 20 fontes .woff2 do MathLive (296 KB). Sem elas o\n * campo desenha com fonte do sistema — feio e SILENCIOSO. O caminho muda\n * por app por causa do base path, então é o app que sabe.\n */\n mathliveFontsDirectory?: string;\n}\n\nexport function EditorProvider({\n editor,\n children,\n mathliveFontsDirectory,\n}: EditorProviderProps): JSX.Element {\n return (\n <EditorContext.Provider value={editor}>\n {children}\n <LinkDialog />\n <PageConfigDialog />\n <ImageCaptionDialog />\n <FormulaDialog fontsDirectory={mathliveFontsDirectory} />\n </EditorContext.Provider>\n );\n}\n\nexport function useEditorContext(): UseEditorResult {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditorContext must be used inside an <EditorProvider>\");\n }\n return ctx;\n}\n","import {\n useEffect,\n useRef,\n useState,\n type FormEvent,\n type JSX,\n} from \"react\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\n\n/**\n * Modal dialog for entering a link URL. Replaces the bare `window.prompt`\n * previously wired to Cmd+K and the toolbar link button. Renders only when\n * `editor.linkRequest` is set; consumes that state via `resolveLinkRequest`.\n *\n * Uses the native `<dialog>` element for focus trapping, ESC handling, and the\n * browser-provided backdrop.\n */\nexport function LinkDialog(): JSX.Element | null {\n const { linkRequest, resolveLinkRequest } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [href, setHref] = useState(\"\");\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (linkRequest) {\n setHref(linkRequest.initialHref);\n if (!dialog.open) dialog.showModal();\n // Defer focus + select-all to after the dialog finishes opening.\n queueMicrotask(() => {\n const input = inputRef.current;\n if (input) {\n input.focus();\n input.select();\n }\n });\n } else if (dialog.open) {\n dialog.close();\n }\n }, [linkRequest]);\n\n if (!linkRequest) return null;\n\n const onSubmit = (e: FormEvent) => {\n e.preventDefault();\n resolveLinkRequest(href.trim());\n };\n\n const onRemove = () => {\n // Empty string signals \"remove the link mark\".\n resolveLinkRequest(\"\");\n };\n\n const onCancel = () => {\n resolveLinkRequest(null);\n };\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-link-dialog\"\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onSubmit} className=\"ed-link-dialog-form\">\n <div className=\"ed-link-dialog-row\">\n <label htmlFor=\"ed-link-href\">URL</label>\n <input\n id=\"ed-link-href\"\n ref={inputRef}\n type=\"url\"\n inputMode=\"url\"\n value={href}\n onChange={(e) => setHref(e.target.value)}\n placeholder=\"https://exemplo.com\"\n autoComplete=\"off\"\n />\n </div>\n <div className=\"ed-link-dialog-actions\">\n {linkRequest.initialHref ? (\n <button type=\"button\" onClick={onRemove} className=\"ed-link-dialog-remove\">\n Remover link\n </button>\n ) : (\n <span />\n )}\n <div className=\"ed-link-dialog-confirm\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" className=\"ed-link-dialog-apply\">\n Aplicar\n </button>\n </div>\n </div>\n </form>\n </dialog>\n );\n}\n","import type { CSSProperties } from \"react\";\n\n/**\n * Inline centering for the editor's native `<dialog showModal()>` modals\n * (link / caption / page-config). The package ships no CSS, so it can't rely on\n * the consumer keeping the UA `dialog { margin: auto }` centering intact — some\n * apps (Tailwind/HeroUI resets, custom `dialog { margin: 0 }`) override it and\n * the modal then opens in the top-left corner. `position: fixed` + translate is\n * reset-proof; `maxHeight`/`overflow` keep a tall dialog from spilling\n * off-screen when translated.\n */\nexport const DIALOG_CENTER_STYLE: CSSProperties = {\n position: \"fixed\",\n top: \"50%\",\n left: \"50%\",\n transform: \"translate(-50%, -50%)\",\n margin: 0,\n maxHeight: \"90vh\",\n overflow: \"auto\",\n};\n","import { useEffect, useMemo, useRef, useState, type FormEvent, type JSX } from \"react\";\nimport {\n MARGIN_PRESETS,\n PAGE_PRESETS,\n detectPreset,\n mmToPx,\n pxToMm,\n type PageSettings,\n type PagePreset,\n} from \"@sofereditor/core\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\n\ntype MarginPreset = \"normal\" | \"narrow\" | \"wide\" | \"custom\";\n\n/**\n * Modal dialog to configure page size and margins. Mirrors `LinkDialog` —\n * driven by `editor.pageConfigRequest`. On apply, writes via\n * `editor.setPageSettings`; on cancel, no write.\n */\nexport function PageConfigDialog(): JSX.Element | null {\n const { pageConfigRequest, resolvePageConfigRequest, setPageSettings } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n\n // Form state. Sizes are kept in mm for user-facing inputs; we convert to px\n // when applying. Width/height pair determines the size preset.\n const [sizePreset, setSizePreset] = useState<PagePreset>(\"a4\");\n const [widthMm, setWidthMm] = useState(210);\n const [heightMm, setHeightMm] = useState(297);\n const [marginPreset, setMarginPreset] = useState<MarginPreset>(\"normal\");\n const [marginTopMm, setMarginTopMm] = useState(25.4);\n const [marginBottomMm, setMarginBottomMm] = useState(25.4);\n const [marginLeftMm, setMarginLeftMm] = useState(25.4);\n const [marginRightMm, setMarginRightMm] = useState(25.4);\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (pageConfigRequest) {\n hydrateFromSettings(pageConfigRequest.initial, {\n setSizePreset,\n setWidthMm,\n setHeightMm,\n setMarginPreset,\n setMarginTopMm,\n setMarginBottomMm,\n setMarginLeftMm,\n setMarginRightMm,\n });\n if (!dialog.open) dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [pageConfigRequest]);\n\n // When the user picks a size preset, snap width/height back to the canonical\n // values. \"custom\" leaves the user free to edit.\n useEffect(() => {\n if (sizePreset === \"custom\") return;\n const p = PAGE_PRESETS[sizePreset];\n setWidthMm(roundMm(pxToMm(p.width)));\n setHeightMm(roundMm(pxToMm(p.height)));\n }, [sizePreset]);\n\n // When the user picks a margin preset, set all 4 margins to that value.\n useEffect(() => {\n if (marginPreset === \"custom\") return;\n const px = MARGIN_PRESETS[marginPreset];\n const mm = roundMm(pxToMm(px));\n setMarginTopMm(mm);\n setMarginBottomMm(mm);\n setMarginLeftMm(mm);\n setMarginRightMm(mm);\n }, [marginPreset]);\n\n const customWidth = sizePreset === \"custom\";\n\n // Live preview of the dimensions in px so the test/QA loop is easy to read.\n const previewPx = useMemo(\n () => ({\n width: Math.round(mmToPx(widthMm)),\n height: Math.round(mmToPx(heightMm)),\n }),\n [widthMm, heightMm],\n );\n\n if (!pageConfigRequest) return null;\n\n const onApply = (e: FormEvent) => {\n e.preventDefault();\n const next: Partial<PageSettings> = {\n width: mmToPx(widthMm),\n height: mmToPx(heightMm),\n marginTop: mmToPx(marginTopMm),\n marginBottom: mmToPx(marginBottomMm),\n marginLeft: mmToPx(marginLeftMm),\n marginRight: mmToPx(marginRightMm),\n };\n setPageSettings(next);\n resolvePageConfigRequest(true);\n };\n const onCancel = () => resolvePageConfigRequest(false);\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-page-config-dialog\"\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onApply} className=\"ed-page-config-form\">\n <h2>Configurar página</h2>\n\n <fieldset>\n <legend>Tamanho</legend>\n <div className=\"ed-page-config-row\">\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"a4\"}\n onChange={() => setSizePreset(\"a4\")}\n />{\" \"}\n A4 (210 × 297 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"letter\"}\n onChange={() => setSizePreset(\"letter\")}\n />{\" \"}\n Carta (216 × 279 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"oficio\"}\n onChange={() => setSizePreset(\"oficio\")}\n />{\" \"}\n Ofício (216 × 330 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"size\"\n checked={sizePreset === \"custom\"}\n onChange={() => setSizePreset(\"custom\")}\n />{\" \"}\n Personalizado\n </label>\n </div>\n <div className=\"ed-page-config-row\">\n <label>\n Largura (mm){\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={50}\n max={1000}\n value={widthMm}\n onChange={(e) => setWidthMm(Number(e.target.value))}\n disabled={!customWidth}\n />\n </label>\n <label>\n Altura (mm){\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={50}\n max={1000}\n value={heightMm}\n onChange={(e) => setHeightMm(Number(e.target.value))}\n disabled={!customWidth}\n />\n </label>\n <span className=\"ed-page-config-preview\">\n {previewPx.width} × {previewPx.height} px\n </span>\n </div>\n </fieldset>\n\n <fieldset>\n <legend>Margens</legend>\n <div className=\"ed-page-config-row\">\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"normal\"}\n onChange={() => setMarginPreset(\"normal\")}\n />{\" \"}\n Normal (25,4 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"narrow\"}\n onChange={() => setMarginPreset(\"narrow\")}\n />{\" \"}\n Estreita (12,7 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"wide\"}\n onChange={() => setMarginPreset(\"wide\")}\n />{\" \"}\n Larga (50,8 mm)\n </label>\n <label>\n <input\n type=\"radio\"\n name=\"margin\"\n checked={marginPreset === \"custom\"}\n onChange={() => setMarginPreset(\"custom\")}\n />{\" \"}\n Personalizada\n </label>\n </div>\n <div className=\"ed-page-config-row\">\n <label>\n Topo{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginTopMm}\n onChange={(e) => {\n setMarginTopMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n <label>\n Inferior{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginBottomMm}\n onChange={(e) => {\n setMarginBottomMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n <label>\n Esquerda{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginLeftMm}\n onChange={(e) => {\n setMarginLeftMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n <label>\n Direita{\" \"}\n <input\n type=\"number\"\n step={0.1}\n min={0}\n max={200}\n value={marginRightMm}\n onChange={(e) => {\n setMarginRightMm(Number(e.target.value));\n setMarginPreset(\"custom\");\n }}\n />\n </label>\n </div>\n </fieldset>\n\n <div className=\"ed-page-config-actions\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" className=\"ed-page-config-apply\">\n Aplicar\n </button>\n </div>\n </form>\n </dialog>\n );\n}\n\nfunction roundMm(mm: number): number {\n return Math.round(mm * 10) / 10;\n}\n\nfunction hydrateFromSettings(\n s: PageSettings,\n setters: {\n setSizePreset: (v: PagePreset) => void;\n setWidthMm: (v: number) => void;\n setHeightMm: (v: number) => void;\n setMarginPreset: (v: MarginPreset) => void;\n setMarginTopMm: (v: number) => void;\n setMarginBottomMm: (v: number) => void;\n setMarginLeftMm: (v: number) => void;\n setMarginRightMm: (v: number) => void;\n },\n): void {\n const preset = detectPreset(s.width, s.height);\n setters.setSizePreset(preset);\n setters.setWidthMm(roundMm(pxToMm(s.width)));\n setters.setHeightMm(roundMm(pxToMm(s.height)));\n const top = roundMm(pxToMm(s.marginTop));\n const bottom = roundMm(pxToMm(s.marginBottom));\n const left = roundMm(pxToMm(s.marginLeft));\n const right = roundMm(pxToMm(s.marginRight));\n setters.setMarginTopMm(top);\n setters.setMarginBottomMm(bottom);\n setters.setMarginLeftMm(left);\n setters.setMarginRightMm(right);\n setters.setMarginPreset(detectMarginPreset(s));\n}\n\nfunction detectMarginPreset(s: PageSettings): MarginPreset {\n const TOL = 1; // px\n const allEqual =\n Math.abs(s.marginTop - s.marginBottom) <= TOL &&\n Math.abs(s.marginTop - s.marginLeft) <= TOL &&\n Math.abs(s.marginTop - s.marginRight) <= TOL;\n if (!allEqual) return \"custom\";\n for (const [name, px] of Object.entries(MARGIN_PRESETS) as Array<[keyof typeof MARGIN_PRESETS, number]>) {\n if (Math.abs(s.marginTop - px) <= TOL) return name as MarginPreset;\n }\n return \"custom\";\n}\n","import { useEffect, useRef, useState, type FormEvent, type JSX } from \"react\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\nimport type { CaptionAlign } from \"./useEditor\";\n\n/**\n * Modal dialog for entering an image caption. Mirrors `LinkDialog` — driven by\n * `editor.imageCaptionRequest`. Empty input is treated as \"remove caption\".\n *\n * The only configurable property besides the text itself is the caption's\n * horizontal alignment (left / center / right). Word-style figure-caption\n * controls stop here intentionally — see `ImageEmbed.captionAlign`.\n */\nexport function ImageCaptionDialog(): JSX.Element | null {\n const { imageCaptionRequest, resolveImageCaptionRequest } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [caption, setCaption] = useState(\"\");\n const [align, setAlign] = useState<CaptionAlign>(\"center\");\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (imageCaptionRequest) {\n setCaption(imageCaptionRequest.initialCaption);\n setAlign(imageCaptionRequest.initialAlign);\n if (!dialog.open) dialog.showModal();\n queueMicrotask(() => {\n const input = inputRef.current;\n if (input) {\n input.focus();\n input.select();\n }\n });\n } else if (dialog.open) {\n dialog.close();\n }\n }, [imageCaptionRequest]);\n\n if (!imageCaptionRequest) return null;\n\n const onSubmit = (e: FormEvent) => {\n e.preventDefault();\n resolveImageCaptionRequest({ caption, align });\n };\n const onRemove = () => resolveImageCaptionRequest({ caption: \"\", align });\n const onCancel = () => resolveImageCaptionRequest(null);\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-caption-dialog ed-link-dialog\"\n // Centraliza por estilo inline (o pacote não envia CSS): consumidores com\n // reset global de `dialog` (ex.: `margin:0`) quebram a centralização do\n // <dialog> nativo via UA `margin:auto` → o modal abria no canto superior\n // esquerdo. `position:fixed` + translate é à prova de reset.\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onSubmit} className=\"ed-link-dialog-form\">\n <div className=\"ed-link-dialog-row\">\n <label htmlFor=\"ed-caption-input\">Legenda</label>\n <input\n id=\"ed-caption-input\"\n ref={inputRef}\n type=\"text\"\n value={caption}\n onChange={(e) => setCaption(e.target.value)}\n placeholder=\"Ex.: Figura 1 — Mapa do Brasil\"\n autoComplete=\"off\"\n />\n </div>\n <div className=\"ed-link-dialog-row\">\n <span>Alinhamento</span>\n <div className=\"ed-caption-align-group\" role=\"radiogroup\" aria-label=\"Alinhamento da legenda\">\n {([\"left\", \"center\", \"right\"] as const).map((value) => (\n <label key={value} className=\"ed-caption-align-option\">\n <input\n type=\"radio\"\n name=\"caption-align\"\n value={value}\n checked={align === value}\n onChange={() => setAlign(value)}\n />\n {value === \"left\" ? \"Esquerda\" : value === \"center\" ? \"Centro\" : \"Direita\"}\n </label>\n ))}\n </div>\n </div>\n <div className=\"ed-link-dialog-actions\">\n {imageCaptionRequest.initialCaption ? (\n <button type=\"button\" onClick={onRemove} className=\"ed-link-dialog-remove\">\n Remover legenda\n </button>\n ) : (\n <span />\n )}\n <div className=\"ed-link-dialog-confirm\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" className=\"ed-link-dialog-apply\">\n Aplicar\n </button>\n </div>\n </div>\n </form>\n </dialog>\n );\n}\n","import { useCallback, useEffect, useRef, useState, type FormEvent, type JSX } from \"react\";\nimport type { FormulaRender } from \"@sofereditor/math\";\nimport type { MathfieldElement } from \"mathlive\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { DIALOG_CENTER_STYLE } from \"./dialogCenterStyle\";\nimport { PALETA, paraMathlive, conteudoDoBotao } from \"./formulaSnippet\";\nimport { podeInserir, motivoBloqueio } from \"./formulaGuarda\";\n\n/**\n * Caracteres que a tecla morta de circunflexo do teclado ABNT2 (o brasileiro)\n * produz quando o professor a pressiona e a próxima tecla não aceita acento —\n * `^` seguido de `2`, por exemplo.\n *\n * `U+02C6` é o circunflexo modificador, e `U+0302` o acento combinante. O\n * MathLive trata os dois como texto literal, então digitar \"x\", circunflexo,\n * \"2\" saía **`xˆ2`** — o acento impresso ao lado do x, e o 2 na linha de\n * baixo. Era o caminho mais natural do mundo para escrever \"x ao quadrado\"\n * numa prova, e o único que não funcionava.\n *\n * O `^` ASCII (U+005E) NÃO entra nesta lista: esse o MathLive já trata certo\n * sozinho. O `²` (U+00B2) também já vira `x^{2}` sem ajuda.\n */\nconst CIRCUNFLEXO_DE_TECLA_MORTA = new Set([\"\\u02C6\", \"\\u0302\"]);\n\n/**\n * Modal de fórmula. Espelha `ImageCaptionDialog` — dirigido por\n * `editor.formulaRequest`.\n *\n * O preview usa o MESMO renderer que a inserção vai usar. Se o preview falha,\n * o botão de inserir fica desabilitado: nunca inserir um embed cujo render\n * falhou, senão entra no documento um <img> quebrado.\n */\nexport function FormulaDialog({\n fontsDirectory,\n}: {\n fontsDirectory?: string;\n}): JSX.Element | null {\n const { formulaRequest, resolveFormulaRequest } = useEditorContext();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const hostRef = useRef<HTMLDivElement>(null);\n const fieldRef = useRef<MathfieldElement | null>(null);\n const [latex, setLatex] = useState(\"\");\n const [display, setDisplay] = useState(false);\n const [abaAtiva, setAbaAtiva] = useState(0);\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (formulaRequest) {\n setLatex(formulaRequest.initialLatex);\n setDisplay(formulaRequest.initialDisplay);\n setAbaAtiva(0);\n if (!dialog.open) dialog.showModal();\n } else if (dialog.open) {\n dialog.close();\n }\n }, [formulaRequest]);\n\n // O renderer chega por import dinâmico quando o modal abre. Fica em ref, não\n // em state: trocá-lo não precisa re-renderizar, quem re-renderiza é o preview.\n const rendererRef = useRef<((l: string, d: boolean) => FormulaRender) | null>(null);\n // Construtor do <math-field>. Ref e não state: quem re-renderiza é o campo,\n // montado imperativamente na Task 2.\n const mathfieldCtorRef = useRef<typeof MathfieldElement | null>(null);\n // Renderer dos botões da paleta. Ref e não state pelo mesmo motivo do\n // `mathfieldCtorRef` acima: quem re-renderiza é o `setCarregando(false)`\n // do mesmo `.then()`.\n const markupRef = useRef<((latex: string) => string) | null>(null);\n const [preview, setPreview] = useState<FormulaRender | null>(null);\n const [carregando, setCarregando] = useState(false);\n // Separado de `preview` de propósito: `preview` é limpo pelo efeito de\n // render toda vez que `latex`/`display` mudam (abaixo), então se o erro de\n // carregamento morasse lá ele sumiria assim que o professor digitasse\n // qualquer coisa — com o renderer ainda ausente e o motivo do \"Carregando…\"\n // eterno escondido de novo.\n const [erroCarregando, setErroCarregando] = useState<string | null>(null);\n\n useEffect(() => {\n if (!formulaRequest || rendererRef.current) return;\n setCarregando(true);\n setErroCarregando(null);\n // Import DINÂMICO dos dois: mantém mathjax-full E mathlive fora do\n // bundle principal. Ver o teste mathliveLazy.test.ts — trocar por\n // import estático não dá erro nenhum, só fica lento para todo mundo.\n void Promise.all([import(\"@sofereditor/math\"), import(\"mathlive\")])\n .then(([math, mathlive]) => {\n rendererRef.current = math.renderLatexToSvg;\n mathfieldCtorRef.current = mathlive.MathfieldElement;\n markupRef.current = mathlive.convertLatexToMarkup;\n // São 240 KB de sons de tecla no pacote. Editor de prova não apita.\n // `soundsDirectory` e `fontsDirectory` são ESTÁTICOS na classe, não\n // opções por instância — daí ficarem aqui, uma vez por página, em\n // vez de na montagem imperativa do campo.\n mathlive.MathfieldElement.soundsDirectory = null;\n if (fontsDirectory) {\n mathlive.MathfieldElement.fontsDirectory = fontsDirectory;\n }\n setCarregando(false);\n })\n .catch(() => {\n // Chunk falhou ao carregar — o caso comum é um deploy que trocou o\n // hash dos assets enquanto a prova estava aberta na aba. Sem isto,\n // `carregando` ficava travado em `true` para sempre: modal aberto,\n // \"Carregando o renderizador…\" eterno, botão Inserir desabilitado\n // sem nenhuma explicação e sem saída a não ser Cancelar.\n setCarregando(false);\n setErroCarregando(\n \"Não foi possível carregar o editor de fórmulas. Recarregue a página e tente de novo.\",\n );\n });\n }, [formulaRequest]);\n\n // Sem debounce: o render é síncrono e leva menos de um milissegundo para as\n // fórmulas desta paleta, depois que o módulo já carregou.\n useEffect(() => {\n const render = rendererRef.current;\n if (!render || !latex.trim()) {\n setPreview(null);\n return;\n }\n setPreview(render(latex, display));\n }, [latex, display, carregando]);\n\n // Precisa ser capture (`true` no addEventListener) e chegar antes do\n // \"keyboard sink\" interno do MathLive — medido: o `beforeinput` chega\n // cancelável no <math-field> antes de chegar no sink. Um `inlineShortcuts`\n // com a mesma chave NÃO funciona: o caractere de tecla morta não passa pelo\n // caminho de atalhos.\n const onCircunflexoMorto = useCallback((e: Event) => {\n const ev = e as InputEvent;\n if (!ev.data || !CIRCUNFLEXO_DE_TECLA_MORTA.has(ev.data)) return;\n ev.preventDefault();\n ev.stopPropagation();\n fieldRef.current?.insert(\"^{#?}\", { focus: true });\n }, []);\n\n // Montagem imperativa do <math-field>, não JSX: o elemento não tem tipo em\n // `IntrinsicElements`, o React 18 põe atributo (não propriedade) em custom\n // element, e o construtor só existe depois do import dinâmico resolver —\n // o JSX teria que renderizar condicionalmente um elemento ainda indefinido.\n useEffect(() => {\n const Ctor = mathfieldCtorRef.current;\n const host = hostRef.current;\n if (!Ctor || !host || !formulaRequest) return;\n\n const mf = new Ctor();\n mf.className = \"ed-formula-field\";\n const onInput = () => setLatex(mf.getValue(\"latex\"));\n mf.addEventListener(\"input\", onInput);\n mf.addEventListener(\"beforeinput\", onCircunflexoMorto, true);\n\n // MONTAR PRIMEIRO, CONFIGURAR DEPOIS — a ordem é o ponto, e custou uma\n // tela branca para descobrir.\n //\n // Boa parte dos acessores do MathfieldElement começa com\n // `if (!this._mathfield) throw new Error(\"Mathfield not mounted\")`, e o\n // `_mathfield` só nasce no `connectedCallback`. O `inlineShortcuts` é o\n // pior deles: tem essa guarda no getter E no setter, então a linha\n // `{ ...mf.inlineShortcuts }` abaixo lançava duas vezes.\n //\n // Como isso roda dentro de um efeito do React, o erro não estraga só o\n // modal: derruba a árvore inteira. O professor clicava em \"Inserir\n // fórmula\" e o editor virava tela branca, com a prova aberta.\n //\n // Nenhum teste de unidade pega isto — o jsdom não roda o MathLive. Só\n // navegador de verdade.\n host.appendChild(mf);\n\n // Teclado virtual desligado: decisão do usuário. Em desktop rouba altura\n // e duplica a paleta, em inglês e sem sen/tg/cotg.\n mf.mathVirtualKeyboardPolicy = \"manual\";\n // Atalhos de digitação em português. Sem isto o único atalho rápido é o\n // embutido \"sin\", que imprime \"sin\" na prova.\n mf.inlineShortcuts = {\n ...mf.inlineShortcuts,\n sen: \"\\\\operatorname{sen}\",\n tg: \"\\\\operatorname{tg}\",\n cotg: \"\\\\operatorname{cotg}\",\n // Vírgula decimal. Em modo matemático a vírgula crua é átomo de\n // pontuação e ganha espaço DEPOIS de si: digitar \"3,14\" sai impresso\n // como \"3, 14\" (medido: 4.4ex contra 4.023ex do agrupado). A paleta\n // tem um botão `{,}`, mas ninguém clica num botão para uma tecla que\n // está no teclado — e decimal aparece em toda prova de matemática,\n // física e química.\n //\n // O `after: \"digit\"` é o que separa os dois usos da vírgula, e foi\n // verificado no navegador: \"3,14\" vira `3{,}14` e \"f(x,y)\" continua\n // `f\\left(x,y\\right)`, com a vírgula de lista intacta — que é o\n // comportamento certo, ali ela É pontuação.\n \",\": { after: \"digit\", value: \"{,}\" },\n };\n mf.setValue(formulaRequest.initialLatex);\n // Sem esta linha, editar uma fórmula existente abre com o campo cheio e o\n // botão Inserir DESABILITADO: `latex` continuaria \"\" e o gate leria campo\n // vazio. Lê de volta do campo em vez de reusar `initialLatex` porque o\n // MathLive pode normalizar na entrada, e o estado tem que ser o que o\n // campo realmente tem.\n setLatex(mf.getValue(\"latex\"));\n fieldRef.current = mf;\n queueMicrotask(() => mf.focus());\n\n return () => {\n mf.removeEventListener(\"input\", onInput);\n mf.removeEventListener(\"beforeinput\", onCircunflexoMorto, true);\n mf.remove();\n fieldRef.current = null;\n };\n }, [formulaRequest, carregando]);\n\n if (!formulaRequest) return null;\n\n const markup = markupRef.current;\n const motivo = motivoBloqueio(latex, preview);\n const podeSubmeter = podeInserir(latex, preview);\n\n const onSubmit = (e: FormEvent) => {\n e.preventDefault();\n if (!podeSubmeter) return;\n resolveFormulaRequest({ latex, display });\n };\n const onCancel = () => resolveFormulaRequest(null);\n\n const onPaleta = (snippet: string) => {\n const mf = fieldRef.current;\n if (!mf) return;\n mf.insert(paraMathlive(snippet), { focus: true });\n setLatex(mf.getValue(\"latex\"));\n };\n\n return (\n <dialog\n ref={dialogRef}\n className=\"ed-formula-dialog ed-link-dialog\"\n style={DIALOG_CENTER_STYLE}\n onClose={onCancel}\n onCancel={(e) => {\n e.preventDefault();\n onCancel();\n }}\n >\n <form onSubmit={onSubmit} className=\"ed-formula-form\">\n <div className=\"ed-formula-abas\" role=\"tablist\">\n {PALETA.map((cat, i) => (\n <button\n key={cat.nome}\n type=\"button\"\n role=\"tab\"\n aria-selected={i === abaAtiva}\n className=\"ed-formula-aba\"\n onClick={() => setAbaAtiva(i)}\n >\n {cat.nome}\n </button>\n ))}\n </div>\n <div\n className=\"ed-formula-paleta\"\n role=\"tabpanel\"\n style={{ gridTemplateColumns: `repeat(${PALETA[abaAtiva].colunas}, 1fr)` }}\n >\n {PALETA[abaAtiva].itens.map((p) => (\n <button\n key={p.label}\n type=\"button\"\n className=\"ed-formula-paleta-btn\"\n title={p.titulo ?? p.label}\n aria-label={p.titulo ?? p.label}\n onClick={() => onPaleta(p.snippet)}\n // Markup vem do `convertLatexToMarkup` do MathLive sobre um\n // snippet ESTÁTICO da nossa PALETA — não é entrada de usuário.\n // `paraMarkup` (não `paraMathlive`, que é para o campo e usa\n // `#?` — inválido para este renderer) troca `{}` por `{□}`\n // só para o ícone: `^{}`/`_{}` crus saem VAZIOS deste\n // renderer (verificado, não deduzido — ver formulaSnippet.ts).\n {...conteudoDoBotao(markup, p.snippet, p.label)}\n />\n ))}\n </div>\n <div ref={hostRef} className=\"ed-formula-host\" />\n <label className=\"ed-formula-display\">\n <input\n type=\"checkbox\"\n checked={display}\n onChange={(e) => setDisplay(e.target.checked)}\n />\n Fórmula em bloco (centralizada, limites acima e abaixo)\n </label>\n <div className=\"ed-formula-preview\" aria-live=\"polite\">\n {carregando ? (\n <span className=\"ed-formula-vazio\">Carregando o editor…</span>\n ) : erroCarregando ? (\n <span className=\"ed-formula-erro\" role=\"alert\">\n {erroCarregando}\n </span>\n ) : motivo ? (\n <span className=\"ed-formula-erro\" role=\"alert\">\n {motivo}\n </span>\n ) : null}\n </div>\n <div className=\"ed-formula-acoes\">\n <button type=\"button\" onClick={onCancel}>\n Cancelar\n </button>\n <button type=\"submit\" disabled={!podeSubmeter}>\n Inserir\n </button>\n </div>\n </form>\n </dialog>\n );\n}\n","export interface CategoriaPaleta {\n /** Rótulo da aba. */\n nome: string;\n /** 4 para rótulos em palavra, 6 para rótulos em caractere. */\n colunas: 4 | 6;\n itens: readonly { label: string; snippet: string; titulo?: string }[];\n}\n\n/**\n * Paleta do modal de fórmula: 82 itens em 7 categorias, cobrindo do 6º ano ao\n * pré-vestibular, mais física e química de prova.\n *\n * Todos os 82 foram verificados contra o renderer real com\n * `packages: ['base','ams']` + o import por efeito colateral do\n * `AmsConfiguration` — zero falhas. Nenhum exige extensão nova do MathJax.\n * Ao acrescentar um item, rode-o pelo `renderLatexToSvg` antes de commitar.\n *\n * NOTAÇÃO BRASILEIRA: `\\sin` renderiza \"sin\" e `\\tan` renderiza \"tan\". Prova\n * brasileira escreve \"sen\" e \"tg\". Por isso as três funções abaixo usam\n * `\\operatorname{}`. NÃO troque para as formas nativas do LaTeX: a paleta\n * continuaria parecendo certa e a prova impressa sairia errada.\n *\n * `titulo`: nome acessível/tooltip do botão quando `label` é um glifo solto\n * (símbolo, letra grega, seta) e não diz nada sozinho — o `title` do botão\n * usa `titulo ?? label`. Itens com rótulo em palavra (Estruturas) dispensam.\n */\nexport const PALETA: readonly CategoriaPaleta[] = [\n {\n nome: \"Estruturas\",\n colunas: 4,\n itens: [\n { label: \"Fração\", snippet: \"\\\\frac{}{}\" },\n { label: \"Expoente\", snippet: \"^{}\" },\n { label: \"Índice\", snippet: \"_{}\" },\n { label: \"Raiz\", snippet: \"\\\\sqrt{}\" },\n { label: \"Raiz n-ésima\", snippet: \"\\\\sqrt[{}]{}\" },\n { label: \"Somatório\", snippet: \"\\\\sum_{}^{}\" },\n { label: \"Produtório\", snippet: \"\\\\prod_{}^{}\" },\n { label: \"Integral\", snippet: \"\\\\int_{}^{}\" },\n { label: \"Limite\", snippet: \"\\\\lim_{{} \\\\to {}}\" },\n { label: \"Derivada\", snippet: \"\\\\frac{d}{d{}}\" },\n {\n label: \"Matriz 2×2\",\n snippet: \"\\\\begin{pmatrix} {} & {} \\\\\\\\ {} & {} \\\\end{pmatrix}\",\n },\n { label: \"Sistema\", snippet: \"\\\\begin{cases} {} \\\\\\\\ \\\\end{cases}\" },\n { label: \"Parênteses\", snippet: \"\\\\left({}\\\\right)\" },\n { label: \"Binomial\", snippet: \"\\\\binom{}{}\" },\n { label: \"Módulo\", snippet: \"\\\\left|{}\\\\right|\" },\n ],\n },\n {\n nome: \"Símbolos\",\n colunas: 6,\n itens: [\n { label: \"±\", snippet: \"\\\\pm\", titulo: \"mais ou menos\" },\n { label: \"×\", snippet: \"\\\\times\", titulo: \"multiplicação\" },\n { label: \"÷\", snippet: \"\\\\div\", titulo: \"divisão\" },\n { label: \"·\", snippet: \"\\\\cdot\", titulo: \"multiplicação (ponto)\" },\n { label: \"∞\", snippet: \"\\\\infty\", titulo: \"infinito\" },\n { label: \"°\", snippet: \"^\\\\circ\", titulo: \"grau\" },\n { label: \"%\", snippet: \"\\\\%\", titulo: \"por cento\" },\n { label: \",\", snippet: \"{,}\", titulo: \"vírgula decimal\" },\n ],\n },\n {\n nome: \"Relações\",\n colunas: 6,\n itens: [\n { label: \"≠\", snippet: \"\\\\neq\", titulo: \"diferente de\" },\n { label: \"≈\", snippet: \"\\\\approx\", titulo: \"aproximadamente\" },\n { label: \"≡\", snippet: \"\\\\equiv\", titulo: \"idêntico a\" },\n { label: \"≤\", snippet: \"\\\\leq\", titulo: \"menor ou igual a\" },\n { label: \"≥\", snippet: \"\\\\geq\", titulo: \"maior ou igual a\" },\n { label: \"∝\", snippet: \"\\\\propto\", titulo: \"proporcional a\" },\n { label: \"⊥\", snippet: \"\\\\perp\", titulo: \"perpendicular a\" },\n { label: \"∥\", snippet: \"\\\\parallel\", titulo: \"paralelo a\" },\n { label: \"≅\", snippet: \"\\\\cong\", titulo: \"congruente a\" },\n { label: \"∼\", snippet: \"\\\\sim\", titulo: \"semelhante a\" },\n ],\n },\n {\n nome: \"Gregas\",\n colunas: 6,\n itens: [\n { label: \"α\", snippet: \"\\\\alpha\", titulo: \"alfa\" },\n { label: \"β\", snippet: \"\\\\beta\", titulo: \"beta\" },\n { label: \"γ\", snippet: \"\\\\gamma\", titulo: \"gama\" },\n { label: \"δ\", snippet: \"\\\\delta\", titulo: \"delta\" },\n { label: \"ε\", snippet: \"\\\\varepsilon\", titulo: \"épsilon\" },\n { label: \"θ\", snippet: \"\\\\theta\", titulo: \"teta\" },\n { label: \"λ\", snippet: \"\\\\lambda\", titulo: \"lambda\" },\n { label: \"μ\", snippet: \"\\\\mu\", titulo: \"mi\" },\n { label: \"π\", snippet: \"\\\\pi\", titulo: \"pi\" },\n { label: \"ρ\", snippet: \"\\\\rho\", titulo: \"rô\" },\n { label: \"σ\", snippet: \"\\\\sigma\", titulo: \"sigma\" },\n { label: \"φ\", snippet: \"\\\\varphi\", titulo: \"fi\" },\n { label: \"ω\", snippet: \"\\\\omega\", titulo: \"ômega\" },\n { label: \"Δ\", snippet: \"\\\\Delta\", titulo: \"delta maiúsculo\" },\n {\n label: \"Σ\",\n snippet: \"\\\\Sigma\",\n titulo: \"sigma maiúsculo — letra grega; para somatório use Estruturas\",\n },\n {\n label: \"Π\",\n snippet: \"\\\\Pi\",\n titulo: \"pi maiúsculo — letra grega; para produtório use Estruturas\",\n },\n { label: \"Ω\", snippet: \"\\\\Omega\", titulo: \"ômega maiúsculo\" },\n { label: \"Φ\", snippet: \"\\\\Phi\", titulo: \"fi maiúsculo\" },\n ],\n },\n {\n nome: \"Conjuntos\",\n colunas: 6,\n itens: [\n { label: \"∈\", snippet: \"\\\\in\", titulo: \"pertence a\" },\n { label: \"∉\", snippet: \"\\\\notin\", titulo: \"não pertence a\" },\n { label: \"⊂\", snippet: \"\\\\subset\", titulo: \"contido em\" },\n { label: \"⊆\", snippet: \"\\\\subseteq\", titulo: \"contido ou igual a\" },\n { label: \"∪\", snippet: \"\\\\cup\", titulo: \"união\" },\n { label: \"∩\", snippet: \"\\\\cap\", titulo: \"interseção\" },\n { label: \"∅\", snippet: \"\\\\varnothing\", titulo: \"conjunto vazio\" },\n { label: \"ℕ\", snippet: \"\\\\mathbb{N}\", titulo: \"conjunto dos naturais\" },\n { label: \"ℤ\", snippet: \"\\\\mathbb{Z}\", titulo: \"conjunto dos inteiros\" },\n { label: \"ℚ\", snippet: \"\\\\mathbb{Q}\", titulo: \"conjunto dos racionais\" },\n { label: \"ℝ\", snippet: \"\\\\mathbb{R}\", titulo: \"conjunto dos reais\" },\n { label: \"ℂ\", snippet: \"\\\\mathbb{C}\", titulo: \"conjunto dos complexos\" },\n { label: \"∀\", snippet: \"\\\\forall\", titulo: \"para todo\" },\n { label: \"∃\", snippet: \"\\\\exists\", titulo: \"existe\" },\n { label: \"{ }\", snippet: \"\\\\{{}\\\\}\", titulo: \"chaves de conjunto\" },\n ],\n },\n {\n nome: \"Funções\",\n colunas: 6,\n itens: [\n { label: \"sen\", snippet: \"\\\\operatorname{sen}\", titulo: \"seno\" },\n { label: \"cos\", snippet: \"\\\\cos\", titulo: \"cosseno\" },\n { label: \"tg\", snippet: \"\\\\operatorname{tg}\", titulo: \"tangente\" },\n { label: \"cotg\", snippet: \"\\\\operatorname{cotg}\", titulo: \"cotangente\" },\n { label: \"log\", snippet: \"\\\\log_{}\", titulo: \"logaritmo\" },\n { label: \"ln\", snippet: \"\\\\ln\", titulo: \"logaritmo natural\" },\n { label: \"exp\", snippet: \"\\\\exp\", titulo: \"exponencial\" },\n ],\n },\n {\n nome: \"Setas\",\n colunas: 6,\n itens: [\n { label: \"→\", snippet: \"\\\\to\", titulo: \"tende a / vai para\" },\n { label: \"←\", snippet: \"\\\\leftarrow\", titulo: \"vem de / sentido inverso\" },\n { label: \"⇒\", snippet: \"\\\\Rightarrow\", titulo: \"implica em\" },\n { label: \"⇔\", snippet: \"\\\\Leftrightarrow\", titulo: \"se e somente se\" },\n { label: \"⇌\", snippet: \"\\\\rightleftharpoons\", titulo: \"equilíbrio químico\" },\n { label: \"vetor AB\", snippet: \"\\\\overrightarrow{}\", titulo: \"vetor entre dois pontos (AB)\" },\n { label: \"vetor\", snippet: \"\\\\vec{}\", titulo: \"vetor (física)\" },\n { label: \"∠\", snippet: \"\\\\angle\", titulo: \"ângulo\" },\n { label: \"△\", snippet: \"\\\\triangle\", titulo: \"triângulo\" },\n ],\n },\n];\n\n/**\n * Traduz o snippet da paleta para o dialeto de inserção do MathLive.\n *\n * O dado fica em `{}` e não em `#?` porque `#` é `macro parameter character`\n * no MathJax: guardar `#?` faria o `formulaPaleta.render.test.ts`, que passa\n * os 82 itens pelo renderer do documento, falhar em toda estrutura.\n *\n * Cada `{}` vira `{#?}`, COM as chaves, e uniformemente — não há caso\n * especial. Isso foi medido no navegador contra as 19 estruturas da paleta\n * que têm `{}`, e não deduzido:\n *\n * `{}` → `{#?}` 19/19 produzem `\\placeholder{}` no `getValue()`\n * `{}` → `#?` 17/19 — `\\vec{}` e `\\overrightarrow{}` saem malformados\n * (`\\vec{\\placeholder}{}`), porque um macro que pega\n * argumento por posição agarra só o `#` e deixa o `?`\n *\n * Se for tentador \"simplificar\" tirando as chaves porque os atalhos do\n * próprio MathLive usam `#?` solto (`\\sum_{#?}^{#?}` tem, a célula de\n * `pmatrix` não tem): as duas formas funcionam para a maioria, e a com\n * chaves funciona para todas.\n *\n * Conferido nos 82 itens (teste abaixo): nenhum tem `{}` que não seja\n * destino de digitação — `\\operatorname{sen}`, `\\mathbb{R}` e\n * `\\begin{pmatrix}` têm conteúdo entre as chaves, então não batem com o\n * padrão vazio.\n */\nexport function paraMathlive(snippet: string): string {\n return snippet.replaceAll(\"{}\", \"{#?}\");\n}\n\n/**\n * Traduz o snippet da paleta para o dialeto de PREVIEW do botão (via\n * `convertLatexToMarkup`) — não confundir com `paraMathlive` acima, que é\n * para inserção no campo.\n *\n * `convertLatexToMarkup` é um renderer estático (sem interatividade de\n * mathfield): passado o `{}` cru, `^{}` e `_{}` — Expoente e Índice — saem\n * **completamente vazios**. Não é feio, é NADA: `convertLatexToMarkup(\"^{}\")`\n * devolve `<span class=\"ML__msubsup\"></span>`, sem glifo, sem linha, sem\n * caixa — verificado com `node` contra o `mathlive/ssr` real, não deduzido.\n * O botão de Expoente ficaria com o mesmo problema que esta tarefa existe\n * pra resolver: sem forma nenhuma, pior que a palavra que troca.\n *\n * `\\placeholder{}` (o marcador visual do MathLive) NÃO ajuda aqui: também\n * verificado, `convertLatexToMarkup(\"\\\\placeholder{}\")` devolve\n * `<span class=\"ML__base\"> </span>` — um espaço, nada visível. O placeholder\n * tracejado do Docs é comportamento do mathfield interativo, não deste\n * renderer estático.\n *\n * O que funciona, testado nos mesmos 82 itens (teste abaixo): trocar cada\n * `{}` por `{□}` (U+25A1 WHITE SQUARE). Mesma regra do `paraMathlive` —\n * SEMPRE com as chaves, sem caso especial — pelo mesmo motivo: `\\vec{}` e\n * `\\overrightarrow{}` são macro de um argumento por posição, e `\\vec□` sem\n * chaves arriscaria o mesmo problema de argumento mal agarrado que motivou\n * manter as chaves lá.\n */\nexport function paraMarkup(snippet: string): string {\n return snippet.replaceAll(\"{}\", \"{□}\");\n}\n\n/**\n * Decide o conteúdo do botão da paleta: markup renderizado quando o renderer\n * do MathLive já carregou, senão o rótulo em texto.\n *\n * Existe como função separada por um motivo concreto, não por estética: no\n * jsdom o `import(\"mathlive\")` do modal não entrega o `convertLatexToMarkup`\n * a tempo, então o botão sempre cai no rótulo e **nenhum teste de componente\n * consegue exercitar o caminho do markup**. Medido: trocar `paraMarkup` por\n * `paraMathlive` na ligação deixava a suíte inteira verde (648/648) com os\n * botões de Fração e Expoente renderizando VAZIO — porque\n * `convertLatexToMarkup(\"^{#?}\")` devolve markup sem glifo nenhum, contra\n * `\"^{□}\"` do `paraMarkup`.\n *\n * Isolado assim, o teste chama direto e a troca morre.\n */\nexport function conteudoDoBotao(\n markup: ((latex: string) => string) | null,\n snippet: string,\n label: string,\n):\n | { dangerouslySetInnerHTML: { __html: string } }\n | { children: string } {\n if (!markup) return { children: label };\n return { dangerouslySetInnerHTML: { __html: markup(paraMarkup(snippet)) } };\n}\n","/** O que o MathLive serializa numa caixa **nunca tocada**. */\nexport const PLACEHOLDER = \"\\\\placeholder{}\";\n\n/**\n * Grupo vazio: `{}` ou `{ }`.\n *\n * É como volta uma caixa que o professor PREENCHEU e depois esvaziou —\n * `\\frac{1}{}` de quem apertou Backspace no denominador para corrigir. O\n * `\\placeholder{}` não cobre esse caso: ele marca só o que nunca foi tocado.\n */\nconst GRUPO_VAZIO = /\\{\\s*\\}/;\n\n/**\n * Célula vazia dentro de um ambiente de array (`pmatrix`, `cases`…).\n *\n * Célula esvaziada não volta como `{}` — volta como **nada**, entre dois\n * separadores: `\\begin{pmatrix}1 & 2\\\\ 3 & \\end{pmatrix}`. Nenhuma regex de\n * grupo alcança isso, e a matriz é o item da paleta com mais caixas.\n */\nconst CELULA_VAZIA = /(?:\\\\begin\\{[a-zA-Z]+\\*?\\}|&|\\\\\\\\)\\s*(?:&|\\\\\\\\|\\\\end\\{)/;\n\ntype Preview = { ok: boolean; error?: string } | null;\n\n/**\n * Três travas, porque uma fórmula pela metade **não** entra em branco no\n * documento — entra quebrada ou mutilada, e sem aviso.\n *\n * O caso que motivou as duas travas novas, medido: a professora clica em\n * Fração, preenche, e aperta Backspace uma vez para corrigir o denominador.\n * O campo vira `\\frac{1}{}`, o MathJax renderiza com `ok: true` um SVG de\n * 919 bytes com um único glifo (o \"1\", sem barra e sem denominador), e o\n * botão Inserir ficava **habilitado, sem mensagem nenhuma**. Nove formas\n * passavam assim: `\\frac{}{2}`, `x^{}`, `x_{}`, `\\sqrt{}`, `\\log_{}`,\n * `\\left|{}\\right|`, célula de matriz e linha de sistema.\n *\n * NÃO trocar por `getValue('latex-without-placeholders')`, que o MathLive\n * oferece: ele apaga a marca e devolve `\\frac{}{}`, que renderiza vazio —\n * é exatamente o bug que este gate existe para fechar.\n */\nexport function podeInserir(latex: string, preview: Preview): boolean {\n if (motivoBloqueio(latex, preview) !== null) return false;\n return latex.trim() !== \"\" && preview?.ok === true;\n}\n\nexport function motivoBloqueio(latex: string, preview: Preview): string | null {\n if (\n latex.includes(PLACEHOLDER) ||\n GRUPO_VAZIO.test(latex) ||\n CELULA_VAZIA.test(latex)\n ) {\n return \"Preencha os campos em branco da fórmula.\";\n }\n if (preview && !preview.ok) return preview.error ?? null;\n return null;\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type JSX,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { ImageEmbed } from \"@sofereditor/core\";\nimport { findBlockIndex, getFragmentForOffset } from \"./dom-bridge\";\n\ninterface ImageResizeOverlayProps {\n rootRef: RefObject<HTMLDivElement>;\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n embed: ImageEmbed;\n /** Called once on pointer-up with the final width/height (in CSS px). */\n onCommit: (width: number, height: number) => void;\n /**\n * Called once on pointer-up after a move-drag with the new offset\n * coordinates (CSS px). Only fired when `embed.layout` is non-inline.\n */\n onCommitMove?: (offsetX: number, offsetY: number) => void;\n /**\n * Called once on pointer-up after a move-drag when the image was dropped on a\n * DIFFERENT page than its current anchor. Re-anchors the embed to a block on\n * the destination page. Only fired for non-inline layouts.\n */\n onReanchor?: (\n to: { blockIndex: number; offset: number; cellIndex?: number },\n newOffsetX: number,\n newOffsetY: number,\n ) => void;\n}\n\ntype Corner = \"nw\" | \"ne\" | \"sw\" | \"se\";\n\ninterface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\n/**\n * Floating overlay drawn over the currently-selected image embed: 4 corner\n * handles + outline. Drag a handle to resize keeping aspect ratio. Changes are\n * applied live to the `<img>` style during the drag (no Y.Doc transactions per\n * frame); a single commit happens on pointer-up so Undo treats it as one step.\n */\nexport function ImageResizeOverlay({\n rootRef,\n blockIndex,\n offset,\n cellIndex,\n embed,\n onCommit,\n onCommitMove,\n onReanchor,\n}: ImageResizeOverlayProps): JSX.Element | null {\n const [rect, setRect] = useState<Rect | null>(null);\n const [isMoving, setIsMoving] = useState(false);\n const imgRef = useRef<HTMLImageElement | null>(null);\n\n const locateImage = useCallback((): HTMLImageElement | null => {\n const root = rootRef.current;\n if (!root) return null;\n let container: HTMLElement | null = null;\n if (cellIndex != null) {\n container = root.querySelector<HTMLElement>(\n `[data-block-index=\"${blockIndex}\"] [data-cell-index=\"${cellIndex}\"]`,\n );\n } else {\n container = getFragmentForOffset(root, blockIndex, offset);\n }\n if (!container) return null;\n // The renderInline data-embed-offset is LOCAL to the fragment / container.\n const fragmentStart = container.dataset.fragmentStart\n ? Number.parseInt(container.dataset.fragmentStart, 10) || 0\n : 0;\n const localOffset = offset - fragmentStart;\n return container.querySelector<HTMLImageElement>(\n `img[data-embed=\"image\"][data-embed-offset=\"${localOffset}\"]`,\n );\n }, [rootRef, blockIndex, offset, cellIndex]);\n\n const measure = useCallback(() => {\n const img = locateImage();\n if (!img) {\n setRect((prev) => (prev === null ? prev : null));\n return;\n }\n imgRef.current = img;\n const root = rootRef.current;\n if (!root) return;\n const r = img.getBoundingClientRect();\n const rr = root.getBoundingClientRect();\n const next: Rect = {\n top: r.top - rr.top + root.scrollTop,\n left: r.left - rr.left + root.scrollLeft,\n width: r.width,\n height: r.height,\n };\n setRect((prev) =>\n prev &&\n prev.top === next.top &&\n prev.left === next.left &&\n prev.width === next.width &&\n prev.height === next.height\n ? prev\n : next,\n );\n }, [locateImage, rootRef]);\n\n useLayoutEffect(() => {\n measure();\n // Pagination runs in a SEPARATE effect after this one, on LATER frames. So\n // right after an embed prop change OR a re-anchor (blockIndex/offset change\n // from a cross-page drag), the target <img> may not be locatable yet —\n // `measure()` sets rect=null and the overlay renders nothing. Re-measure\n // across a few frames UNTIL the image is found (rect set), then stop.\n // A single rAF wasn't enough: pagination can take several frames to place\n // the re-anchored block, leaving the overlay hidden until the next\n // selection change (bug: \"can't click to select the image right after it\n // re-anchors onto the new page\").\n let cancelled = false;\n let frame = 0;\n let rafId = 0;\n const remeasure = () => {\n if (cancelled) return;\n measure();\n // Keep retrying while the image still isn't locatable (bounded budget).\n if (!locateImage() && frame++ < 30) {\n rafId =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(remeasure)\n : (queueMicrotask(remeasure), 0);\n }\n };\n rafId =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(remeasure)\n : (queueMicrotask(remeasure), 0);\n return () => {\n cancelled = true;\n if (typeof cancelAnimationFrame === \"function\" && typeof rafId === \"number\" && rafId > 0) {\n cancelAnimationFrame(rafId);\n }\n };\n }, [\n measure,\n locateImage,\n embed.width,\n embed.height,\n embed.align,\n embed.src,\n embed.layout,\n embed.offsetX,\n embed.offsetY,\n ]);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onScroll = () => measure();\n window.addEventListener(\"resize\", onScroll);\n root.addEventListener(\"scroll\", onScroll);\n return () => {\n window.removeEventListener(\"resize\", onScroll);\n root.removeEventListener(\"scroll\", onScroll);\n };\n }, [measure, rootRef]);\n\n /*\n * The `useLayoutEffect` above re-measures synchronously when embed props\n * change — but the pagination engine runs its own re-layout in a SEPARATE\n * effect right after. That post-pagination shift can move the image by\n * dozens of pixels (e.g. when `align: center` flips it to `display: block`\n * and the paragraph reflows), leaving the overlay at the pre-shift\n * coordinates.\n *\n * A ResizeObserver on both the image AND the editor root fires whenever\n * either box changes — covers reflow, resize, font load, page repagination,\n * and image-load-late-decoded scenarios. Falls back to no-op in jsdom etc.\n */\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") return;\n const img = locateImage();\n const root = rootRef.current;\n if (!img || !root) return;\n const ro = new ResizeObserver(() => measure());\n ro.observe(img);\n ro.observe(root);\n return () => ro.disconnect();\n }, [locateImage, measure, rootRef, embed.layout, embed.align, embed.src]);\n\n if (!rect) return null;\n\n const layout = embed.layout ?? \"inline\";\n const movable = layout !== \"inline\" && onCommitMove !== undefined;\n\n return (\n <div\n className=\"ed-image-overlay\"\n style={{ top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n contentEditable={false}\n >\n {isMoving && (\n // Non-clipped drag preview (Tier 2): the overlay lives in .ed-root and\n // follows the cursor, so the image stays visible while it crosses the\n // page boundary (the real, clipped figure is hidden during the drag).\n // Inline styles only — the package does not ship CSS.\n <img\n src={embed.src}\n alt=\"\"\n aria-hidden\n draggable={false}\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"fill\",\n opacity: 0.8,\n pointerEvents: \"none\",\n }}\n />\n )}\n {movable && (\n <MoveHandle\n layout={layout}\n startOffsetX={embed.offsetX ?? 0}\n startOffsetY={embed.offsetY ?? 0}\n imgRef={imgRef}\n onCommit={onCommitMove!}\n onLiveChange={measure}\n rootRef={rootRef}\n blockIndex={blockIndex}\n offset={offset}\n cellIndex={cellIndex}\n onReanchor={onReanchor}\n onMoveStart={() => setIsMoving(true)}\n onMoveEnd={() => setIsMoving(false)}\n />\n )}\n <Handle\n corner=\"nw\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n <Handle\n corner=\"ne\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n <Handle\n corner=\"sw\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n <Handle\n corner=\"se\"\n startW={embed.width}\n startH={embed.height}\n imgRef={imgRef}\n onCommit={onCommit}\n onLiveChange={measure}\n />\n </div>\n );\n}\n\ninterface MoveHandleProps {\n layout: \"wrap-left\" | \"wrap-right\" | \"behind\" | \"front\" | \"inline\";\n startOffsetX: number;\n startOffsetY: number;\n imgRef: RefObject<HTMLImageElement | null>;\n onCommit: (ox: number, oy: number) => void;\n onLiveChange: () => void;\n rootRef: RefObject<HTMLDivElement>;\n blockIndex: number;\n offset: number;\n cellIndex?: number;\n onReanchor?: (\n to: { blockIndex: number; offset: number; cellIndex?: number },\n newOffsetX: number,\n newOffsetY: number,\n ) => void;\n /** Fired when a move-drag starts/ends, so the overlay can show a drag preview. */\n onMoveStart?: () => void;\n onMoveEnd?: () => void;\n}\n\nfunction MoveHandle({\n layout,\n startOffsetX,\n startOffsetY,\n imgRef,\n onCommit,\n onLiveChange,\n rootRef,\n blockIndex,\n offset,\n cellIndex,\n onReanchor,\n onMoveStart,\n onMoveEnd,\n}: MoveHandleProps): JSX.Element {\n const dragRef = useRef<{\n startX: number;\n startY: number;\n liveOX: number;\n liveOY: number;\n } | null>(null);\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return;\n if (!imgRef.current) return;\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n dragRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n liveOX: startOffsetX,\n liveOY: startOffsetY,\n };\n // Hide the real (clipped) image and show a non-clipped preview in the\n // overlay for the duration of the drag, so the image stays visible while\n // it crosses the page boundary (Tier 2). The figure keeps being moved\n // below (drives the overlay/preview position via onLiveChange) — it's just\n // invisible. Restored in finish().\n const styled = imgRef.current.closest<HTMLElement>(\".ed-figure\") ?? imgRef.current;\n styled.style.opacity = \"0\";\n onMoveStart?.();\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d || !imgRef.current) return;\n const dx = e.clientX - d.startX;\n const dy = e.clientY - d.startY;\n // Post-#11 the positioned/styled element is the <figure> wrapper, not the\n // <img> (which renders position:static inside it). Mutate the figure so the\n // live drag actually moves the image; fall back to the img defensively.\n const styled = imgRef.current.closest<HTMLElement>(\".ed-figure\") ?? imgRef.current;\n let nox = startOffsetX;\n let noy = startOffsetY;\n if (layout === \"wrap-left\") {\n // dx → right margin (gap to text); negative dx clamped to 8 (min gap).\n nox = Math.max(8, startOffsetX + dx);\n noy = Math.max(0, startOffsetY + dy);\n styled.style.marginRight = `${nox}px`;\n styled.style.marginTop = `${noy}px`;\n } else if (layout === \"wrap-right\") {\n // dx is mirrored: dragging LEFT increases the gap to text.\n nox = Math.max(8, startOffsetX - dx);\n noy = Math.max(0, startOffsetY + dy);\n styled.style.marginLeft = `${nox}px`;\n styled.style.marginTop = `${noy}px`;\n } else if (layout === \"behind\" || layout === \"front\") {\n nox = startOffsetX + dx;\n noy = startOffsetY + dy;\n styled.style.left = `${nox}px`;\n styled.style.top = `${noy}px`;\n }\n d.liveOX = nox;\n d.liveOY = noy;\n onLiveChange();\n };\n\n // Clamp a behind/front vertical offset so the image stays within `frag`'s\n // page content box (never clipped by .ed-page-content overflow:hidden).\n // Offsets are fragment-local (position:absolute relative to the .ed-block).\n // Used for BOTH the same-page path and the re-anchor path (dest fragment).\n const clampOffsetToFragment = (frag: HTMLElement, oy: number): number => {\n const img = imgRef.current;\n if (!img) return oy;\n const page = frag.closest<HTMLElement>(\".ed-page\");\n const content = page?.querySelector<HTMLElement>(\".ed-page-content\");\n if (!content) return oy;\n const fragRect = frag.getBoundingClientRect();\n const contentRect = content.getBoundingClientRect();\n const imgH = img.getBoundingClientRect().height;\n // Allowed top range in fragment-local px: [contentTop, contentBottom-imgH].\n const minLocalTop = contentRect.top - fragRect.top;\n const maxLocalTop = contentRect.bottom - fragRect.top - imgH;\n return Math.round(Math.max(minLocalTop, Math.min(oy, maxLocalTop)));\n };\n\n const clampedCommit = (ox: number, oy: number) => {\n const img = imgRef.current;\n const root = rootRef.current;\n if (!img || !root || (layout !== \"behind\" && layout !== \"front\")) {\n onCommit(ox, oy);\n return;\n }\n const frag = getFragmentForOffset(root, blockIndex, offset);\n if (!frag) {\n onCommit(ox, oy);\n return;\n }\n onCommit(ox, clampOffsetToFragment(frag, oy));\n };\n\n const finish = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d) return;\n dragRef.current = null;\n try {\n (e.target as HTMLElement).releasePointerCapture(e.pointerId);\n } catch {\n // ignore\n }\n\n // End the drag preview and restore the real image's visibility. Restoring\n // imperatively (not just relying on React's commit re-render) avoids a\n // one-frame flicker between the commit and the re-render.\n if (imgRef.current) {\n const styled = imgRef.current.closest<HTMLElement>(\".ed-figure\") ?? imgRef.current;\n styled.style.opacity = \"\";\n }\n onMoveEnd?.();\n\n const img = imgRef.current;\n const root = rootRef.current;\n const liveOX = Math.round(d.liveOX);\n const liveOY = Math.round(d.liveOY);\n\n // Tables / wrap-anchors-in-cell are out of scope: keep the current path.\n // Re-anchoring only happens for non-inline embeds dropped on a DIFFERENT\n // top-level page, with onReanchor wired.\n if (!img || !root || onReanchor === undefined || cellIndex != null) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n const imgRect = img.getBoundingClientRect();\n const centerY = imgRect.top + imgRect.height / 2;\n\n // Destination page by GEOMETRY. `elementFromPoint(center)` was unreliable:\n // it returns null in the gap between pages or off-viewport, so cross-page\n // drops failed to re-anchor and snapped back to page 1. Instead pick the\n // .ed-page whose box CONTAINS the image center, else the NEAREST page —\n // never null. imgRect and getBoundingClientRect share the viewport-relative\n // coordinate space, so the comparison holds at any scroll.\n const pages = Array.from(root.querySelectorAll<HTMLElement>(\".ed-page\"));\n let destPage: HTMLElement | null = null;\n let bestPageDist = Infinity;\n for (const pg of pages) {\n const r = pg.getBoundingClientRect();\n if (centerY >= r.top && centerY <= r.bottom) {\n destPage = pg;\n break;\n }\n const dist = centerY < r.top ? r.top - centerY : centerY - r.bottom;\n if (dist < bestPageDist) {\n bestPageDist = dist;\n destPage = pg;\n }\n }\n\n // Current anchor page = the page owning the current fragment.\n const currentFrag = getFragmentForOffset(root, blockIndex, offset);\n const currentPage = currentFrag?.closest<HTMLElement>(\".ed-page\") ?? null;\n\n // Same page (or no page resolved) → existing same-anchor path.\n if (destPage == null || destPage === currentPage) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n // ---- Re-anchor to a block on the destination page ----\n // Destination anchor block by GEOMETRY: the block fragment on destPage whose\n // top is nearest the dropped image's top (no elementFromPoint).\n const blocksOnPage = Array.from(\n destPage.querySelectorAll<HTMLElement>(\"[data-block-index]\"),\n );\n let destFrag: HTMLElement | null = null;\n let bestBlockDist = Infinity;\n for (const b of blocksOnPage) {\n const dist = Math.abs(b.getBoundingClientRect().top - imgRect.top);\n if (dist < bestBlockDist) {\n bestBlockDist = dist;\n destFrag = b;\n }\n }\n\n // Empty destination page (no block fragments) → keep same-anchor + clamp.\n if (!destFrag) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n const destBlockIndex = findBlockIndex(destFrag, root);\n if (destBlockIndex == null) {\n clampedCommit(liveOX, liveOY);\n return;\n }\n\n // Destination offset = the fragment's first rendered offset (fragmentStart),\n // so the anchor lands in the FRACTION of the block that lives on this page.\n const destOffset = destFrag.dataset.fragmentStart\n ? Number.parseInt(destFrag.dataset.fragmentStart, 10) || 0\n : 0;\n\n // Recompute absolute coords relative to the destination fragment box, and\n // CLAMP so the re-anchored image is never clipped on the destination page.\n // behind/front render position:absolute; left:ox; top:oy relative to the\n // .ed-block fragment (position:relative). So ox = imgLeft - fragLeft.\n const fragRect = destFrag.getBoundingClientRect();\n const newOffsetX = Math.round(imgRect.left - fragRect.left);\n const newOffsetY = clampOffsetToFragment(\n destFrag,\n Math.round(imgRect.top - fragRect.top),\n );\n\n onReanchor(\n { blockIndex: destBlockIndex, offset: destOffset, cellIndex: undefined },\n newOffsetX,\n newOffsetY,\n );\n };\n\n return (\n <div\n className=\"ed-image-move\"\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={finish}\n onPointerCancel={finish}\n />\n );\n}\n\ninterface HandleProps {\n corner: Corner;\n startW: number;\n startH: number;\n imgRef: RefObject<HTMLImageElement | null>;\n onCommit: (w: number, h: number) => void;\n onLiveChange: () => void;\n}\n\nconst MIN_W = 20;\n\nfunction Handle({\n corner,\n startW,\n startH,\n imgRef,\n onCommit,\n onLiveChange,\n}: HandleProps): JSX.Element {\n const dragRef = useRef<{\n startX: number;\n startY: number;\n startW: number;\n startH: number;\n aspect: number;\n liveW: number;\n liveH: number;\n } | null>(null);\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return;\n if (!imgRef.current) return;\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const aspect = startW > 0 && startH > 0 ? startW / startH : 1;\n dragRef.current = {\n startX: e.clientX,\n startY: e.clientY,\n startW,\n startH,\n aspect,\n liveW: startW,\n liveH: startH,\n };\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d || !imgRef.current) return;\n const dxRaw = e.clientX - d.startX;\n // SE/NE handles grow with +dx; NW/SW grow with -dx.\n const dx = corner === \"se\" || corner === \"ne\" ? dxRaw : -dxRaw;\n let w = Math.max(MIN_W, d.startW + dx);\n let h = w / d.aspect;\n // Apply live to the img element directly — no transaction.\n imgRef.current.style.width = `${w}px`;\n imgRef.current.style.height = `${h}px`;\n imgRef.current.width = Math.round(w);\n imgRef.current.height = Math.round(h);\n d.liveW = w;\n d.liveH = h;\n onLiveChange();\n };\n\n const finish = (e: ReactPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current;\n if (!d) return;\n dragRef.current = null;\n try {\n (e.target as HTMLElement).releasePointerCapture(e.pointerId);\n } catch {\n // ignore\n }\n onCommit(Math.round(d.liveW), Math.round(d.liveH));\n };\n\n return (\n <div\n className={`ed-image-handle ${corner}`}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={finish}\n onPointerCancel={finish}\n />\n );\n}\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type JSX,\n type RefObject,\n} from \"react\";\nimport {\n decodeSelection,\n type EditorDocument,\n type EncodedCursor,\n} from \"@sofereditor/core\";\nimport { locatePoint } from \"./dom-bridge\";\n\n/**\n * Subconjunto mínimo da Awareness do y-protocols que o overlay usa. Tipar assim\n * evita que `@sofereditor/react` dependa de `y-protocols` — o objeto vem de fora\n * (de `useCollab().binding.awareness`) e é estruturalmente compatível.\n */\nexport interface AwarenessLike {\n clientID: number;\n getStates(): Map<number, Record<string, unknown>>;\n on(event: \"change\", cb: () => void): void;\n off(event: \"change\", cb: () => void): void;\n setLocalStateField(field: string, value: unknown): void;\n}\n\ninterface Rect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\ninterface PeerCursorView {\n clientId: number;\n name: string;\n color: string;\n caret: { top: number; left: number; height: number } | null;\n selection: Rect[];\n}\n\ninterface RemoteCursorsOverlayProps {\n rootRef: RefObject<HTMLDivElement>;\n doc: EditorDocument;\n awareness: AwarenessLike;\n /** Muda a cada edição/repaginação → re-medir os carets remotos. */\n revision?: unknown;\n}\n\nconst DEFAULT_COLOR = \"#3b82f6\";\n\n/**\n * Desenha o caret + seleção + nome de cada usuário remoto sobre o conteúdo\n * paginado. Posições remotas chegam via awareness (campo `cursor`, codificado\n * com Y.RelativePosition pelo core) e são resolvidas para coordenadas DOM com\n * o mesmo bridge da seleção local — ciente de fragmentação por página.\n */\nexport function RemoteCursorsOverlay({\n rootRef,\n doc,\n awareness,\n revision,\n}: RemoteCursorsOverlayProps): JSX.Element | null {\n const [views, setViews] = useState<PeerCursorView[]>([]);\n const rafRef = useRef<number | null>(null);\n\n const measure = useCallback(() => {\n const root = rootRef.current;\n if (!root) {\n setViews((prev) => (prev.length === 0 ? prev : []));\n return;\n }\n const rr = root.getBoundingClientRect();\n const out: PeerCursorView[] = [];\n\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === awareness.clientID) return;\n const s = state as {\n user?: { name?: string; color?: string };\n cursor?: EncodedCursor;\n };\n if (!s.cursor) return;\n const sel = decodeSelection(doc, s.cursor);\n if (!sel) return;\n\n const color = s.user?.color || DEFAULT_COLOR;\n const name = s.user?.name || \"Anônimo\";\n\n const focus = locatePoint(root, sel.focus);\n let caret: PeerCursorView[\"caret\"] = null;\n if (focus) {\n try {\n const range = document.createRange();\n range.setStart(focus.node, focus.offset);\n range.collapse(true);\n let rect = range.getBoundingClientRect();\n if (rect.height === 0 && focus.node.nodeType === Node.ELEMENT_NODE) {\n rect = (focus.node as HTMLElement).getBoundingClientRect();\n }\n caret = {\n top: rect.top - rr.top + root.scrollTop,\n left: rect.left - rr.left + root.scrollLeft,\n height: rect.height || 18,\n };\n } catch {\n /* range inválido — sem caret */\n }\n }\n\n const collapsed =\n sel.anchor.blockIndex === sel.focus.blockIndex &&\n (sel.anchor.cellIndex ?? -1) === (sel.focus.cellIndex ?? -1) &&\n sel.anchor.offset === sel.focus.offset;\n\n const selection: Rect[] = [];\n if (!collapsed && focus) {\n const anchor = locatePoint(root, sel.anchor);\n if (anchor) {\n try {\n const r = document.createRange();\n r.setStart(anchor.node, anchor.offset);\n r.setEnd(focus.node, focus.offset);\n if (r.collapsed) {\n r.setStart(focus.node, focus.offset);\n r.setEnd(anchor.node, anchor.offset);\n }\n for (const cr of Array.from(r.getClientRects())) {\n if (cr.width === 0 || cr.height === 0) continue;\n selection.push({\n top: cr.top - rr.top + root.scrollTop,\n left: cr.left - rr.left + root.scrollLeft,\n width: cr.width,\n height: cr.height,\n });\n }\n } catch {\n /* seleção cross-fragmento inválida — ignora */\n }\n }\n }\n\n out.push({ clientId, name, color, caret, selection });\n });\n\n setViews(out);\n }, [rootRef, doc, awareness]);\n\n const scheduleMeasure = useCallback(() => {\n if (rafRef.current != null) return;\n const raf =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame\n : (cb: FrameRequestCallback) => setTimeout(() => cb(0), 16) as unknown as number;\n rafRef.current = raf(() => {\n rafRef.current = null;\n measure();\n });\n }, [measure]);\n\n // Awareness: re-mede quando qualquer peer muda cursor/entra/sai.\n useEffect(() => {\n const onChange = () => scheduleMeasure();\n awareness.on(\"change\", onChange);\n scheduleMeasure();\n return () => awareness.off(\"change\", onChange);\n }, [awareness, scheduleMeasure]);\n\n // Edição/repaginação local.\n useEffect(() => {\n scheduleMeasure();\n }, [revision, scheduleMeasure]);\n\n // Resize do root + scroll re-posicionam os carets.\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n const onScroll = () => scheduleMeasure();\n root.addEventListener(\"scroll\", onScroll, { passive: true });\n let ro: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\") {\n ro = new ResizeObserver(() => scheduleMeasure());\n ro.observe(root);\n }\n return () => {\n root.removeEventListener(\"scroll\", onScroll);\n ro?.disconnect();\n };\n }, [rootRef, scheduleMeasure]);\n\n if (views.length === 0) return null;\n\n const layer: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n pointerEvents: \"none\",\n zIndex: 6,\n };\n\n return (\n <div className=\"ed-remote-cursors\" aria-hidden style={layer}>\n {views.map((v) => (\n <div key={v.clientId}>\n {v.selection.map((r, i) => (\n <div\n key={i}\n style={{\n position: \"absolute\",\n top: r.top,\n left: r.left,\n width: r.width,\n height: r.height,\n background: v.color,\n opacity: 0.22,\n borderRadius: 2,\n }}\n />\n ))}\n {v.caret && (\n <div\n style={{\n position: \"absolute\",\n top: v.caret.top,\n left: v.caret.left,\n height: v.caret.height,\n width: 2,\n background: v.color,\n }}\n >\n <span\n style={{\n position: \"absolute\",\n top: -15,\n left: -1,\n fontSize: 10,\n lineHeight: \"14px\",\n padding: \"0 4px\",\n borderRadius: 3,\n color: \"#fff\",\n background: v.color,\n whiteSpace: \"nowrap\",\n fontFamily: \"system-ui, sans-serif\",\n userSelect: \"none\",\n }}\n >\n {v.name}\n </span>\n </div>\n )}\n </div>\n ))}\n </div>\n );\n}\n","import { useMemo, useRef, type CSSProperties, type JSX, type ReactNode } from \"react\";\nimport {\n answerLineStyle,\n cellBorderStyle,\n deltaLength,\n normalizarLarguras,\n splitDeltaByLines,\n tableRectSelection,\n type BlockAttrs,\n type CellAttrs,\n type DeltaOp,\n type SerializedBlock,\n type StyleRecord,\n} from \"@sofereditor/core\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { renderInline } from \"./renderInline\";\nimport { sliceDelta } from \"./sliceDelta\";\nimport { TableResizeOverlay } from \"./TableResizeOverlay\";\n\nexport interface NodeViewFragment {\n index: number;\n start: number;\n end: number;\n}\n\ninterface NodeViewProps {\n block: SerializedBlock;\n index: number;\n /** Optional nested content. Used by `listItem` to host the nested <ol>/<ul>. */\n children?: ReactNode;\n /** Optional fragment slice — when present, the rendered DOM covers only chars [start, end). */\n fragment?: NodeViewFragment;\n /**\n * Optional table-row fragmentation slice for Sub-phase 4.6. When set, only\n * the rows in `[rowStart, rowEnd)` are rendered, and the resize overlay is\n * suppressed except on the FIRST fragment.\n */\n tableFragment?: { index: number; rowStart: number; rowEnd: number };\n}\n\nexport function NodeView({ block, index, children, fragment, tableFragment }: NodeViewProps): JSX.Element {\n if (block.type === \"table\") {\n return <TableView block={block} index={index} tableFragment={tableFragment} />;\n }\n const inline = fragment\n ? renderInline(sliceDelta(block.delta, fragment.start, fragment.end), index)\n : renderInline(block.delta, index);\n const common = commonBlockProps(block.attrs, index, block.type, fragment);\n\n switch (block.type) {\n case \"heading\": {\n const level = clampLevel(block.attrs.level);\n const Tag = (`h${level}` as unknown) as keyof JSX.IntrinsicElements;\n return (\n <Tag\n {...common}\n className={`ed-block ed-heading ed-heading-${level}${alignClass(block.attrs.align)}${fragmentClass(fragment)}`}\n >\n {inline}\n </Tag>\n );\n }\n case \"blockquote\":\n return (\n <blockquote\n {...common}\n className={`ed-block ed-blockquote${alignClass(block.attrs.align)}${fragmentClass(fragment)}`}\n >\n {inline}\n </blockquote>\n );\n case \"codeBlock\":\n return (\n <pre\n {...common}\n data-language={block.attrs.language ?? undefined}\n className={`ed-block ed-codeblock${alignClass(block.attrs.align)}`}\n >\n <code>{inline}</code>\n </pre>\n );\n case \"listItem\": {\n const level = clampListLevel(block.attrs.listLevel);\n return (\n <li\n {...common}\n data-list-kind={block.attrs.listKind ?? \"bullet\"}\n data-list-level={level}\n className={`ed-block ed-listitem${alignClass(block.attrs.align)}`}\n >\n {inline}\n {children}\n </li>\n );\n }\n case \"paragraph\":\n default:\n return (\n <div\n {...common}\n className={`ed-block ed-paragraph${alignClass(block.attrs.align)}${fragmentClass(fragment)}`}\n >\n {inline}\n </div>\n );\n }\n}\n\nexport function commonBlockProps(\n attrs: BlockAttrs,\n index: number,\n type: string,\n fragment: NodeViewFragment | undefined,\n) {\n const style: CSSProperties = {};\n if (attrs.align) style.textAlign = attrs.align;\n // Linha de resposta: régua inferior + entrelinha. Vem de um helper puro em\n // core para o HTML de servidor emitir exatamente o mesmo estilo.\n Object.assign(style, answerLineStyle(attrs) ?? {});\n const base: Record<string, unknown> = {\n \"data-block-index\": index,\n \"data-block-type\": type,\n dir: attrs.dir,\n style: Object.keys(style).length > 0 ? style : undefined,\n };\n if (!fragment) return base;\n base[\"data-fragment-index\"] = fragment.index;\n base[\"data-fragment-start\"] = fragment.start;\n base[\"data-fragment-end\"] = fragment.end;\n return base;\n}\n\nfunction fragmentClass(f?: NodeViewFragment): string {\n if (!f) return \"\";\n // First fragment keeps top margin, last keeps bottom; intermediate fragments drop both.\n return ` ed-fragment ed-fragment-${f.index}`;\n}\n\nfunction alignClass(a?: BlockAttrs[\"align\"]): string {\n return a ? ` ed-align-${a}` : \"\";\n}\n\n/**\n * Inline style for a table cell's visual attrs (align, bgColor).\n * Returns undefined when the cell has none, so the <td> carries no style attr.\n */\nexport function cellStyle(\n attrs?: CellAttrs,\n border?: StyleRecord,\n): CSSProperties | undefined {\n const style: CSSProperties = { ...(border as CSSProperties | undefined) };\n if (attrs?.align) style.textAlign = attrs.align;\n if (attrs?.bgColor) style.backgroundColor = attrs.bgColor;\n return Object.keys(style).length > 0 ? style : undefined;\n}\n\nfunction clampLevel(l: BlockAttrs[\"level\"]): 1 | 2 | 3 | 4 | 5 | 6 {\n if (l === 2 || l === 3 || l === 4 || l === 5 || l === 6) return l;\n return 1;\n}\n\nfunction clampListLevel(l: BlockAttrs[\"listLevel\"]): number {\n if (typeof l !== \"number\" || !Number.isFinite(l)) return 0;\n return Math.max(0, Math.min(5, Math.trunc(l)));\n}\n\n/**\n * Conteúdo de um `<td>`. Sem `listKind`, é o delta cru (o `\\n` quebra linha via\n * `white-space: pre-wrap`). Com `listKind`, cada linha separada por `\\n` vira\n * um item.\n *\n * `data-cell-line` em TODO `<li>` é contrato com o `dom-bridge`: ele conta os\n * de índice maior que zero como um caractere do modelo (o `\\n` que sumiu do\n * texto do DOM). Mexer aqui sem mexer lá desloca o cursor.\n *\n * Cada linha vira um delta PRÓPRIO (`splitDeltaByLines`), então `renderInline`\n * por padrão numeraria `data-embed-offset` a partir de 0 dentro da linha. A\n * célula inteira é um único `Y.Text`, e é nesse espaço que `Editor.tsx` e o\n * `ImageResizeOverlay` interpretam o offset — por isso cada linha recebe um\n * `baseOffset` igual à soma dos comprimentos (em caracteres do modelo) das\n * linhas anteriores mais um `\\n` por linha anterior (o separador comido pelo\n * split).\n */\nfunction renderCellContent(\n attrs: CellAttrs | undefined,\n delta: DeltaOp[],\n keyPrefix: string,\n): ReactNode {\n const kind = attrs?.listKind;\n if (!kind) return renderInline(delta, keyPrefix);\n const linhas = splitDeltaByLines(delta);\n const Tag = kind === \"ordered\" ? \"ol\" : \"ul\";\n let baseOffset = 0;\n return (\n <Tag\n className={`ed-list ed-list-${kind}`}\n data-list-kind={kind}\n start={kind === \"ordered\" && typeof attrs?.listStart === \"number\" ? attrs.listStart : undefined}\n style={attrs?.listStyle ? { listStyleType: attrs.listStyle } : undefined}\n >\n {linhas.map((linha, i) => {\n const li = (\n <li key={i} className=\"ed-listitem\" data-cell-line={i}>\n {renderInline(linha, `${keyPrefix}-l${i}`, baseOffset)}\n </li>\n );\n baseOffset += deltaLength(linha) + 1;\n return li;\n })}\n </Tag>\n );\n}\n\ninterface TableViewProps {\n block: SerializedBlock;\n index: number;\n tableFragment?: { index: number; rowStart: number; rowEnd: number };\n}\n\nfunction TableView({ block, index, tableFragment }: TableViewProps): JSX.Element {\n const rows = clampPositive(block.attrs.rows);\n const cols = clampPositive(block.attrs.cols);\n const cells = block.cells ?? [];\n const widths = normalizarLarguras(block.attrs.colWidths as number[] | undefined, cols);\n const tableWidth = block.attrs.tableWidth;\n const rowHeights = block.attrs.rowHeights as number[] | undefined;\n const tableRef = useRef<HTMLTableElement | null>(null);\n\n // Row range to render. Default = full table.\n const rowStart = tableFragment?.rowStart ?? 0;\n const rowEnd = tableFragment?.rowEnd ?? rows;\n const isFirstFragment = !tableFragment || tableFragment.index === 0;\n\n // Recompute the rectangular cell selection each render. Reading the snapshot\n // off `editor.snapshot` ties this to the same render that NodeView is in.\n const editor = useEditorContext();\n const rect = useMemo(() => {\n if (editor.selection.anchor.blockIndex !== index) return null;\n if (editor.selection.focus.blockIndex !== index) return null;\n return tableRectSelection(editor.doc, editor.selection);\n }, [editor.doc, editor.selection, index]);\n\n return (\n <div className=\"ed-table-wrap\">\n <table\n ref={tableRef}\n data-block-index={index}\n data-block-type=\"table\"\n // Em todo fragmento, não só no primeiro: numa tabela quebrada entre\n // páginas cada fragmento é um <table> próprio.\n dir={block.attrs.dir === \"rtl\" || block.attrs.dir === \"ltr\" ? block.attrs.dir : undefined}\n className=\"ed-block ed-table\"\n style={typeof tableWidth === \"number\" ? { width: `${tableWidth}%` } : undefined}\n >\n <colgroup>\n {widths.map((w, c) => (\n <col key={c} style={{ width: `${w}%` }} />\n ))}\n </colgroup>\n <tbody>\n {Array.from({ length: rowEnd - rowStart }, (_, i) => {\n const r = rowStart + i;\n return (\n <tr\n key={r}\n data-cell-row={r}\n // `height` no <tr> é tratado pelo CSS de tabela como MÍNIMO, não\n // fixo — conteúdo maior empurra a linha. Não usar max-height nem\n // overflow: hidden aqui, ou texto some da prova.\n style={\n typeof rowHeights?.[r] === \"number\"\n ? { height: `${rowHeights[r]}px` }\n : undefined\n }\n >\n {Array.from({ length: cols }, (_, c) => {\n const flat = r * cols + c;\n const cell = cells[flat];\n if (cell?.attrs.covered === true) return null;\n const rowspan = spanAttr(cell?.attrs.rowspan);\n const colspan = spanAttr(cell?.attrs.colspan);\n const delta = cell?.delta ?? [];\n const inRect =\n rect !== null &&\n r >= rect.top && r <= rect.bottom &&\n c >= rect.left && c <= rect.right;\n // Cell align is applied via inline textAlign; block-level align uses ed-align-* classes instead.\n return (\n <td\n key={c}\n data-cell-index={flat}\n data-cell-row={r}\n data-cell-col={c}\n data-cell-rowspan={rowspan}\n data-cell-colspan={colspan}\n rowSpan={rowspan > 1 ? rowspan : undefined}\n colSpan={colspan > 1 ? colspan : undefined}\n style={cellStyle(\n cell?.attrs,\n // rowStart/rowEnd são os limites do FRAGMENTO: numa tabela\n // quebrada entre páginas, cada página fecha a própria caixa.\n cellBorderStyle(\n block.attrs.borderPreset,\n { row: r, col: c, rowspan, colspan, cols, rowStart, rowEnd },\n \"screen\",\n block.attrs.borderColor,\n ),\n )}\n className={inRect ? \"ed-cell ed-cell--selected\" : \"ed-cell\"}\n >\n {renderCellContent(cell?.attrs, delta, `t${index}-c${flat}`)}\n </td>\n );\n })}\n </tr>\n );\n })}\n </tbody>\n </table>\n {isFirstFragment && (\n <TableResizeOverlay\n tableRef={tableRef}\n blockIndex={index}\n cols={cols}\n rows={rows}\n rtl={block.attrs.dir === \"rtl\"}\n />\n )}\n </div>\n );\n}\n\nfunction spanAttr(v: unknown): number {\n if (typeof v !== \"number\" || !Number.isFinite(v) || v < 1) return 1;\n return Math.trunc(v);\n}\n\nfunction clampPositive(n: unknown): number {\n if (typeof n !== \"number\" || !Number.isFinite(n) || n <= 0) return 1;\n return Math.trunc(n);\n}\n","import { Fragment, memo, type CSSProperties, type ReactNode } from \"react\";\nimport {\n BLANK_STYLE,\n isImageEmbed,\n splitUnderscoreRuns,\n type DeltaOp,\n type ImageEmbed,\n type MarkAttrs,\n} from \"@sofereditor/core\";\nimport { hasOpenModifier } from \"./platform\";\n\n/**\n * Convert a Y.Text delta into ReactNodes.\n *\n * Wrap order (innermost → outermost): bold → italic → underline → strike → font/color span → link.\n * Semantic tags carry bold/italic/underline/strike; color and fonts go on a styled <span>.\n *\n * Keys are stable on `${blockIndex}-${runningOffset}` so React reconciliation\n * survives split/merge of runs under collaboration.\n *\n * `baseOffset` shifts every emitted `data-embed-offset` (and key) by a fixed\n * amount. Needed when `delta` is only a SLICE of a larger model range — e.g.\n * one line of a célula-lista, where each `<li>` gets its own line-local delta\n * but `data-embed-offset` must stay in CELL coordinates (contract with\n * `Editor.tsx`'s embed-selection and `ImageResizeOverlay`'s DOM query).\n * Defaults to 0, which reproduces the old line-local numbering exactly.\n */\nexport function renderInline(\n delta: DeltaOp[],\n keyPrefix: number | string,\n baseOffset = 0,\n): ReactNode {\n if (\n delta.length === 0 ||\n (delta.length === 1 &&\n typeof delta[0].insert === \"string\" &&\n delta[0].insert === \"\")\n ) {\n return <br data-empty=\"true\" />;\n }\n let offset = baseOffset;\n // Float-wrap images (wrap-left / wrap-right) are emitted as the FIRST DOM\n // children of the block so CSS float can flow the whole paragraph around\n // them — float only wraps siblings that come after it in DOM order. A\n // zero-width phantom span is left at the embed's original offset so the\n // caret-position math in dom-bridge keeps counting the embed as 1 char in\n // its true model position.\n const wrapAnchors: ReactNode[] = [];\n const body: ReactNode[] = [];\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n const text = op.insert;\n if (text.length === 0) continue;\n const key = `${keyPrefix}-${offset}`;\n body.push(<Fragment key={key}>{wrap(text, op.attributes)}</Fragment>);\n offset += text.length;\n } else if (isImageEmbed(op.insert)) {\n const key = `${keyPrefix}-${offset}`;\n const layout = op.insert.layout ?? \"inline\";\n // Marks aplicadas ao 1-char range que cobre o embed: hoje só\n // `comment` é renderizada visualmente (espelha o tratamento de\n // texto em `wrap()`). Outras marks (bold/italic/etc.) não fazem\n // sentido em embed visual e são ignoradas.\n const commentMarkId = op.attributes?.comment?.markId;\n if (layout === \"wrap-left\" || layout === \"wrap-right\") {\n wrapAnchors.push(\n <ImageEmbedView key={`${key}-anchor`} embed={op.insert} offset={offset} wrapAnchor />,\n );\n body.push(\n <span\n key={`${key}-phantom`}\n data-embed-phantom=\"true\"\n data-embed-offset={offset}\n contentEditable={false}\n style={PHANTOM_STYLE}\n />,\n );\n } else {\n const imgNode = <ImageEmbedView key={key} embed={op.insert} offset={offset} />;\n if (commentMarkId) {\n body.push(\n <span key={`${key}-cmt`} data-comment-id={commentMarkId} className=\"ed-comment\">\n {imgNode}\n </span>,\n );\n } else {\n body.push(imgNode);\n }\n }\n offset += 1;\n } else if (op.insert != null) {\n // Unknown embed kind — reserve the offset slot so caret math stays correct.\n offset += 1;\n }\n }\n if (wrapAnchors.length === 0 && body.length === 0) return <br data-empty=\"true\" />;\n return [...wrapAnchors, ...body];\n}\n\nconst PHANTOM_STYLE: CSSProperties = {\n display: \"inline-block\",\n width: 0,\n height: 0,\n overflow: \"hidden\",\n verticalAlign: \"baseline\",\n};\n\n// Inline style for links (the package ships no CSS). `color`/underline give the\n// link its visual affordance; `cursor: \"text\"` matches the surrounding editable\n// text — the browser's UA stylesheet makes `<a href>` use `cursor: pointer`,\n// which flickers pointer↔text across the link boundary inside a contentEditable\n// (hard to click / \"cursor oscila\"). To FOLLOW a link, Cmd/Ctrl+click.\nconst LINK_STYLE: CSSProperties = {\n color: \"#2563eb\",\n textDecoration: \"underline\",\n cursor: \"text\",\n};\n\ninterface ImageEmbedViewProps {\n embed: ImageEmbed;\n offset: number;\n wrapAnchor?: boolean;\n}\n\n/**\n * `embed.src` is a (potentially huge) base64 data URL. Y.Text.toDelta() builds\n * a fresh `ImageEmbed` object every time the parent snapshot recomputes — so\n * by default React would re-render `<img src={...}>` on every keystroke even\n * when nothing about the image changed. Memoizing by *value* on the cheap\n * fields and skipping the src deep-compare (it's stable while the embed is\n * stable; if src actually changes, width/height typically change too) keeps\n * the `<img>` DOM node fully stable across keystrokes.\n */\nfunction imageEmbedPropsEqual(a: ImageEmbedViewProps, b: ImageEmbedViewProps): boolean {\n if (a.offset !== b.offset || a.wrapAnchor !== b.wrapAnchor) return false;\n const x = a.embed;\n const y = b.embed;\n if (x === y) return true;\n return (\n x.width === y.width &&\n x.height === y.height &&\n x.layout === y.layout &&\n x.align === y.align &&\n x.offsetX === y.offsetX &&\n x.offsetY === y.offsetY &&\n x.caption === y.caption &&\n x.captionAlign === y.captionAlign &&\n // src is the expensive compare — only fall back to ref equality. Fresh\n // toDelta() objects share the same string reference for embed payloads\n // because Y.js stores the embed in its content map and re-exposes it.\n x.src === y.src\n );\n}\n\nconst ImageEmbedView = memo(ImageEmbedViewImpl, imageEmbedPropsEqual);\n\nfunction ImageEmbedViewImpl({\n embed,\n offset,\n wrapAnchor,\n}: ImageEmbedViewProps): ReactNode {\n const layout = embed.layout ?? \"inline\";\n const align = embed.align;\n const ox = embed.offsetX ?? 0;\n const oy = embed.offsetY ?? 0;\n const hasCaption = !!embed.caption && embed.caption.length > 0;\n\n // The outer style positions the wrapper (float / absolute / inline-block /\n // block-with-align). The img itself stays at intrinsic width × height so the\n // existing caret-position math (`data-embed-offset`) and ImageResizeOverlay\n // queries (`img[data-embed=\"image\"]`) keep working. A `<figcaption>` lives\n // inside the wrapper when a caption is set.\n let outerStyle: CSSProperties;\n switch (layout) {\n case \"wrap-left\":\n outerStyle = {\n float: \"left\",\n width: embed.width,\n marginRight: Math.max(8, ox),\n marginTop: Math.max(0, oy),\n marginBottom: 4,\n };\n break;\n case \"wrap-right\":\n outerStyle = {\n float: \"right\",\n width: embed.width,\n marginLeft: Math.max(8, ox),\n marginTop: Math.max(0, oy),\n marginBottom: 4,\n };\n break;\n case \"behind\":\n case \"front\":\n outerStyle = {\n position: \"absolute\",\n left: ox,\n top: oy,\n width: embed.width,\n zIndex: layout === \"behind\" ? -1 : 2,\n };\n break;\n case \"inline\":\n default:\n outerStyle = align\n ? {\n display: \"block\",\n width: embed.width,\n marginLeft: align === \"left\" ? 0 : \"auto\",\n marginRight: align === \"right\" ? 0 : \"auto\",\n }\n : {\n display: \"inline-block\",\n // Fórmula inline traz o deslocamento de base que o MathJax mediu;\n // sem ele a fórmula flutua no topo da linha em vez de sentar nela.\n verticalAlign: embed.formula?.vAlign ?? \"text-bottom\",\n width: embed.width,\n };\n break;\n }\n\n // The img always carries the data-* attributes used by Editor/Overlay.\n //\n // Structure-stable rendering (bug #11): the <img> ALWAYS lives inside a\n // <figure> that carries `outerStyle`, so adding/removing a caption never\n // changes the <img>'s parent type. React then keeps the same <img> DOM node\n // instead of unmounting+remounting it — a remount would force a synchronous\n // base64 re-decode (`decoding=\"sync\"` below), freezing the UI on large\n // images. The <img> therefore always uses the intrinsic-size style; the\n // <figure> owns the positioning.\n const imgStyle: CSSProperties = {\n width: embed.width,\n height: embed.height,\n display: \"block\",\n };\n\n // `decoding=\"sync\"` + `loading=\"eager\"` evita o piscar quando a paginação\n // remonta o <img> (consumidor com `renderPageHeader` faz a 1ª página ter\n // marginTop diferente, o que muda o DOM pai do bloco quando ele cruza\n // páginas): com cache do browser quente, o frame de remount já vem pintado.\n const img = (\n <img\n src={embed.src}\n width={embed.width}\n height={embed.height}\n contentEditable={false}\n draggable={false}\n decoding=\"sync\"\n loading=\"eager\"\n data-embed=\"image\"\n data-embed-offset={offset}\n data-embed-align={align ?? \"\"}\n data-embed-layout={layout}\n data-wrap-anchor={wrapAnchor ? \"true\" : undefined}\n style={imgStyle}\n alt=\"\"\n />\n );\n\n // Always render the <figure> wrapper (structure-stable; see imgStyle note).\n // The <figcaption> is the ONLY part that toggles with the caption — the\n // <img> stays mounted, so no re-decode/freeze.\n const captionAlign = embed.captionAlign ?? \"center\";\n return (\n <figure\n className=\"ed-figure\"\n contentEditable={false}\n data-embed-figure={hasCaption ? \"true\" : undefined}\n data-embed-layout={layout}\n style={outerStyle}\n >\n {img}\n {hasCaption && (\n <figcaption\n className=\"ed-figcaption\"\n data-caption-align={captionAlign}\n style={{ textAlign: captionAlign }}\n >\n {embed.caption}\n </figcaption>\n )}\n </figure>\n );\n}\n\n/**\n * Corridas de 3+ underlines viram um traço contínuo (lacuna de prova).\n *\n * Os caracteres PERMANECEM no DOM como nós de texto: `dom-bridge` mapeia\n * posição de DOM → offset do modelo somando `textContent.length`, então dividir\n * a run em sub-spans é seguro, mas introduzir ou remover caracteres não seria.\n */\nfunction decorateBlanks(text: string): ReactNode {\n const segs = splitUnderscoreRuns(text);\n // Caminho comum (texto sem lacuna): devolve a string crua, sem wrapper.\n if (segs.length === 1 && !segs[0].blank) return text;\n return segs.map((s, i) =>\n s.blank ? (\n <span key={i} data-blank=\"true\" style={BLANK_STYLE as CSSProperties}>\n {s.text}\n </span>\n ) : (\n <Fragment key={i}>{s.text}</Fragment>\n ),\n );\n}\n\nfunction wrap(text: string, attrs?: MarkAttrs): ReactNode {\n // A lacuna fica DENTRO dos wrappers de mark: é decoração do texto, não uma mark.\n let node: ReactNode = decorateBlanks(text);\n if (!attrs) return node;\n\n if (attrs.bold) node = <strong>{node}</strong>;\n if (attrs.italic) node = <em>{node}</em>;\n if (attrs.underline) node = <u>{node}</u>;\n if (attrs.strike) node = <s>{node}</s>;\n\n const styleParts: CSSProperties = {};\n if (attrs.color) styleParts.color = attrs.color;\n if (attrs.highlight) styleParts.backgroundColor = attrs.highlight;\n if (attrs.fontFamily) styleParts.fontFamily = attrs.fontFamily;\n if (attrs.fontSize) styleParts.fontSize = attrs.fontSize;\n if (Object.keys(styleParts).length > 0) {\n node = <span style={styleParts}>{node}</span>;\n }\n\n if (attrs.link?.href) {\n node = (\n <a\n href={attrs.link.href}\n title={attrs.link.title}\n rel=\"noopener noreferrer\"\n target=\"_blank\"\n style={LINK_STYLE}\n onClick={(e) => {\n // Plain click edits the link text (caret), never navigates. The\n // platform \"open\" modifier (⌘ on macOS, Ctrl elsewhere) opens the\n // link directly in a new tab. We `preventDefault` in BOTH cases and\n // open via `window.open` ourselves: inside a contentEditable the\n // browser doesn't reliably navigate an `<a>` on modifier-click, so\n // relying on the default action did nothing.\n e.preventDefault();\n if (hasOpenModifier(e) && attrs.link?.href) {\n window.open(attrs.link.href, \"_blank\", \"noopener,noreferrer\");\n }\n }}\n >\n {node}\n </a>\n );\n }\n\n if (attrs.comment?.markId) {\n node = (\n <span data-comment-id={attrs.comment.markId} className=\"ed-comment\">\n {node}\n </span>\n );\n }\n\n return node;\n}\n","import type { DeltaOp } from \"@sofereditor/core\";\n\n/** Total number of inserted characters. Embeds (non-string inserts) count as 1. */\nexport function deltaLength(delta: DeltaOp[]): number {\n let sum = 0;\n for (const op of delta) {\n if (typeof op.insert === \"string\") sum += op.insert.length;\n else if (op.insert != null) sum += 1;\n }\n return sum;\n}\n\n/**\n * Return the subset of `delta` covering `[start, end)` characters. Attributes\n * are preserved on each intersecting op. Embeds are atomic length-1 ops:\n * included whole if their slot intersects the range.\n */\nexport function sliceDelta(delta: DeltaOp[], start: number, end: number): DeltaOp[] {\n if (end <= start) return [];\n const out: DeltaOp[] = [];\n let cursor = 0;\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n const opStart = cursor;\n const opEnd = cursor + op.insert.length;\n cursor = opEnd;\n if (opEnd <= start) continue;\n if (opStart >= end) break;\n const sliceFrom = Math.max(opStart, start) - opStart;\n const sliceTo = Math.min(opEnd, end) - opStart;\n out.push({\n insert: op.insert.slice(sliceFrom, sliceTo),\n attributes: op.attributes,\n });\n } else if (op.insert != null) {\n const opStart = cursor;\n const opEnd = cursor + 1;\n cursor = opEnd;\n if (opEnd <= start) continue;\n if (opStart >= end) break;\n out.push({ insert: op.insert, attributes: op.attributes });\n }\n }\n return out;\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type JSX,\n type RefObject,\n} from \"react\";\nimport { MIN_LINHA_PX, normalizarLarguras } from \"@sofereditor/core\";\nimport { useEditorContext } from \"./EditorContext\";\n\ninterface Props {\n /** Ref to the underlying `<table>` element. */\n tableRef: RefObject<HTMLTableElement | null>;\n blockIndex: number;\n cols: number;\n rows: number;\n /**\n * Tabela RTL (`dir=\"rtl\"`): a coluna lógica 0 fica à DIREITA. A medição\n * continua acumulando em ordem lógica; só a posição da alça e o sinal do\n * arrasto são espelhados.\n */\n rtl?: boolean;\n}\n\n/** Converte a divisa lógica (acumulada desde a coluna 0) em `left` visual. */\nexport function posicaoVisualDaDivisa(xLogico: number, larguraPx: number, rtl: boolean): number {\n return rtl ? larguraPx - xLogico : xLogico;\n}\n\n/**\n * Em RTL, arrastar a divisa para a ESQUERDA alarga a coluna lógica anterior\n * (que está à direita dela) — o oposto de LTR.\n */\nexport function sinalDoArrasto(rtl: boolean): 1 | -1 {\n return rtl ? -1 : 1;\n}\n\n/**\n * O arrasto chega em px de tela; o modelo é proporção. A conversão é contra\n * a largura RENDERIZADA da tabela, que é justamente o que torna o arrasto\n * exato: mover o dedo 60 px numa tabela de 600 px é mover a divisa 10 pontos,\n * e 10 pontos renderizam 60 px. Ida e volta fecham.\n */\nexport function deltaPctDoArrasto(dxPx: number, larguraTabelaPx: number): number {\n if (!(larguraTabelaPx > 0)) return 0;\n return (dxPx / larguraTabelaPx) * 100;\n}\n\n/**\n * Divide `deltaPx` em partes IGUAIS (não proporcionais) entre as linhas de\n * `base` — é o que faz a alça da base da tabela preservar uma linha que o\n * professor deixou alta de propósito, em vez de ela crescer/encolher mais\n * que as outras. Cada resultado passa pelo mesmo piso de `setRowHeight`\n * (`MIN_LINHA_PX`), então encolher demais nunca estoura para negativo — a\n * conta simplesmente para no piso.\n *\n * Pura e sem DOM: é o coração da alça de \"altura total\" e do braço vertical\n * da alça de canto, testável sem montar o editor.\n */\nexport function distribuirAltura(base: number[], deltaPx: number): number[] {\n if (base.length === 0) return base;\n const fatia = deltaPx / base.length;\n return base.map((h) => Math.max(MIN_LINHA_PX, Math.round(h + fatia)));\n}\n\n/**\n * Estado de arrasto — um só ref, discriminado por `tipo`, para as cinco\n * famílias de alça (divisa de coluna, largura total, divisa de linha, altura\n * total, canto). Em TODOS os ramos o delta do `onPointerMove` é calculado\n * contra o que foi lido aqui no `pointerdown`, nunca contra o modelo/DOM\n * corrente — é a regra que mata o emborrachado (ver `deltaPctDoArrasto` e o\n * brief da Task 3 para a medição do defeito original na coluna).\n */\ntype Arrasto =\n | { tipo: \"coluna\"; boundary: number; startX: number; baseWidths: number[]; tableWidthPx: number }\n | { tipo: \"larguraTotal\"; startX: number; baseWidthPct: number; utilPx: number }\n | { tipo: \"linha\"; row: number; startY: number; baseHeightPx: number }\n | { tipo: \"alturaTotal\"; startY: number; baseHeights: number[] }\n | {\n tipo: \"canto\";\n startX: number;\n startY: number;\n baseWidthPct: number;\n utilPx: number;\n baseHeights: number[];\n };\n\n/**\n * Renders absolute-positioned drag handles for every resizable boundary of a\n * table: column divisas, the table's own right edge (width), row divisas,\n * the table's own bottom edge (height, distributed evenly across rows via\n * `distribuirAltura`), and a corner handle that drives both axes at once.\n *\n * O delta é sempre calculado contra o estado do MODELO (ou, para linhas, do\n * DOM RENDERIZADO — ver nota abaixo) lido uma única vez no `pointerdown`,\n * nunca contra o corrente: acumular contra o DOM/modelo corrente é o que\n * produzia o salto elástico que a Task 3 matou para colunas, e o mesmo vale\n * aqui para as outras quatro famílias.\n *\n * Altura de linha é MÍNIMO, não largura — por isso a base de uma alça de\n * linha/altura-total/canto é lida do LAYOUT MEDIDO (`layout.bottoms`), não\n * do atributo do modelo: se o modelo não tem `rowHeights` (ou tem um mínimo\n * menor que o conteúdo), o `<tr>` renderiza mais alto que o mínimo gravado, e\n * ancorar no mínimo faria o arrasto de CRESCER ter uma \"zona morta\" antes de\n * a borda começar a se mexer. Ancorar no renderizado faz crescer colar no\n * dedo desde o primeiro pixel — e faz ENCOLHER abaixo do conteúdo gravar o\n * valor sem mover o desenho, que é exatamente o comportamento esperado (ver\n * `onPointerUp`/`measure` abaixo).\n *\n * No `pointerup` a alça reancora via `measure()` — aqui isso importa MAIS do\n * que na coluna: como altura é mínimo, arrastar a base para cima do que o\n * conteúdo permite grava o valor mas NÃO move o desenho, e a alça (se não\n * reancorada) ficaria flutuando onde o dedo soltou, longe da borda real.\n * `measure()` NÃO é chamado durante o `pointermove` — chamá-lo ali voltaria\n * a ler o renderizado a cada quadro e reintroduziria o defeito por outro\n * caminho, agora nas linhas.\n *\n * Lives as a sibling of the `<table>` inside a `position: relative` wrapper.\n * Handles are `contentEditable={false}` so they don't interfere with caret\n * placement inside cells.\n */\nexport function TableResizeOverlay({ tableRef, blockIndex, cols, rows, rtl = false }: Props): JSX.Element | null {\n const editor = useEditorContext();\n // Handle positions (column rights, row bottoms, in px relative to the\n // table's own top-left) and the table's own rendered box. Recomputed after\n // every layout via `measure()`.\n const [layout, setLayout] = useState<{\n rights: number[];\n bottoms: number[];\n width: number;\n height: number;\n } | null>(null);\n const arrastoRef = useRef<Arrasto | null>(null);\n\n type Layout = { rights: number[]; bottoms: number[]; width: number; height: number };\n\n /**\n * Mede column right-edges e row bottom-edges relativos ao canto\n * superior-esquerdo da própria tabela — SEMPRE contra o DOM corrente, sem\n * depender do estado `layout` (que só é atualizado quando `measure()`\n * decide chamar `setLayout`, ou seja, pode estar um `ResizeObserver` atrás\n * do real). Usada tanto por `measure()` (pra atualizar o estado usado no\n * posicionamento visual das alças) quanto pelos `pointerdown` de\n * linha/altura-total/canto (pra garantir que `baseHeightPx`/`baseHeights`\n * nunca partam de uma medição velha).\n */\n const measureNow = useCallback((): Layout | null => {\n const table = tableRef.current;\n if (!table) return null;\n const tableRect = table.getBoundingClientRect();\n // Prefer measuring via `<col>` elements when present.\n const cgCols = table.querySelectorAll<HTMLTableColElement>(\"colgroup > col\");\n let rights: number[] = [];\n if (cgCols.length === cols) {\n let acc = 0;\n cgCols.forEach((c) => {\n acc += c.getBoundingClientRect().width;\n rights.push(acc);\n });\n } else {\n // Fallback: use the first row's <td> bounding rects.\n const firstRowCells = table.querySelectorAll<HTMLTableCellElement>(\"tbody > tr:first-child > td\");\n let acc = 0;\n firstRowCells.forEach((td) => {\n // Account for colSpan: split the width evenly across the spanned cols.\n const cs = Math.max(1, td.colSpan || 1);\n const w = td.getBoundingClientRect().width / cs;\n for (let i = 0; i < cs; i++) {\n acc += w;\n rights.push(acc);\n }\n });\n }\n if (rights.length !== cols) {\n // Column shape mismatch — bail out rather than place handles in wrong spots.\n return null;\n }\n // Row bottoms — same accumulating approach, via each `<tr>`'s rendered\n // height. Rows stack directly under the table's top edge (no <thead>\n // here), so the accumulation starts at 0 exactly like the column rights\n // above.\n const trs = table.querySelectorAll<HTMLTableRowElement>(\"tbody > tr\");\n const bottoms: number[] = [];\n let accY = 0;\n trs.forEach((tr) => {\n accY += tr.getBoundingClientRect().height;\n bottoms.push(accY);\n });\n // Row/height handles only make sense when this DOM covers the WHOLE\n // table. A table split across pages renders only the first fragment's\n // rows here (`isFirstFragment` in NodeView) — `trs.length < rows` in\n // that case. Rather than null out the entire overlay (which would also\n // kill the still-valid column handles), just suppress the row-based\n // handles: `bottoms` stays empty and callers skip them.\n return {\n rights,\n bottoms: trs.length === rows ? bottoms : [],\n width: tableRect.width,\n height: tableRect.height,\n };\n }, [cols, rows, tableRef]);\n\n const measure = useCallback(() => {\n setLayout(measureNow());\n }, [measureNow]);\n\n useLayoutEffect(() => {\n measure();\n }, [measure]);\n\n useEffect(() => {\n const table = tableRef.current;\n if (!table) return;\n const ro = new ResizeObserver(() => measure());\n ro.observe(table);\n return () => ro.disconnect();\n }, [measure, tableRef]);\n\n const onPointerUp = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n arrastoRef.current = null;\n // Remede: reancora a alça na borda REAL (renderizada). Necessário\n // sempre que o arrasto pode ter batido num piso antes do dedo — coluna\n // no MIN_COLUNA_PCT, linha no MIN_LINHA_PX ou no próprio conteúdo (que\n // não tem \"mínimo\" nenhum pra bater, mas também não obedece um mínimo\n // MENOR que ele mesmo). Chamado só aqui — nunca durante o pointermove\n // (ver docstring do componente).\n measure();\n try {\n (e.currentTarget as Element).releasePointerCapture(e.pointerId);\n } catch {\n /* ignore */\n }\n },\n [measure],\n );\n\n const onPointerMove = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n const d = arrastoRef.current;\n if (!d) return;\n switch (d.tipo) {\n case \"coluna\": {\n const dx = (e.clientX - d.startX) * sinalDoArrasto(rtl);\n const dPct = deltaPctDoArrasto(dx, d.tableWidthPx);\n editor.setColumnBoundary(blockIndex, d.boundary, dPct, d.baseWidths);\n break;\n }\n case \"larguraTotal\": {\n const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.utilPx);\n editor.setTableWidth(blockIndex, d.baseWidthPct + dPct);\n break;\n }\n case \"linha\": {\n editor.setRowHeight(blockIndex, d.row, d.baseHeightPx + (e.clientY - d.startY));\n break;\n }\n case \"alturaTotal\": {\n editor.setRowHeights(blockIndex, distribuirAltura(d.baseHeights, e.clientY - d.startY));\n break;\n }\n case \"canto\": {\n const dPct = deltaPctDoArrasto(e.clientX - d.startX, d.utilPx);\n editor.setTableWidth(blockIndex, d.baseWidthPct + dPct);\n editor.setRowHeights(blockIndex, distribuirAltura(d.baseHeights, e.clientY - d.startY));\n break;\n }\n }\n },\n [blockIndex, editor, rtl],\n );\n\n // `.ed-table-wrap` is always the table's parent (see TableView) and is the\n // containing block the inline `width:X%` style resolves against.\n const containerWidthPxOf = useCallback((table: HTMLTableElement, tableWidthPx: number): number => {\n return table.parentElement?.getBoundingClientRect().width || tableWidthPx;\n }, []);\n\n const onColumnPointerDown = useCallback(\n (boundary: number) => (e: React.PointerEvent<HTMLDivElement>) => {\n // Prevent the editor from losing focus / caret being placed on the handle.\n e.preventDefault();\n e.stopPropagation();\n const table = tableRef.current;\n if (!table) return;\n const tableWidthPx = table.getBoundingClientRect().width || 1;\n const attrs = editor.doc.getBlockAttrs(blockIndex);\n const baseWidths = normalizarLarguras(attrs.colWidths as number[] | undefined, cols);\n arrastoRef.current = { tipo: \"coluna\", boundary, startX: e.clientX, baseWidths, tableWidthPx };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [tableRef, editor, blockIndex, cols],\n );\n\n const onTableWidthPointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n const table = tableRef.current;\n if (!table) return;\n const tableWidthPx = table.getBoundingClientRect().width || 1;\n // `<table style=\"width:X%\">` is a percentage of `.ed-table-wrap`\n // (which has no width of its own and simply fills the page column),\n // NOT of the table's own current width. Whenever `baseWidthPct !== 100`\n // those two differ, and using `tableWidthPx` there overshoots by a\n // factor of `100 / baseWidthPct` — invisible on a fresh 100%-wide\n // table, but a second drag of the right edge after a resize reproduces\n // the exact rubber-banding Task 3 killed, just on the outer edge.\n const utilPx = containerWidthPxOf(table, tableWidthPx);\n const attrs = editor.doc.getBlockAttrs(blockIndex);\n const baseWidthPct = typeof attrs.tableWidth === \"number\" ? attrs.tableWidth : 100;\n arrastoRef.current = { tipo: \"larguraTotal\", startX: e.clientX, baseWidthPct, utilPx };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [tableRef, editor, blockIndex, containerWidthPxOf],\n );\n\n // Converte um array acumulado de bottoms em alturas por-linha.\n const heightsFromBottoms = (bottoms: number[]): number[] =>\n bottoms.map((b, i) => b - (i === 0 ? 0 : bottoms[i - 1]!));\n\n const onRowPointerDown = useCallback(\n (row: number) => (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n // Medição FRESCA, não o estado `layout` — que só é atualizado quando o\n // `ResizeObserver` dispara (assíncrono) e pode estar um frame atrás de\n // uma edição de conteúdo que acabou de mudar a altura desta linha.\n const m = measureNow();\n if (!m || m.bottoms.length === 0) return;\n const baseHeightPx = m.bottoms[row]! - (row === 0 ? 0 : m.bottoms[row - 1]!);\n arrastoRef.current = { tipo: \"linha\", row, startY: e.clientY, baseHeightPx };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [measureNow],\n );\n\n const onTableHeightPointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n const m = measureNow();\n if (!m || m.bottoms.length === 0) return;\n arrastoRef.current = { tipo: \"alturaTotal\", startY: e.clientY, baseHeights: heightsFromBottoms(m.bottoms) };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [measureNow],\n );\n\n const onCornerPointerDown = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n e.stopPropagation();\n const table = tableRef.current;\n const m = measureNow();\n if (!table || !m) return;\n const tableWidthPx = table.getBoundingClientRect().width || 1;\n const utilPx = containerWidthPxOf(table, tableWidthPx);\n const attrs = editor.doc.getBlockAttrs(blockIndex);\n const baseWidthPct = typeof attrs.tableWidth === \"number\" ? attrs.tableWidth : 100;\n arrastoRef.current = {\n tipo: \"canto\",\n startX: e.clientX,\n startY: e.clientY,\n baseWidthPct,\n utilPx,\n baseHeights: heightsFromBottoms(m.bottoms),\n };\n (e.currentTarget as Element).setPointerCapture(e.pointerId);\n },\n [tableRef, editor, blockIndex, measureNow, containerWidthPxOf],\n );\n\n if (!layout) return null;\n\n // `x` já é a posição VISUAL. Só as divisas entre colunas são espelhadas em\n // RTL; a alça de largura total fica sempre na borda direita da tabela.\n const colHandleStyle = (x: number): CSSProperties => ({\n position: \"absolute\",\n top: 0,\n left: `${x - 3}px`,\n width: \"6px\",\n height: `${layout.height}px`,\n cursor: \"col-resize\",\n zIndex: 5,\n });\n const rowHandleStyle = (y: number): CSSProperties => ({\n position: \"absolute\",\n left: 0,\n top: `${y - 3}px`,\n width: `${layout.width}px`,\n height: \"6px\",\n cursor: \"row-resize\",\n zIndex: 5,\n });\n const cornerHandleStyle: CSSProperties = {\n position: \"absolute\",\n left: `${layout.width - 5}px`,\n top: `${layout.height - 5}px`,\n width: \"10px\",\n height: \"10px\",\n cursor: \"nwse-resize\",\n zIndex: 6,\n };\n\n return (\n <>\n {/* Column divisas + the table's own right edge (width). */}\n {layout.rights.map((x, c) =>\n c < cols - 1 ? (\n <div\n key={`col-${c}`}\n contentEditable={false}\n className=\"ed-col-resize-handle\"\n style={colHandleStyle(posicaoVisualDaDivisa(x, layout.width, rtl))}\n onPointerDown={onColumnPointerDown(c)}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label={`Redimensionar coluna ${c + 1}`}\n role=\"separator\"\n />\n ) : (\n <div\n key=\"col-total\"\n contentEditable={false}\n className=\"ed-col-resize-handle\"\n style={colHandleStyle(x)}\n onPointerDown={onTableWidthPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label=\"Largura da tabela\"\n role=\"separator\"\n />\n ),\n )}\n {/* Row divisas + the table's own bottom edge (height). */}\n {layout.bottoms.map((y, r) =>\n r < rows - 1 ? (\n <div\n key={`row-${r}`}\n contentEditable={false}\n className=\"ed-row-resize-handle\"\n style={rowHandleStyle(y)}\n onPointerDown={onRowPointerDown(r)}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label={`Redimensionar linha ${r + 1}`}\n role=\"separator\"\n />\n ) : (\n <div\n key=\"row-total\"\n contentEditable={false}\n className=\"ed-row-resize-handle\"\n style={rowHandleStyle(y)}\n onPointerDown={onTableHeightPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label=\"Altura da tabela\"\n role=\"separator\"\n />\n ),\n )}\n {/* Corner: drives width + height at once. */}\n <div\n contentEditable={false}\n className=\"ed-table-corner-handle\"\n style={cornerHandleStyle}\n onPointerDown={onCornerPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n aria-label=\"Redimensionar tabela\"\n role=\"separator\"\n />\n </>\n );\n}\n","import { useLayoutEffect, useRef, useState, type RefObject } from \"react\";\n\n/**\n * Sub-phase 3.2 — page layout with intra-paragraph fragmentation.\n *\n * Two-phase render cycle:\n * 1. \"measure\" phase: editor renders with the default (unfragmented) layout\n * — every top-level on page 1. After the DOM commits, `usePageLayout`\n * reads each top-level's geometry, runs `computeLayout`, and sets the\n * real layout (with page splits and fragment offsets).\n * 2. \"stable\" phase: editor renders the real layout. No further measurement\n * until `deps` change (typically `[topLevels]`), at which point we go\n * back to step 1.\n *\n * Fragmentation only applies to \"fragmentable\" top-levels (paragraph, heading,\n * blockquote — single Y.Text blocks). List groups and code blocks are placed\n * atomically: if they don't fit they jump to the next page and bleed if larger\n * than a page.\n */\n\nexport interface PageGeometry {\n /** Page width in CSS px (A4 @ 96dpi = ~794). */\n width: number;\n /** Page height in CSS px (A4 @ 96dpi = ~1123). */\n height: number;\n marginTop: number;\n marginBottom: number;\n marginLeft: number;\n marginRight: number;\n /**\n * Espaço adicional somado ao marginTop só na 1ª página. Útil para clientes\n * que rendem um header fixo (timbrado, identificação) na pág 1 via\n * `renderPageHeader` e não querem que esse espaço seja reservado nas demais.\n * Quando omitido, todas as páginas têm a mesma `contentHeight`.\n */\n firstPageExtraTop?: number;\n}\n\nexport const A4_PAGE: PageGeometry = {\n width: 794,\n height: 1123,\n marginTop: 96,\n marginBottom: 96,\n marginLeft: 96,\n marginRight: 96,\n};\n\nexport function contentHeight(g: PageGeometry, pageIndex: number = 0): number {\n const extra = pageIndex === 0 ? g.firstPageExtraTop ?? 0 : 0;\n return g.height - g.marginTop - g.marginBottom - extra;\n}\n\nexport function contentWidth(g: PageGeometry): number {\n return g.width - g.marginLeft - g.marginRight;\n}\n\nexport interface BlockFragment {\n index: number;\n start: number;\n end: number;\n}\n\nexport interface TableRowFragment {\n /** 0, 1, 2... sequential fragment id within the table. */\n index: number;\n /** First row included (inclusive). */\n rowStart: number;\n /** First row NOT included (exclusive). */\n rowEnd: number;\n}\n\nexport interface ListItemRangeFragment {\n /** 0, 1, 2... sequential fragment id within the list group. */\n index: number;\n /** First top-level item (level 0) included (inclusive). */\n itemStart: number;\n /** First top-level item (level 0) NOT included (exclusive). */\n itemEnd: number;\n}\n\nexport interface PageSlot {\n topLevelIndex: number;\n /** Set when this slot represents a paragraph/heading/blockquote line-level fragment. */\n fragment?: BlockFragment;\n /** Set when this slot is a row-range fragment of a table (Sub-phase 4.6). */\n tableFragment?: TableRowFragment;\n /** Set when this slot is an item-range fragment of an ordered/bullet list. */\n listFragment?: ListItemRangeFragment;\n}\n\nexport interface PageEntry {\n slots: PageSlot[];\n}\n\nexport interface PageLayout {\n pages: PageEntry[];\n}\n\nexport function defaultPageLayout(topLevelCount: number): PageLayout {\n return {\n pages: [\n {\n slots: Array.from({ length: topLevelCount }, (_, i) => ({ topLevelIndex: i })),\n },\n ],\n };\n}\n\nexport type PaginationPhase = \"measure\" | \"stable\";\n\nexport interface PageLayoutResult {\n layout: PageLayout;\n /**\n * \"measure\" between a deps change and the post-commit measurement; \"stable\"\n * once the layout matches the rendered DOM. Consumers can hide the caret /\n * suppress visible flashes during \"measure\" (the DOM is briefly in the\n * unfragmented intermediate state and the native caret may jump to the start\n * of the line before useLayoutEffect re-applies the model selection).\n */\n phase: PaginationPhase;\n}\n\nexport function usePageLayout(\n rootRef: RefObject<HTMLElement>,\n geometry: PageGeometry,\n topLevelCount: number,\n deps: ReadonlyArray<unknown>,\n): PageLayoutResult {\n const [phase, setPhase] = useState<PaginationPhase>(\"measure\");\n const [layout, setLayout] = useState<PageLayout>(() => defaultPageLayout(topLevelCount));\n const lastDepsRef = useRef<unknown[] | null>(null);\n\n // Reset effect: when `deps` (consumer-provided), `topLevelCount`, or\n // `geometry` change, go back to the unfragmented default layout and\n // re-enter the measure phase.\n //\n // Why a useLayoutEffect (instead of `setState` in render body, the previous\n // approach): with concurrent rendering / Strict Mode, mutating\n // `lastDepsRef.current` during render is fragile — React may discard a\n // render pass while keeping the ref mutation, so a subsequent pass sees\n // \"deps unchanged\" and the reset never fires. The historical symptom was\n // a doc whose N blocks arrived entirely via remote `Y.applyUpdate`\n // (e.g. an Hocuspocus / y-websocket sync): the editor's snapshot updated\n // to N in `useEditor`, but `layout` stayed at 1 page × 1 slot — the reset\n // condition in the old render-body path silently missed firing.\n //\n // Doing the reset in a useLayoutEffect runs after the commit (so refs are\n // stable and visible), but BEFORE paint (so the user never sees a frame\n // with stale layout). The phase change schedules a synchronous re-render\n // and the measure effect below picks up immediately.\n //\n // Equality uses `shallowArrayEqual` for deps (consumer's responsibility to\n // pass stable references / structural keys) and `layoutsDeepEqual` for\n // the layout itself (fragment-aware — see the historical bug where shallow\n // equality let a fragmented layout matching `1 page × 1 slot` survive the\n // reset and lock subsequent measures via `!isUnfragmented(layout)`).\n useLayoutEffect(() => {\n const currentDeps = [...deps, topLevelCount, geometry];\n if (lastDepsRef.current !== null && shallowArrayEqual(lastDepsRef.current, currentDeps)) {\n return;\n }\n lastDepsRef.current = currentDeps;\n setLayout((prev) => {\n const next = defaultPageLayout(topLevelCount);\n return layoutsDeepEqual(prev, next) ? prev : next;\n });\n setPhase(\"measure\");\n // No dependency array: this effect runs after every commit and decides\n // internally whether deps changed. We can't put `[...deps, topLevelCount, geometry]`\n // here because `deps` is a variable-length array from the caller — React's\n // hook deps must be a fixed-shape list. Running every commit is cheap (one\n // shallow array compare).\n });\n\n // Measure effect: when in the measure phase with an unfragmented layout,\n // read the DOM and compute the real (possibly multi-page, possibly\n // fragmented) layout. Returns to the \"stable\" phase.\n useLayoutEffect(() => {\n if (phase !== \"measure\") return;\n const root = rootRef.current;\n if (!root) return;\n // Only safe to measure when the DOM is in the unfragmented state.\n if (!isUnfragmented(layout)) return;\n const next = computeLayout(root, geometry, topLevelCount);\n if (!layoutsDeepEqual(layout, next)) setLayout(next);\n setPhase(\"stable\");\n }, [phase, layout, geometry, topLevelCount, rootRef]);\n\n return { layout, phase };\n}\n\n// ---------- Layout computation ----------\n\nfunction isUnfragmented(layout: PageLayout): boolean {\n return (\n layout.pages.length === 1 &&\n layout.pages[0].slots.every(\n (s) =>\n s.fragment === undefined &&\n s.tableFragment === undefined &&\n s.listFragment === undefined,\n )\n );\n}\n\nfunction computeLayout(\n root: HTMLElement,\n geometry: PageGeometry,\n topLevelCount: number,\n): PageLayout {\n const tops = collectTopLevelElements(root);\n if (tops.length === 0) return defaultPageLayout(topLevelCount);\n\n // `firstPageExtraTop` faz a pág 0 ter content height menor; usar uma função\n // permite recomputar quando empurramos novas páginas.\n const limitFor = (pageIndex: number) => contentHeight(geometry, pageIndex);\n const pages: PageEntry[] = [{ slots: [] }];\n let currentPage = pages[0];\n let limit = limitFor(0);\n let used = 0;\n\n const cap = Math.min(tops.length, topLevelCount);\n for (let i = 0; i < cap; i++) {\n const el = tops[i];\n const h = elTotalHeight(el);\n\n if (used + h <= limit) {\n currentPage.slots.push({ topLevelIndex: i });\n used += h;\n continue;\n }\n\n if (isFragmentable(el)) {\n const result = placeFragmentedBlock(el, i, pages, used, limitFor);\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = result.lastUsed;\n continue;\n }\n\n if (isTableTopLevel(el)) {\n const result = placeFragmentedTable(el, i, pages, used, limitFor);\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = result.lastUsed;\n continue;\n }\n\n if (isListTopLevel(el)) {\n const result = placeFragmentedList(el, i, pages, used, limitFor);\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = result.lastUsed;\n continue;\n }\n\n // Atomic top-level that doesn't fit\n if (currentPage.slots.length === 0) {\n // empty current page; place anyway (bleed if huge)\n currentPage.slots.push({ topLevelIndex: i });\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n } else {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n currentPage.slots.push({ topLevelIndex: i });\n used = h;\n }\n }\n\n // Strip trailing empty page if any\n if (pages.length > 1 && pages[pages.length - 1].slots.length === 0) pages.pop();\n\n return { pages };\n}\n\nfunction placeFragmentedBlock(\n el: HTMLElement,\n topLevelIndex: number,\n pages: PageEntry[],\n startUsed: number,\n limitFor: (pageIndex: number) => number,\n): { lastUsed: number } {\n let limit = limitFor(pages.length - 1);\n const totalChars = getBlockTextLength(el);\n if (totalChars === 0) {\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n // Measure every original line. Splitting only at line boundaries is what\n // makes fragmentation work correctly: when fragment K starts at an original\n // line boundary AND is rendered at the same width, its line wrapping matches\n // the original exactly, so our measured height predicts the rendered height.\n const lines = measureLines(el);\n if (lines.length === 0) {\n // Couldn't measure (no text nodes?) — place atomically as last resort.\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n let lineIdx = 0;\n let currentPage = pages[pages.length - 1];\n let used = startUsed;\n let fragIndex = 0;\n\n while (lineIdx < lines.length) {\n const available = Math.max(0, limit - used);\n if (available <= 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n\n // Greedy: include as many consecutive lines as fit. bestK is the last fitting line index.\n const startTop = lines[lineIdx].top;\n let bestK = -1;\n for (let k = lineIdx; k < lines.length; k++) {\n const h = lines[k].bottom - startTop;\n if (h <= available) bestK = k;\n else break;\n }\n\n if (bestK < 0) {\n // Not even one line fits in the remaining space.\n if (currentPage.slots.length === 0) {\n // Page is empty; force-place a single oversized line (bleed).\n bestK = lineIdx;\n } else {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n }\n\n const fragStart = lines[lineIdx].start;\n const fragEnd = bestK + 1 < lines.length ? lines[bestK + 1].start : totalChars;\n const fragHeight = lines[bestK].bottom - startTop;\n\n currentPage.slots.push({\n topLevelIndex,\n fragment: { index: fragIndex, start: fragStart, end: fragEnd },\n });\n used += fragHeight;\n fragIndex++;\n lineIdx = bestK + 1;\n\n if (lineIdx < lines.length) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n }\n }\n\n return { lastUsed: used };\n}\n\ninterface LineMetric {\n /** Char offset (within the block) of the first char on this line. */\n start: number;\n /** Viewport-y top of the line. */\n top: number;\n /** Viewport-y bottom of the line. */\n bottom: number;\n}\n\n/**\n * Walk every text node inside `el`, measuring each character's rect to detect\n * line boundaries. Linear in total character count.\n *\n * Memoized by `(offsetWidth, textContent)`: a block whose width and text\n * haven't changed (the common case during typing — only ONE block changes per\n * keystroke) skips the measurement entirely. This is the perf core of Sub-phase\n * 3.3's incremental repagination story.\n *\n * NOTE: cached `top`/`bottom` are viewport-absolute, but `placeFragmentedBlock`\n * only ever uses RELATIVE differences (`lines[k].bottom - lines[j].top`), which\n * are invariant to scroll/layout offsets — so cached values stay correct.\n */\nconst lineMetricsCache = new Map<string, LineMetric[]>();\nconst LINE_METRICS_CACHE_LIMIT = 200;\n\nfunction measureLines(el: HTMLElement): LineMetric[] {\n const text = el.textContent ?? \"\";\n const cacheKey = `${el.offsetWidth}|${text}`;\n const cached = lineMetricsCache.get(cacheKey);\n if (cached) {\n // LRU touch\n lineMetricsCache.delete(cacheKey);\n lineMetricsCache.set(cacheKey, cached);\n return cached;\n }\n\n const fresh = measureLinesUncached(el);\n lineMetricsCache.set(cacheKey, fresh);\n if (lineMetricsCache.size > LINE_METRICS_CACHE_LIMIT) {\n const oldest = lineMetricsCache.keys().next().value;\n if (oldest !== undefined) lineMetricsCache.delete(oldest);\n }\n return fresh;\n}\n\nfunction measureLinesUncached(el: HTMLElement): LineMetric[] {\n const range = document.createRange();\n const lines: LineMetric[] = [];\n\n // Walk text nodes AND image embeds — embeds count as 1 char in offset\n // arithmetic and contribute their bounding rect as a line (or part of one).\n const walker = document.createTreeWalker(\n el,\n NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,\n blockBoundaryFilter(el),\n );\n let n: Node | null;\n let charOffset = 0;\n\n while ((n = walker.nextNode())) {\n if (n.nodeType === Node.TEXT_NODE) {\n const text = n.textContent ?? \"\";\n for (let i = 0; i < text.length; i++) {\n try {\n range.setStart(n, i);\n range.setEnd(n, i + 1);\n } catch {\n continue;\n }\n const rect = range.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) continue;\n const last = lines[lines.length - 1];\n if (!last || rect.top >= last.bottom) {\n lines.push({ start: charOffset + i, top: rect.top, bottom: rect.bottom });\n } else {\n last.top = Math.min(last.top, rect.top);\n last.bottom = Math.max(last.bottom, rect.bottom);\n }\n }\n charOffset += text.length;\n continue;\n }\n if (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embed === \"image\"\n ) {\n const rect = (n as HTMLElement).getBoundingClientRect();\n if (rect.height > 0) {\n const last = lines[lines.length - 1];\n if (!last || rect.top >= last.bottom) {\n lines.push({ start: charOffset, top: rect.top, bottom: rect.bottom });\n } else {\n last.top = Math.min(last.top, rect.top);\n last.bottom = Math.max(last.bottom, rect.bottom);\n }\n }\n charOffset += 1;\n }\n }\n return lines;\n}\n\n// ---------- DOM measurement primitives ----------\n\nfunction elTotalHeight(el: HTMLElement): number {\n const cs = getComputedStyle(el);\n const m = parseFloat(cs.marginTop || \"0\") + parseFloat(cs.marginBottom || \"0\");\n return el.offsetHeight + m;\n}\n\nfunction isFragmentable(el: HTMLElement): boolean {\n // Top-level fragmentable types: paragraph, heading, blockquote.\n // Tables fragment via `placeFragmentedTable` (row-level), not the line engine.\n const type = el.getAttribute(\"data-block-type\");\n if (type === \"paragraph\") return true;\n if (type === \"heading\") return true;\n if (type === \"blockquote\") return true;\n return false;\n}\n\n/**\n * The top-level for a table block is a `<div class=\"ed-table-wrap\">` wrapper —\n * the `data-block-type=\"table\"` attribute lives on the nested `<table>`.\n */\nfunction isTableTopLevel(el: HTMLElement): boolean {\n return el.querySelector(':scope > table[data-block-type=\"table\"]') !== null;\n}\n\ninterface PlaceFragmentedTableResult { lastUsed: number; }\n\n/**\n * Sub-phase 4.6 — split a table that doesn't fit on the current page across\n * pages at row boundaries.\n *\n * Constraints:\n * - A break between rows `r` and `r+1` is INVALID when any cell with\n * `rowSpan > 1` whose owner sits at row `r' <= r` extends into row `r+1`.\n * We compute this by walking each row's `<td>`s: a `rowSpan = N > 1` at row\n * `r` blocks the boundaries `(r, r+1), (r+1, r+2), … (r+N-2, r+N-1)`.\n *\n * - If no clean break fits the available height (e.g. a very tall span at the\n * top of the table), and the current page already has content, we move to\n * the next page and try again. If even an empty page can't fit a valid\n * prefix, we bleed the smallest legal prefix rather than loop forever.\n *\n * Heights come from `<tr>.getBoundingClientRect()` — these are measured against\n * the unfragmented render, but row layout is invariant under fragmentation as\n * long as the `<colgroup>` widths don't change (they don't), and as long as a\n * span isn't cut (which is exactly the constraint above).\n */\nfunction placeFragmentedTable(\n el: HTMLElement,\n topLevelIndex: number,\n pages: PageEntry[],\n startUsed: number,\n limitFor: (pageIndex: number) => number,\n): PlaceFragmentedTableResult {\n let limit = limitFor(pages.length - 1);\n const table = el.querySelector(':scope > table[data-block-type=\"table\"]') as HTMLTableElement | null;\n const tbody = table?.querySelector(':scope > tbody');\n const trs = tbody ? (Array.from(tbody.children) as HTMLTableRowElement[]) : [];\n if (trs.length === 0) {\n // Empty / no rows — fall back to atomic.\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n const rowHeights = trs.map((tr) => tr.getBoundingClientRect().height);\n\n // Invalid break boundaries — indices `k` for which the break between row `k`\n // and `k+1` is blocked by a rowspan crossing it.\n const invalid = new Set<number>();\n for (let r = 0; r < trs.length; r++) {\n const tds = Array.from(trs[r].children) as HTMLTableCellElement[];\n for (const td of tds) {\n const rs = td.rowSpan || 1;\n if (rs > 1) {\n for (let k = r; k <= r + rs - 2 && k < trs.length - 1; k++) {\n invalid.add(k);\n }\n }\n }\n }\n\n // Vertical padding/margin around the table proper, distributed conservatively\n // onto the FIRST fragment only — keeps subsequent fragments compact.\n const wrapHeight = elTotalHeight(el);\n const tableHeight = table ? table.getBoundingClientRect().height : 0;\n const chrome = Math.max(0, wrapHeight - tableHeight);\n\n let rowIdx = 0;\n let used = startUsed;\n let fragIndex = 0;\n let currentPage = pages[pages.length - 1];\n\n while (rowIdx < trs.length) {\n const overhead = fragIndex === 0 ? chrome : 0;\n const available = Math.max(0, limit - used - overhead);\n if (available <= 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n\n // Greedy: keep extending the run as long as height fits and we haven't\n // passed a valid break boundary. Track the LAST valid `bestEnd`.\n let bestEnd = -1;\n let accH = 0;\n for (let k = rowIdx; k < trs.length; k++) {\n accH += rowHeights[k];\n if (accH > available) break;\n if (k === trs.length - 1 || !invalid.has(k)) bestEnd = k + 1;\n }\n\n if (bestEnd < 0) {\n if (currentPage.slots.length === 0) {\n // Empty page yet still no valid prefix fits — find the smallest legal\n // prefix and bleed (better than an infinite loop).\n let k = rowIdx;\n while (k < trs.length - 1 && invalid.has(k)) k++;\n bestEnd = k + 1;\n } else {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n }\n\n let fragHeight = 0;\n for (let k = rowIdx; k < bestEnd; k++) fragHeight += rowHeights[k];\n if (fragIndex === 0) fragHeight += chrome;\n\n currentPage.slots.push({\n topLevelIndex,\n tableFragment: { index: fragIndex, rowStart: rowIdx, rowEnd: bestEnd },\n });\n used += fragHeight;\n fragIndex++;\n rowIdx = bestEnd;\n\n if (rowIdx < trs.length) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n }\n }\n\n return { lastUsed: used };\n}\n\n/**\n * A top-level for a list group is the `<ol>` or `<ul>` rendered by\n * `renderListTree`. Its direct `<li>` children are the level-0 entries of\n * the group; nested sub-lists live INSIDE those `<li>`s and travel with them.\n */\nfunction isListTopLevel(el: HTMLElement): boolean {\n if (el.tagName !== \"OL\" && el.tagName !== \"UL\") return false;\n return el.classList.contains(\"ed-list\");\n}\n\ninterface PlaceFragmentedListResult { lastUsed: number; }\n\n/**\n * Split a list group that doesn't fit on the current page across pages at\n * top-level `<li>` boundaries. Nested sub-lists stay attached to their parent\n * `<li>` (we never break inside a nested list — that's reserved for the rare\n * case where a single top-level item is itself taller than a full page, in\n * which case we bleed it).\n *\n * Heights come from `<li>.getBoundingClientRect()` measured against the\n * unfragmented render. They include the nested sub-list height when present.\n */\nfunction placeFragmentedList(\n el: HTMLElement,\n topLevelIndex: number,\n pages: PageEntry[],\n startUsed: number,\n limitFor: (pageIndex: number) => number,\n): PlaceFragmentedListResult {\n let limit = limitFor(pages.length - 1);\n\n // Direct <li> children of the top-level <ol>/<ul>. Filter strictly by\n // `.ed-listitem` class so any other DOM injected by the consumer (markers,\n // dividers) doesn't get counted as an item.\n const items: HTMLLIElement[] = [];\n for (const c of Array.from(el.children)) {\n if (c instanceof HTMLLIElement && c.classList.contains(\"ed-listitem\")) {\n items.push(c);\n }\n }\n\n if (items.length === 0) {\n const h = elTotalHeight(el);\n let currentPage = pages[pages.length - 1];\n if (startUsed + h > limit && currentPage.slots.length > 0) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n startUsed = 0;\n }\n currentPage.slots.push({ topLevelIndex });\n return { lastUsed: startUsed + h };\n }\n\n const heights = items.map((li) => elTotalHeight(li));\n // Vertical padding/margin contributed by the wrapping <ol>/<ul> itself\n // (margin-block + padding-block beyond the sum of item heights). We charge\n // this overhead only to the FIRST fragment's page so subsequent continuation\n // fragments don't double-count it.\n const sumHeights = heights.reduce((a, b) => a + b, 0);\n const wrapperOverhead = Math.max(0, elTotalHeight(el) - sumHeights);\n\n let i = 0;\n let currentPage = pages[pages.length - 1];\n let used = startUsed;\n let fragIndex = 0;\n\n while (i < items.length) {\n const available = Math.max(0, limit - used);\n // Greedy: include as many consecutive items as fit. bestK is the last\n // fitting item index.\n const isFirstFragment = fragIndex === 0;\n let bestK = -1;\n let runningH = isFirstFragment ? wrapperOverhead : 0;\n for (let k = i; k < items.length; k++) {\n runningH += heights[k];\n if (runningH <= available) bestK = k;\n else break;\n }\n\n if (bestK < 0) {\n // Not even one item fits in the remaining space.\n if (currentPage.slots.length === 0) {\n // Page is empty; force-place a single oversized item (bleed).\n bestK = i;\n } else {\n // Move to next page and retry.\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n continue;\n }\n }\n\n currentPage.slots.push({\n topLevelIndex,\n listFragment: { index: fragIndex, itemStart: i, itemEnd: bestK + 1 },\n });\n\n // Account for consumed height on the current page.\n let consumed = isFirstFragment ? wrapperOverhead : 0;\n for (let k = i; k <= bestK; k++) consumed += heights[k];\n used += consumed;\n\n i = bestK + 1;\n fragIndex++;\n if (i < items.length) {\n pages.push({ slots: [] });\n currentPage = pages[pages.length - 1];\n limit = limitFor(pages.length - 1);\n used = 0;\n }\n }\n\n return { lastUsed: used };\n}\n\nfunction collectTopLevelElements(root: HTMLElement): HTMLElement[] {\n // The root may be in one of three shapes:\n // - flat: root > top1, top2, ...\n // - paged simple: root > .ed-page > .ed-page-content > top1, top2, ...\n // - paged + fragments: same as above, but some \"tops\" are fragments of the\n // same block index. We don't expect to be called in\n // that state (caller guards on isUnfragmented), so\n // collecting direct children is fine.\n const pageContents = root.querySelectorAll<HTMLElement>(\":scope > .ed-page > .ed-page-content\");\n if (pageContents.length > 0) {\n const out: HTMLElement[] = [];\n pageContents.forEach((pc) => {\n Array.from(pc.children).forEach((c) => out.push(c as HTMLElement));\n });\n return out;\n }\n return Array.from(root.children) as HTMLElement[];\n}\n\n/**\n * Sum text-node lengths inside `el` plus 1 for each image embed, skipping any\n * descendant `[data-block-index]`. Mirrors the model's notion of \"length\".\n */\nfunction getBlockTextLength(el: HTMLElement): number {\n const walker = document.createTreeWalker(\n el,\n NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,\n blockBoundaryFilter(el),\n );\n let n: Node | null;\n let sum = 0;\n while ((n = walker.nextNode())) {\n if (n.nodeType === Node.TEXT_NODE) {\n sum += (n.textContent ?? \"\").length;\n } else if (\n n.nodeType === Node.ELEMENT_NODE &&\n (n as HTMLElement).dataset.embed === \"image\"\n ) {\n sum += 1;\n }\n }\n return sum;\n}\n\nfunction blockBoundaryFilter(blockEl: HTMLElement): NodeFilter {\n return {\n acceptNode(n: Node) {\n let p: Node | null = n.parentNode;\n while (p && p !== blockEl) {\n if (p.nodeType === Node.ELEMENT_NODE) {\n const e = p as HTMLElement;\n if (e.dataset.blockIndex != null) return NodeFilter.FILTER_REJECT;\n }\n p = p.parentNode;\n }\n return NodeFilter.FILTER_ACCEPT;\n },\n };\n}\n\n// ---------- Equality helpers ----------\n\nfunction shallowArrayEqual(a: unknown[], b: unknown[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n\nfunction layoutsDeepEqual(a: PageLayout, b: PageLayout): boolean {\n if (a.pages.length !== b.pages.length) return false;\n for (let i = 0; i < a.pages.length; i++) {\n const sA = a.pages[i].slots;\n const sB = b.pages[i].slots;\n if (sA.length !== sB.length) return false;\n for (let j = 0; j < sA.length; j++) {\n if (sA[j].topLevelIndex !== sB[j].topLevelIndex) return false;\n const fA = sA[j].fragment;\n const fB = sB[j].fragment;\n if (fA || fB) {\n if (!fA || !fB) return false;\n if (fA.index !== fB.index || fA.start !== fB.start || fA.end !== fB.end) return false;\n }\n const tA = sA[j].tableFragment;\n const tB = sB[j].tableFragment;\n if (tA || tB) {\n if (!tA || !tB) return false;\n if (tA.index !== tB.index || tA.rowStart !== tB.rowStart || tA.rowEnd !== tB.rowEnd) {\n return false;\n }\n }\n const lA = sA[j].listFragment;\n const lB = sB[j].listFragment;\n if (lA || lB) {\n if (!lA || !lB) return false;\n if (lA.index !== lB.index || lA.itemStart !== lB.itemStart || lA.itemEnd !== lB.itemEnd) {\n return false;\n }\n }\n }\n }\n return true;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { MAX_INSERT_WIDTH } from \"./imageConstraints\";\nimport { usePageSettings } from \"./usePageSettings\";\nimport {\n EditorDocument,\n EditorHistory,\n collapsedSelection,\n dedentList as cmdDedentList,\n deleteTable as cmdDeleteTable,\n deleteTableColumn as cmdDeleteColumn,\n deleteTableRow as cmdDeleteRow,\n insertImage as cmdInsertImage,\n mergeDown as cmdMergeDown,\n mergeRight as cmdMergeRight,\n mergeSelection as cmdMergeSelection,\n moveEmbedAnchor as cmdMoveEmbedAnchor,\n setBlockAttrAtIndex as cmdSetBlockAttrAtIndex,\n setColumnBoundary as cmdSetColumnBoundary,\n setImageAttrs as cmdSetImageAttrs,\n setRowHeight as cmdSetRowHeight,\n setRowHeights as cmdSetRowHeights,\n splitCell as cmdSplitCell,\n tableRectSelection,\n getMarksInRange,\n indentList as cmdIndentList,\n insertAnswerLines as cmdInsertAnswerLines,\n insertTable as cmdInsertTable,\n insertTableColumn as cmdInsertColumn,\n insertTableRow as cmdInsertRow,\n isFormulaEmbed,\n isImageEmbed,\n isMarkUniformInRange,\n moveToNextCell as cmdMoveNextCell,\n moveToPrevCell as cmdMovePrevCell,\n orderedRange,\n removeMark as cmdRemoveMark,\n setBlockAttr as cmdSetBlockAttr,\n setCellAttr as cmdSetCellAttr,\n setBlockType as cmdSetBlockType,\n setMark as cmdSetMark,\n setTableWidth as cmdSetTableWidth,\n splitListItem as cmdSplitListItem,\n tableLocationOf,\n toggleList as cmdToggleList,\n toggleMark as cmdToggleMark,\n type AlignValue,\n type BlockAttrs,\n type AnswerLineSpacing,\n type BlockType,\n type TableBorderPreset,\n type CommandContext,\n type DeltaOp,\n type EmbedLoc,\n type ImageEmbed,\n type ListKind,\n type MarkAttrs,\n type MarkName,\n type PageSettings,\n type SerializedDocument,\n type Selection,\n type TableLocation,\n type TableRect,\n} from \"@sofereditor/core\";\n\nexport interface UseEditorOptions {\n document?: EditorDocument;\n /**\n * Quando definido, `insertImageFromFile` chama esta função pra obter a URL\n * final em vez de embutir o arquivo como data URL base64 no Y.Doc.\n *\n * Use isto quando o app tem storage externo (S3, Azure Blob, etc.) e não quer\n * inflar o documento com imagens base64. O componente `<Editor>` propaga o\n * mesmo upload para paste/drop/file picker.\n */\n uploadImage?: (file: File) => Promise<string>;\n}\n\nexport type PendingMarks = Partial<Record<MarkName, MarkAttrs[MarkName] | null>>;\n\nexport interface LinkRequest {\n initialHref: string;\n resolve: (href: string | null) => void;\n /** Model selection captured when the link was requested (bug #6). */\n selection: Selection;\n}\n\nexport interface PageConfigRequest {\n initial: PageSettings;\n resolve: (applied: boolean) => void;\n}\n\nexport type CaptionAlign = \"left\" | \"center\" | \"right\";\n\nexport interface ImageCaptionResult {\n caption: string;\n align: CaptionAlign;\n}\n\nexport interface ImageCaptionRequest {\n initialCaption: string;\n initialAlign: CaptionAlign;\n /**\n * Resolves with the new caption + alignment, or null on cancel. An empty\n * `caption` string means \"remove caption\" (align is ignored in that case).\n */\n resolve: (result: ImageCaptionResult | null) => void;\n}\n\nexport interface FormulaResult {\n latex: string;\n display: boolean;\n}\n\nexport interface FormulaRequest {\n initialLatex: string;\n initialDisplay: boolean;\n /** Resolve com a fórmula, ou null no cancelamento. */\n resolve: (result: FormulaResult | null) => void;\n}\n\nexport interface UseEditorResult {\n doc: EditorDocument;\n history: EditorHistory;\n /** Snapshot of the model, re-computed on every Y.js update. */\n snapshot: SerializedDocument;\n /** Current model selection. Use `setSelection` to move the caret. */\n selection: Selection;\n setSelection: (sel: Selection) => void;\n /** Stable getter — commands read this to know where the caret is. */\n getSelection: () => Selection;\n\n // ---- Mark API (added in Sub-phase 2.1) ----\n isMarkActive: (name: MarkName, value?: MarkAttrs[MarkName]) => boolean;\n getActiveMarks: () => Partial<Record<MarkName, MarkAttrs[MarkName] | \"mixed\">>;\n toggleMark: (name: MarkName, value?: MarkAttrs[MarkName]) => void;\n setMark: (name: MarkName, value: MarkAttrs[MarkName]) => void;\n removeMark: (name: MarkName) => void;\n\n // ---- Block API (added in Sub-phase 2.2) ----\n /** Returns the type of the focus block (or \"mixed\" when the selection covers heterogeneous blocks). */\n getBlockType: () => BlockType | \"mixed\";\n /** Returns one block attribute at the focus block, or \"mixed\" when not uniform across the selection. */\n getBlockAttr: <K extends keyof BlockAttrs>(key: K) => BlockAttrs[K] | \"mixed\" | undefined;\n setBlockType: (type: BlockType, attrs?: BlockAttrs) => void;\n setBlockAttr: <K extends keyof BlockAttrs>(key: K, value: BlockAttrs[K] | null) => void;\n setAlign: (value: AlignValue) => void;\n getAlign: () => AlignValue | \"mixed\" | undefined;\n /** Cor de fundo das células selecionadas (rect) ou da célula focada. `null` remove. No-op fora de tabela. */\n setCellBackground: (color: string | null) => void;\n /** Cor de fundo da célula focada; undefined fora de tabela ou sem cor. */\n getCellBackground: () => string | undefined;\n /** Onde as linhas da grade da tabela focada aparecem. No-op fora de tabela. */\n setTableBorderPreset: (preset: TableBorderPreset) => void;\n /** Preset da tabela focada; \"all\" fora de tabela ou quando ausente. */\n getTableBorderPreset: () => TableBorderPreset;\n /** Cor das linhas da grade da tabela focada. `null` restaura o padrão. No-op fora de tabela. */\n setTableBorderColor: (color: string | null) => void;\n /** Cor da tabela focada; undefined fora de tabela ou quando usa o padrão. */\n getTableBorderColor: () => string | undefined;\n\n // ---- List API (added in Sub-phase 2.3) ----\n isListActive: (kind: ListKind) => boolean;\n toggleList: (kind: ListKind) => void;\n indentList: () => void;\n dedentList: () => void;\n /** Returns true when handled (current block is a listItem); false to fall back to insertParagraph. */\n splitListItem: () => boolean;\n\n /** Marks queued for the next `insertText` (e.g. user pressed Cmd+B with a collapsed caret). */\n pendingMarks: PendingMarks;\n /** Consumed by the input layer right before calling `insertText`. */\n consumePendingMarks: () => PendingMarks | undefined;\n\n // ---- Link dialog (Polish iteration) ----\n linkRequest: LinkRequest | null;\n /** Open the link dialog; resolves with the entered href, empty string to remove, or null on cancel. */\n requestLink: (initialHref?: string) => Promise<string | null>;\n /** Called by the LinkDialog itself when the user submits or cancels. */\n resolveLinkRequest: (href: string | null) => void;\n\n // ---- Page settings (Fase 9.6) ----\n /** Reactive page settings snapshot (re-renders when the Y.Doc docSettings changes). */\n pageSettings: PageSettings;\n /** Merge a partial PageSettings into the Y.Doc. Not undoable. */\n setPageSettings: (partial: Partial<PageSettings>) => void;\n /** Page-config dialog request (null when closed). */\n pageConfigRequest: PageConfigRequest | null;\n /** Open the page-config dialog; resolves true on apply, false on cancel. */\n requestPageConfig: () => Promise<boolean>;\n /** Resolver called by `<PageConfigDialog>` on apply/cancel. */\n resolvePageConfigRequest: (applied: boolean) => void;\n\n // ---- Image caption (Fase 9.6+) ----\n imageCaptionRequest: ImageCaptionRequest | null;\n /** Open the image-caption dialog with the current caption + alignment pre-filled. */\n requestImageCaption: (\n initialCaption?: string,\n initialAlign?: CaptionAlign,\n ) => Promise<ImageCaptionResult | null>;\n /** Resolver called by `<ImageCaptionDialog>` on apply/cancel. */\n resolveImageCaptionRequest: (result: ImageCaptionResult | null) => void;\n\n // ---- Fórmula matemática ----\n formulaRequest: FormulaRequest | null;\n requestFormula: (\n initialLatex?: string,\n initialDisplay?: boolean,\n ) => Promise<FormulaResult | null>;\n resolveFormulaRequest: (result: FormulaResult | null) => void;\n /** Renderiza o LaTeX, gera SVG + PNG e insere como embed na seleção. */\n insertFormula: (latex: string, display: boolean) => Promise<void>;\n\n // ---- Tables (Phase 4) ----\n /** True iff the focus position lies inside a table cell. */\n isInTable: () => boolean;\n /** Returns `{blockIndex, row, col}` for the focus position, or null when outside a table. */\n getTableLocation: () => TableLocation | null;\n /** Insere `count` linhas de resposta pautadas depois do bloco focado. */\n insertAnswerLines: (count: number, spacing: AnswerLineSpacing) => void;\n insertTable: (rows: number, cols: number) => void;\n /** Removes the table at `blockIndex`. Defaults to the current focus table. */\n deleteTable: (blockIndex?: number) => void;\n insertRowAbove: () => void;\n insertRowBelow: () => void;\n deleteCurrentRow: () => void;\n insertColumnLeft: () => void;\n insertColumnRight: () => void;\n deleteCurrentColumn: () => void;\n moveToNextCell: () => void;\n moveToPrevCell: () => void;\n mergeRight: () => boolean;\n mergeDown: () => boolean;\n splitCell: () => boolean;\n /** Move a divisa `boundary` (entre a coluna `boundary` e a `boundary+1`) por `deltaPct` pontos percentuais. */\n setColumnBoundary: (blockIndex: number, boundary: number, deltaPct: number, base?: number[]) => void;\n /** Define a largura total da tabela como percentual da largura útil da página. */\n setTableWidth: (blockIndex: number, pct: number) => void;\n /** Grava a altura de UMA linha, em px. É um mínimo, não fixo. */\n setRowHeight: (blockIndex: number, row: number, px: number) => void;\n /** Grava o array de alturas inteiro numa única transação. */\n setRowHeights: (blockIndex: number, heights: number[]) => void;\n /** Returns the table rectangle if the current selection is a multi-cell range, else null. */\n getTableRect: () => TableRect | null;\n /** True when there's a rectangular table-cell selection. */\n hasTableRectSelection: () => boolean;\n /** Merge every cell in the rectangular selection. Returns true on success. */\n mergeSelection: () => boolean;\n\n // ---- Inline images (Fase 5) ----\n insertImage: (embed: ImageEmbed) => void;\n setImageAttrs: (\n blockIndex: number,\n offset: number,\n partial: Partial<ImageEmbed>,\n cellIndex?: number,\n newOffset?: number,\n ) => void;\n /**\n * Move a floating image embed from one anchor to another (re-anchor across\n * blocks/pages) in a single transaction. No-op for inline embeds.\n */\n moveEmbedAnchor: (\n from: EmbedLoc,\n to: EmbedLoc,\n newOffsetX: number,\n newOffsetY: number,\n ) => void;\n /** Read a File (image) and insert it inline at the caret as a data-URL embed. */\n insertImageFromFile: (file: File) => Promise<void>;\n /**\n * Returns the embed currently selected (single 1-char range over an image),\n * or null when no embed is solely selected.\n */\n getSelectedEmbed: () =>\n | { blockIndex: number; offset: number; cellIndex?: number; embed: ImageEmbed }\n | null;\n /**\n * Passthrough of `UseEditorOptions.uploadImage` — `undefined` when not\n * configured. Exposed so callers with the editor instance but not the\n * original options (the `<Editor>` component's paste handler, notably) can\n * upload an image without going through `insertImageFromFile` (which also\n * inserts it — not always what's wanted, e.g. resolving `data:` embeds\n * already inside a pasted slice before `insertSlice`).\n */\n uploadImage?: (file: File) => Promise<string>;\n}\n\nexport function useEditor(opts: UseEditorOptions = {}): UseEditorResult {\n const doc = useMemo(() => opts.document ?? new EditorDocument(), [opts.document]);\n const history = useMemo(() => new EditorHistory(doc), [doc]);\n\n const [snapshot, setSnapshot] = useState<SerializedDocument>(() => doc.toJSON());\n const [selection, setSelectionState] = useState<Selection>(() =>\n collapsedSelection({ blockIndex: 0, offset: 0 }),\n );\n const selectionRef = useRef(selection);\n selectionRef.current = selection;\n\n // pendingMarks lives on a ref so the beforeinput handler reads the latest value\n // without re-binding listeners on every render. The state copy exists so the\n // Toolbar can render a \"pressed\" hint when a mark is armed at a collapsed caret.\n const pendingMarksRef = useRef<PendingMarks>({});\n const [pendingMarks, setPendingMarksState] = useState<PendingMarks>({});\n\n const setPendingMarks = useCallback((next: PendingMarks) => {\n pendingMarksRef.current = next;\n setPendingMarksState(next);\n }, []);\n\n useEffect(() => {\n // Coalesce snapshot recomputation: `afterTransaction` fires per Y.js\n // transaction, which means once per keystroke for normal typing — but on\n // paste / multi-op commands it can fire N times in a microtask burst.\n // `toJSON()` is expensive for docs with embedded images (each call deep-\n // clones every block's delta, including base64 image src). Coalescing\n // into rAF guarantees at most one recompute per animation frame regardless\n // of how many transactions fire underneath. A pending flag avoids\n // scheduling multiple rAFs.\n let scheduled = false;\n let cancelled = false;\n let frame = 0 as number | ReturnType<typeof requestAnimationFrame>;\n const flush = () => {\n scheduled = false;\n if (cancelled) return;\n setSnapshot(doc.toJSON());\n };\n const schedule = () => {\n if (scheduled) return;\n scheduled = true;\n // Use rAF when available (browser), fall back to microtask in jsdom/Node.\n if (typeof requestAnimationFrame === \"function\") {\n frame = requestAnimationFrame(flush);\n } else {\n queueMicrotask(flush);\n }\n };\n doc.ydoc.on(\"afterTransaction\", schedule);\n return () => {\n cancelled = true;\n if (\n scheduled &&\n typeof requestAnimationFrame === \"function\" &&\n typeof cancelAnimationFrame === \"function\"\n ) {\n cancelAnimationFrame(frame as number);\n }\n doc.ydoc.off(\"afterTransaction\", schedule);\n };\n }, [doc]);\n\n const getSelection = useCallback(() => selectionRef.current, []);\n const setSelection = useCallback(\n (sel: Selection) => {\n const prev = selectionRef.current;\n selectionRef.current = sel;\n setSelectionState(sel);\n // Any caret movement that isn't a direct typing follow-up clears the queue.\n if (\n Object.keys(pendingMarksRef.current).length > 0 &&\n (prev.focus.blockIndex !== sel.focus.blockIndex || prev.focus.offset !== sel.focus.offset)\n ) {\n // Heuristic: typing advances the caret by exactly 1; everything else clears.\n const advancedByOne =\n prev.focus.blockIndex === sel.focus.blockIndex &&\n sel.focus.offset === prev.focus.offset + 1;\n if (!advancedByOne) {\n pendingMarksRef.current = {};\n setPendingMarksState({});\n }\n }\n },\n [],\n );\n\n const ctxRef = useRef<CommandContext>({ doc, getSelection, setSelection });\n ctxRef.current = { doc, getSelection, setSelection };\n\n const consumePendingMarks = useCallback((): PendingMarks | undefined => {\n const current = pendingMarksRef.current;\n if (Object.keys(current).length === 0) return undefined;\n pendingMarksRef.current = {};\n setPendingMarksState({});\n return current;\n }, []);\n\n const isMarkActive = useCallback(\n (name: MarkName, value?: MarkAttrs[MarkName]): boolean => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n if (start.blockIndex === end.blockIndex && start.offset === end.offset) {\n // Collapsed: union of pending and ambient (mark of the char immediately before caret).\n const pending = pendingMarksRef.current[name];\n if (pending !== undefined) {\n if (pending === null) return false;\n return value === undefined ? true : sameValue(pending, value);\n }\n const yText = doc.getBlockText(start.blockIndex);\n if (!yText || start.offset === 0) return false;\n return isMarkUniformInRange(yText, start.offset - 1, start.offset, name, value);\n }\n if (start.blockIndex !== end.blockIndex) {\n // Multi-block: active only if uniformly active on every covered slice.\n for (let i = start.blockIndex; i <= end.blockIndex; i++) {\n const yText = doc.getBlockText(i);\n if (!yText) return false;\n const s = i === start.blockIndex ? start.offset : 0;\n const e = i === end.blockIndex ? end.offset : yText.length;\n if (s >= e) continue;\n if (!isMarkUniformInRange(yText, s, e, name, value)) return false;\n }\n return true;\n }\n const yText = doc.getBlockText(start.blockIndex);\n if (!yText) return false;\n return isMarkUniformInRange(yText, start.offset, end.offset, name, value);\n },\n [doc],\n );\n\n const getActiveMarks = useCallback(() => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n if (start.blockIndex !== end.blockIndex) {\n // For Sub-phase 2.1 we conservatively report empty across multi-block selections.\n return {};\n }\n const yText = doc.getBlockText(start.blockIndex);\n if (!yText) return {};\n if (start.offset === end.offset) {\n if (start.offset === 0) return {};\n return getMarksInRange(yText, start.offset - 1, start.offset);\n }\n return getMarksInRange(yText, start.offset, end.offset);\n }, [doc]);\n\n const toggleMark = useCallback(\n (name: MarkName, value?: MarkAttrs[MarkName]) => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;\n if (isCollapsed) {\n // Queue for next insert.\n const next: PendingMarks = { ...pendingMarksRef.current };\n const currentlyActive = isMarkActive(name, value);\n if (currentlyActive) {\n next[name] = null;\n } else {\n next[name] = value ?? (booleanMarks.has(name) ? true : null);\n }\n setPendingMarks(next);\n return;\n }\n cmdToggleMark(ctxRef.current, name, value);\n },\n [isMarkActive, setPendingMarks],\n );\n\n const setMark = useCallback(\n (name: MarkName, value: MarkAttrs[MarkName]) => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;\n if (isCollapsed) {\n setPendingMarks({ ...pendingMarksRef.current, [name]: value });\n return;\n }\n cmdSetMark(ctxRef.current, name, value);\n },\n [setPendingMarks],\n );\n\n const removeMark = useCallback(\n (name: MarkName) => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const isCollapsed = start.blockIndex === end.blockIndex && start.offset === end.offset;\n if (isCollapsed) {\n setPendingMarks({ ...pendingMarksRef.current, [name]: null });\n return;\n }\n cmdRemoveMark(ctxRef.current, name);\n },\n [setPendingMarks],\n );\n\n const getBlockType = useCallback((): BlockType | \"mixed\" => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const first = doc.getBlockType(start.blockIndex);\n if (first === undefined) return \"paragraph\";\n for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {\n if (doc.getBlockType(i) !== first) return \"mixed\";\n }\n return first;\n }, [doc]);\n\n const getBlockAttr = useCallback(\n <K extends keyof BlockAttrs>(key: K): BlockAttrs[K] | \"mixed\" | undefined => {\n const sel = selectionRef.current;\n const { start, end } = orderedRange(sel);\n const first = doc.getBlockAttrs(start.blockIndex)[key];\n for (let i = start.blockIndex + 1; i <= end.blockIndex; i++) {\n const next = doc.getBlockAttrs(i)[key];\n if (next !== first) return \"mixed\";\n }\n return first;\n },\n [doc],\n );\n\n const setBlockType = useCallback((type: BlockType, attrs?: BlockAttrs) => {\n cmdSetBlockType(ctxRef.current, type, attrs);\n }, []);\n\n const setBlockAttr = useCallback(\n <K extends keyof BlockAttrs>(key: K, value: BlockAttrs[K] | null) => {\n cmdSetBlockAttr(ctxRef.current, key, value);\n },\n [],\n );\n\n const setAlign = useCallback((value: AlignValue) => {\n const sel = selectionRef.current;\n // Route on focus only: setCellAttr keys off sel.focus.cellIndex internally\n // and returns early unless the focus position is inside a table cell.\n const inCell = sel.focus.cellIndex != null;\n if (inCell) cmdSetCellAttr(ctxRef.current, \"align\", value);\n else cmdSetBlockAttr(ctxRef.current, \"align\", value);\n }, []);\n\n const getAlign = useCallback((): AlignValue | \"mixed\" | undefined => {\n const sel = selectionRef.current;\n if (sel.focus.cellIndex != null && doc.isTable(sel.focus.blockIndex)) {\n // Cell path: single focused-cell read (multi-cell mixed-state is out of scope).\n return doc.getCellAttrs(sel.focus.blockIndex, sel.focus.cellIndex).align;\n }\n // Block path: delegate to getBlockAttr so multi-block selections return \"mixed\".\n return getBlockAttr(\"align\");\n }, [doc, getBlockAttr]);\n\n const setCellBackground = useCallback((color: string | null) => {\n // setCellAttr keys off sel.focus.cellIndex internally: applies to the\n // rect selection (or the focused cell) and no-ops outside a table.\n cmdSetCellAttr(ctxRef.current, \"bgColor\", color);\n }, []);\n\n const getCellBackground = useCallback((): string | undefined => {\n const sel = selectionRef.current;\n if (sel.focus.cellIndex == null || !doc.isTable(sel.focus.blockIndex)) return undefined;\n // Single focused-cell read, like getAlign (multi-cell mixed-state out of scope).\n return doc.getCellAttrs(sel.focus.blockIndex, sel.focus.cellIndex).bgColor;\n }, [doc]);\n\n const setTableBorderPreset = useCallback((preset: TableBorderPreset) => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return;\n // setBlockAttr é inerte com o caret dentro de uma célula — que é sempre o\n // caso aqui. Por isso a escrita é por índice de bloco.\n cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, \"borderPreset\", preset);\n }, [doc]);\n\n const getTableBorderPreset = useCallback((): TableBorderPreset => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return \"all\";\n return doc.getBlockAttrs(sel.focus.blockIndex).borderPreset ?? \"all\";\n }, [doc]);\n\n const setTableBorderColor = useCallback((color: string | null) => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return;\n cmdSetBlockAttrAtIndex(ctxRef.current, sel.focus.blockIndex, \"borderColor\", color);\n }, [doc]);\n\n const getTableBorderColor = useCallback((): string | undefined => {\n const sel = selectionRef.current;\n if (!doc.isTable(sel.focus.blockIndex)) return undefined;\n return doc.getBlockAttrs(sel.focus.blockIndex).borderColor;\n }, [doc]);\n\n const isListActive = useCallback(\n (kind: ListKind): boolean => {\n const sel = selectionRef.current;\n // Célula de tabela: o \"bloco\" ali é sempre \"table\" (a célula é um\n // Y.Text plano), então o estado de lista mora em CellAttrs.listKind,\n // não em BlockAttrs — mesmo padrão de leitura de getAlign/getCellBackground.\n // Foco pode pousar numa célula coberta por span (colspan/rowspan), que\n // não guarda `listKind` (só `covered: true`) — precisa redirecionar\n // pro owner real, mesmo redirecionamento que `toggleList` já faz na\n // leitura (commands.ts) pra não achar sempre \"sem lista\" ali.\n if (sel.focus.cellIndex != null && doc.isTable(sel.focus.blockIndex)) {\n const real = doc.realCellIndex(sel.focus.blockIndex, sel.focus.cellIndex);\n return real != null && doc.getCellAttrs(sel.focus.blockIndex, real).listKind === kind;\n }\n const { start, end } = orderedRange(sel);\n for (let i = start.blockIndex; i <= end.blockIndex; i++) {\n if (doc.getBlockType(i) !== \"listItem\") return false;\n if (doc.getBlockAttrs(i).listKind !== kind) return false;\n }\n return true;\n },\n [doc],\n );\n\n const toggleList = useCallback((kind: ListKind) => {\n cmdToggleList(ctxRef.current, kind);\n }, []);\n const indentList = useCallback(() => {\n cmdIndentList(ctxRef.current);\n }, []);\n const dedentList = useCallback(() => {\n cmdDedentList(ctxRef.current);\n }, []);\n const splitListItem = useCallback((): boolean => {\n return cmdSplitListItem(ctxRef.current);\n }, []);\n\n // ---- Tables ----\n const isInTable = useCallback((): boolean => {\n return selectionRef.current.focus.cellIndex != null;\n }, []);\n const getTableLocation = useCallback((): TableLocation | null => {\n return tableLocationOf(doc, selectionRef.current.focus);\n }, [doc]);\n const insertTable = useCallback((rows: number, cols: number) => {\n cmdInsertTable(ctxRef.current, rows, cols);\n }, []);\n const insertAnswerLines = useCallback((count: number, spacing: AnswerLineSpacing) => {\n cmdInsertAnswerLines(ctxRef.current, count, spacing);\n }, []);\n const deleteTable = useCallback((blockIndex?: number) => {\n const idx = blockIndex ?? selectionRef.current.focus.blockIndex;\n cmdDeleteTable(ctxRef.current, idx);\n }, []);\n const insertRowAbove = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertRow(ctxRef.current, loc.blockIndex, loc.row, \"before\");\n }, [doc]);\n const insertRowBelow = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertRow(ctxRef.current, loc.blockIndex, loc.row, \"after\");\n }, [doc]);\n const deleteCurrentRow = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdDeleteRow(ctxRef.current, loc.blockIndex, loc.row);\n }, [doc]);\n const insertColumnLeft = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertColumn(ctxRef.current, loc.blockIndex, loc.col, \"before\");\n }, [doc]);\n const insertColumnRight = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdInsertColumn(ctxRef.current, loc.blockIndex, loc.col, \"after\");\n }, [doc]);\n const deleteCurrentColumn = useCallback(() => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return;\n cmdDeleteColumn(ctxRef.current, loc.blockIndex, loc.col);\n }, [doc]);\n const moveToNextCell = useCallback(() => {\n cmdMoveNextCell(ctxRef.current);\n }, []);\n const moveToPrevCell = useCallback(() => {\n cmdMovePrevCell(ctxRef.current);\n }, []);\n const mergeRight = useCallback((): boolean => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return false;\n return cmdMergeRight(ctxRef.current, loc.blockIndex, loc.row, loc.col);\n }, [doc]);\n const mergeDown = useCallback((): boolean => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return false;\n return cmdMergeDown(ctxRef.current, loc.blockIndex, loc.row, loc.col);\n }, [doc]);\n const splitCell = useCallback((): boolean => {\n const loc = tableLocationOf(doc, selectionRef.current.focus);\n if (!loc) return false;\n return cmdSplitCell(ctxRef.current, loc.blockIndex, loc.row, loc.col);\n }, [doc]);\n const setColumnBoundary = useCallback(\n (blockIndex: number, boundary: number, deltaPct: number, base?: number[]) => {\n cmdSetColumnBoundary(ctxRef.current, blockIndex, boundary, deltaPct, base);\n },\n [],\n );\n const setTableWidth = useCallback((blockIndex: number, pct: number) => {\n cmdSetTableWidth(ctxRef.current, blockIndex, pct);\n }, []);\n const setRowHeight = useCallback((blockIndex: number, row: number, px: number) => {\n cmdSetRowHeight(ctxRef.current, blockIndex, row, px);\n }, []);\n const setRowHeights = useCallback((blockIndex: number, heights: number[]) => {\n cmdSetRowHeights(ctxRef.current, blockIndex, heights);\n }, []);\n const getTableRect = useCallback(\n () => tableRectSelection(doc, selectionRef.current),\n [doc],\n );\n const hasTableRectSelection = useCallback((): boolean => {\n return tableRectSelection(doc, selectionRef.current) !== null;\n }, [doc]);\n const mergeSelection = useCallback((): boolean => {\n return cmdMergeSelection(ctxRef.current);\n }, []);\n\n // ---- Inline images ----\n const insertImage = useCallback((embed: ImageEmbed) => {\n cmdInsertImage(ctxRef.current, embed);\n }, []);\n const setImageAttrs = useCallback(\n (\n blockIndex: number,\n offset: number,\n partial: Partial<ImageEmbed>,\n cellIndex?: number,\n newOffset?: number,\n ) => {\n cmdSetImageAttrs(ctxRef.current, blockIndex, offset, partial, cellIndex, newOffset);\n },\n [],\n );\n const moveEmbedAnchor = useCallback(\n (from: EmbedLoc, to: EmbedLoc, newOffsetX: number, newOffsetY: number) => {\n cmdMoveEmbedAnchor(ctxRef.current, from, to, newOffsetX, newOffsetY);\n },\n [],\n );\n const uploadImageRef = useRef(opts.uploadImage);\n uploadImageRef.current = opts.uploadImage;\n const insertImageFromFile = useCallback(async (file: File): Promise<void> => {\n if (!file.type.startsWith(\"image/\")) return;\n // Lê dimensões a partir de um dataUrl temporário em ambos os caminhos\n // (lê o arquivo só uma vez; o uploadImage decide se persiste essa data\n // ou faz upload pra um storage externo).\n const dataUrl = await readAsDataURL(file);\n const dims = await readImageDimensions(dataUrl);\n let w = dims.width;\n let h = dims.height;\n if (w > MAX_INSERT_WIDTH) {\n h = Math.round((h * MAX_INSERT_WIDTH) / w);\n w = MAX_INSERT_WIDTH;\n }\n const upload = uploadImageRef.current;\n const src = upload ? await upload(file) : dataUrl;\n cmdInsertImage(ctxRef.current, { type: \"image\", src, width: w, height: h });\n }, []);\n const getSelectedEmbed = useCallback(() => {\n const sel = selectionRef.current;\n if (sel.anchor.blockIndex !== sel.focus.blockIndex) return null;\n if ((sel.anchor.cellIndex ?? -1) !== (sel.focus.cellIndex ?? -1)) return null;\n const a = sel.anchor.offset;\n const b = sel.focus.offset;\n if (Math.abs(a - b) !== 1) return null;\n const start = Math.min(a, b);\n const yText = doc.textAt(sel.anchor.blockIndex, sel.anchor.cellIndex);\n if (!yText) return null;\n const delta = yText.toDelta() as DeltaOp[];\n let cursor = 0;\n for (const op of delta) {\n if (typeof op.insert === \"string\") {\n cursor += op.insert.length;\n continue;\n }\n if (cursor === start && isImageEmbed(op.insert)) {\n return {\n blockIndex: sel.anchor.blockIndex,\n cellIndex: sel.anchor.cellIndex,\n offset: start,\n embed: op.insert,\n };\n }\n cursor += 1;\n }\n return null;\n }, [doc]);\n\n const [linkRequest, setLinkRequest] = useState<LinkRequest | null>(null);\n const linkRequestRef = useRef<LinkRequest | null>(null);\n const requestLink = useCallback((initialHref = \"\"): Promise<string | null> => {\n return new Promise((resolve) => {\n // Capture the model selection NOW (bug #6): the modal round-trip can\n // collapse it (the editor refocuses on close → selectionchange overwrites\n // the model), and the consumer applies the link mark AFTER the modal —\n // a no-op on a collapsed selection. We restore it on resolve.\n const req: LinkRequest = { initialHref, resolve, selection: selectionRef.current };\n linkRequestRef.current = req;\n setLinkRequest(req);\n });\n }, []);\n const resolveLinkRequest = useCallback(\n (href: string | null) => {\n const req = linkRequestRef.current;\n linkRequestRef.current = null;\n setLinkRequest(null);\n if (req) {\n // Restore the captured selection so the consumer's setMark applies to\n // the originally-selected text even if the modal collapsed it.\n setSelection(req.selection);\n req.resolve(href);\n }\n },\n [setSelection],\n );\n\n // ---- Page settings ----\n const pageSettings = usePageSettings(doc);\n const setPageSettings = useCallback(\n (partial: Partial<PageSettings>) => {\n doc.setPageSettings(partial);\n },\n [doc],\n );\n const [pageConfigRequest, setPageConfigRequest] = useState<PageConfigRequest | null>(null);\n const requestPageConfig = useCallback((): Promise<boolean> => {\n return new Promise((resolve) => {\n setPageConfigRequest({ initial: doc.getPageSettings(), resolve });\n });\n }, [doc]);\n const resolvePageConfigRequest = useCallback((applied: boolean) => {\n setPageConfigRequest((prev) => {\n if (prev) prev.resolve(applied);\n return null;\n });\n }, []);\n\n // ---- Image caption ----\n const [imageCaptionRequest, setImageCaptionRequest] =\n useState<ImageCaptionRequest | null>(null);\n const requestImageCaption = useCallback(\n (initialCaption = \"\", initialAlign: CaptionAlign = \"center\"): Promise<ImageCaptionResult | null> => {\n return new Promise((resolve) => {\n setImageCaptionRequest({ initialCaption, initialAlign, resolve });\n });\n },\n [],\n );\n const resolveImageCaptionRequest = useCallback((result: ImageCaptionResult | null) => {\n setImageCaptionRequest((prev) => {\n if (prev) prev.resolve(result);\n return null;\n });\n }, []);\n\n // ---- Fórmula ----\n const [formulaRequest, setFormulaRequest] = useState<FormulaRequest | null>(null);\n const requestFormula = useCallback(\n (initialLatex = \"\", initialDisplay = false): Promise<FormulaResult | null> => {\n return new Promise((resolve) => {\n setFormulaRequest({ initialLatex, initialDisplay, resolve });\n });\n },\n [],\n );\n const resolveFormulaRequest = useCallback((result: FormulaResult | null) => {\n setFormulaRequest((prev) => {\n if (prev) prev.resolve(result);\n return null;\n });\n }, []);\n\n const insertFormula = useCallback(\n async (latex: string, display: boolean): Promise<void> => {\n // Snapshot da seleção ANTES de qualquer `await`. Esta função tem um\n // `await import(...)` logo abaixo (e outro `await`, no PNG de\n // fallback) NO MEIO do próprio corpo — um gap assíncrono real, não só\n // um round-trip de Promise síncrono. Se o `<dialog>` do modal (que já\n // fechou antes de `insertFormula` ser chamada) devolver o foco ao\n // editor e a seleção do modelo colapsar durante esse gap (bug #6 —\n // mesmo motivo de `LinkRequest.selection`, mas aqui SEM jeito de o\n // chamador segurar a seleção através do `await`, porque ele não vê o\n // await por dentro), `cmdInsertImage` (mais abaixo) leria uma seleção\n // colapsada, `insertImage` (`commands.ts`) pularia o `deleteRange`\n // inteiro (`isCollapsed` verdadeiro) e a fórmula antiga sobreviveria\n // ao lado da nova — duas fórmulas no documento. Capturar aqui e\n // restaurar logo antes de `cmdInsertImage` fecha esse gap pra QUALQUER\n // chamador (toolbar, duplo clique, botão \"Editar fórmula\") num lugar\n // só. Não dá pra resolver espelhando `resolveLinkRequest` (restaurar a\n // seleção ali, no `resolve()`): essa restauração aconteceria ANTES do\n // `await` desta função, não depois — o gap continuaria aberto.\n const selectionAtCall = ctxRef.current.getSelection();\n // Mesma linha de raciocínio do comentário acima: `getSelectedEmbed()`\n // também precisa rodar ANTES do primeiro `await`, lendo a seleção AO\n // VIVO no mesmo instante síncrono de `selectionAtCall` — depois do gap\n // ela pode ter colapsado (mesmo bug #6) e `getSelectedEmbed()` não\n // acharia mais nada para herdar. Usado abaixo, perto de\n // `cmdInsertImage`, só para herdar layout/posição de uma edição —\n // nunca para decidir ONDE inserir (isso é `selectionAtCall`).\n const embedAtCall = getSelectedEmbed();\n // Import DINÂMICO, pelo mesmo motivo do modal: manter o mathjax-full\n // fora do bundle principal. Como esta função já é async, não custa nada.\n let mathModule: typeof import(\"@sofereditor/math\");\n try {\n mathModule = await import(\"@sofereditor/math\");\n } catch {\n // Chunk falhou ao carregar (ex.: deploy do portal trocou o hash dos\n // assets enquanto a aba estava aberta). Sair em silêncio é certo\n // aqui: quem chama `insertFormula` só chega depois de já ter visto\n // o preview funcionar no modal (mesmo import, resolvido lá) — o\n // feedback de erro é responsabilidade do `FormulaDialog`, não desta\n // função.\n return;\n }\n const { renderLatexToSvg, measureExInPx, svgToDataUrl, svgToPngDataUrl } = mathModule;\n const r = renderLatexToSvg(latex, display);\n if (!r.ok) return; // o modal já barra isto; aqui é cinto de segurança\n // `useEditor` não tem ref do elemento raiz — conferido. `.ed-root` é o\n // mesmo seletor que TableFloatingToolbar usa para achar a raiz do editor.\n // Medir no <body> daria o font-size do documento HTML, não o do editor.\n const root = document.querySelector<HTMLElement>(\".ed-root\");\n const exPx = root ? measureExInPx(root) : 8;\n let w = Math.round(r.widthEx * exPx);\n let h = Math.round(r.heightEx * exPx);\n // vAlignEx é relativo à ALTURA renderizada (`r.heightEx`, não clampada).\n // Quando o clamp abaixo encolhe w/h, o `ex` da linha de base tem que\n // encolher pelo MESMO fator — senão a fórmula clampada senta fora da\n // linha de base, deslocada proporcionalmente ao quanto foi clampada.\n // Fator 1 (sem clamp) é o caso comum e não altera nada.\n let vAlignScale = 1;\n if (w > MAX_INSERT_WIDTH) {\n vAlignScale = MAX_INSERT_WIDTH / w;\n // Fórmula de `h` inalterada (mesma ordem de operações de antes do\n // fix) — só a variável `vAlignScale` é nova.\n h = Math.round((h * MAX_INSERT_WIDTH) / w);\n w = MAX_INSERT_WIDTH;\n }\n const src = svgToDataUrl(r.svg);\n let svgFallback: string | undefined;\n try {\n svgFallback = await svgToPngDataUrl(r.svg, w, h);\n } catch {\n // Sem PNG a fórmula ainda entra no documento e sai no PDF; só o DOCX\n // vai pular esse embed, contando em `skipped`. Melhor que não inserir.\n svgFallback = undefined;\n }\n // Restaura a seleção capturada ANTES do primeiro `await` lá em cima —\n // ver o comentário no topo da função. Sem isto, `cmdInsertImage` lê a\n // seleção AO VIVO no instante em que roda (`ctx.getSelection()` dentro\n // de `insertImage`, `commands.ts`), que pode ter colapsado durante o\n // gap assíncrono.\n ctxRef.current.setSelection(selectionAtCall);\n // Herda layout/posição do embed que está sendo SUBSTITUÍDO (edição de\n // uma fórmula existente — duplo clique ou \"Editar fórmula\" na\n // toolbar), nunca de uma imagem comum: `embedAtCall` pode ser uma\n // imagem selecionada quando a fórmula está sendo inserida do zero pela\n // toolbar, e herdar o layout dela seria pior que não herdar nada.\n // `align` NÃO entra aqui de propósito — é derivado de `display` logo\n // abaixo (bloco → \"center\"); herdá-lo reintroduziria a centralização\n // depois de o professor desmarcar \"Fórmula em bloco\". `w`/`h` também\n // não são preservados: o valor salvo é medida+clamp do LaTeX ANTIGO, a\n // razão do resize aplicado pelo usuário não é recuperável sem\n // re-renderizar o LaTeX antigo, e o novo tem tamanho natural diferente\n // — re-medir é o comportamento pretendido, não um bug a \"consertar\".\n const inheritedLayout: Pick<ImageEmbed, \"layout\" | \"offsetX\" | \"offsetY\"> = {};\n if (isFormulaEmbed(embedAtCall?.embed)) {\n // Spreads condicionais por campo, não um objeto fixo com chaves\n // `undefined`: o embed vira um insert de Y.Text (`yText.insert`, em\n // `commands.ts`) que viaja pela rede — melhor não gravar chaves\n // `offsetX`/`offsetY` explicitamente `undefined` (ex.: fórmula\n // antiga em `layout: \"inline\"`, que não tem offsets) quando dá pra\n // simplesmente omiti-las.\n const prev = embedAtCall.embed;\n if (prev.layout !== undefined) inheritedLayout.layout = prev.layout;\n if (prev.offsetX !== undefined) inheritedLayout.offsetX = prev.offsetX;\n if (prev.offsetY !== undefined) inheritedLayout.offsetY = prev.offsetY;\n }\n cmdInsertImage(ctxRef.current, {\n type: \"image\",\n src,\n width: w,\n height: h,\n ...inheritedLayout,\n ...(display ? { align: \"center\" as const } : {}),\n formula: {\n latex,\n display,\n ...(display ? {} : { vAlign: `${r.vAlignEx * vAlignScale}ex` }),\n },\n ...(svgFallback ? { svgFallback } : {}),\n });\n },\n [getSelectedEmbed],\n );\n\n return {\n doc,\n history,\n snapshot,\n selection,\n setSelection,\n getSelection,\n isMarkActive,\n getActiveMarks,\n toggleMark,\n setMark,\n removeMark,\n pendingMarks,\n consumePendingMarks,\n getBlockType,\n getBlockAttr,\n setBlockType,\n setBlockAttr,\n setAlign,\n getAlign,\n setCellBackground,\n getCellBackground,\n setTableBorderPreset,\n getTableBorderPreset,\n setTableBorderColor,\n getTableBorderColor,\n isListActive,\n toggleList,\n indentList,\n dedentList,\n splitListItem,\n linkRequest,\n requestLink,\n resolveLinkRequest,\n isInTable,\n getTableLocation,\n insertAnswerLines,\n insertTable,\n deleteTable,\n insertRowAbove,\n insertRowBelow,\n deleteCurrentRow,\n insertColumnLeft,\n insertColumnRight,\n deleteCurrentColumn,\n moveToNextCell,\n moveToPrevCell,\n mergeRight,\n mergeDown,\n splitCell,\n setColumnBoundary,\n setTableWidth,\n setRowHeight,\n setRowHeights,\n getTableRect,\n hasTableRectSelection,\n mergeSelection,\n insertImage,\n setImageAttrs,\n moveEmbedAnchor,\n insertImageFromFile,\n getSelectedEmbed,\n pageSettings,\n setPageSettings,\n pageConfigRequest,\n requestPageConfig,\n resolvePageConfigRequest,\n imageCaptionRequest,\n requestImageCaption,\n resolveImageCaptionRequest,\n formulaRequest,\n requestFormula,\n resolveFormulaRequest,\n insertFormula,\n uploadImage: opts.uploadImage,\n };\n}\n\nfunction readAsDataURL(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = () => reject(reader.error ?? new Error(\"FileReader error\"));\n reader.onload = () => resolve(reader.result as string);\n reader.readAsDataURL(file);\n });\n}\n\nfunction readImageDimensions(src: string): Promise<{ width: number; height: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onerror = () => reject(new Error(\"image load failed\"));\n img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });\n img.src = src;\n });\n}\n\nconst booleanMarks: Set<MarkName> = new Set([\"bold\", \"italic\", \"underline\", \"strike\"]);\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a === \"object\" && a && b && typeof b === \"object\") {\n const ak = Object.keys(a);\n const bk = Object.keys(b);\n if (ak.length !== bk.length) return false;\n return ak.every((k) => (a as Record<string, unknown>)[k] === (b as Record<string, unknown>)[k]);\n }\n return false;\n}\n","import { useCallback, useRef, useSyncExternalStore } from \"react\";\nimport type { EditorDocument, PageSettings } from \"@sofereditor/core\";\n\n/**\n * Reactive snapshot of an `EditorDocument`'s page settings. Re-renders the\n * component when `docSettings` changes (either locally via `setPageSettings`\n * or remotely via Y.js sync).\n *\n * Caches the snapshot reference to satisfy `useSyncExternalStore`'s stable-\n * identity requirement — `getPageSettings` returns a fresh object each call,\n * which would loop the store forever without this caching.\n */\nexport function usePageSettings(doc: EditorDocument): PageSettings {\n const cacheRef = useRef<PageSettings | null>(null);\n\n const subscribe = useCallback(\n (notify: () => void) => doc.observePageSettings(notify),\n [doc],\n );\n\n const getSnapshot = useCallback((): PageSettings => {\n const next = doc.getPageSettings();\n const prev = cacheRef.current;\n if (prev && shallowEqual(prev, next)) return prev;\n cacheRef.current = next;\n return next;\n }, [doc]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nfunction shallowEqual(a: PageSettings, b: PageSettings): boolean {\n return (\n a.width === b.width &&\n a.height === b.height &&\n a.marginTop === b.marginTop &&\n a.marginBottom === b.marginBottom &&\n a.marginLeft === b.marginLeft &&\n a.marginRight === b.marginRight &&\n a.preset === b.preset &&\n a.systemHeaderTitle === b.systemHeaderTitle &&\n a.systemHeaderAuthor === b.systemHeaderAuthor\n );\n}\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type JSX,\n type MouseEvent,\n type ReactNode,\n} from \"react\";\nimport { useEditorContext } from \"./EditorContext\";\nimport { ANSWER_LINE_MAX, isFormulaEmbed, TABLE_BORDER_COLOR } from \"@sofereditor/core\";\nimport { TableBorderPanel, BORDER_MENU_GLYPH } from \"./TableBorderPanel\";\nimport type {\n AlignValue,\n AnswerLineSpacing,\n BlockType,\n DirValue,\n ImageLayout,\n MarkName,\n} from \"@sofereditor/core\";\n\nconst FONT_SIZES = [\"8pt\", \"10pt\", \"11pt\", \"12pt\", \"14pt\", \"16pt\", \"18pt\", \"24pt\", \"32pt\", \"48pt\"];\n\ninterface BlockOption {\n value: string;\n label: string;\n type: BlockType;\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nconst BLOCK_OPTIONS: BlockOption[] = [\n { value: \"paragraph\", label: \"Parágrafo\", type: \"paragraph\" },\n { value: \"h1\", label: \"Título 1\", type: \"heading\", level: 1 },\n { value: \"h2\", label: \"Título 2\", type: \"heading\", level: 2 },\n { value: \"h3\", label: \"Título 3\", type: \"heading\", level: 3 },\n { value: \"h4\", label: \"Título 4\", type: \"heading\", level: 4 },\n { value: \"h5\", label: \"Título 5\", type: \"heading\", level: 5 },\n { value: \"h6\", label: \"Título 6\", type: \"heading\", level: 6 },\n { value: \"blockquote\", label: \"Citação\", type: \"blockquote\" },\n { value: \"codeBlock\", label: \"Código\", type: \"codeBlock\" },\n];\n\nconst ALIGN_OPTIONS: { value: AlignValue; label: string; title: string }[] = [\n { value: \"left\", label: \"⇤\", title: \"Alinhar à esquerda\" },\n { value: \"center\", label: \"≡\", title: \"Centralizar\" },\n { value: \"right\", label: \"⇥\", title: \"Alinhar à direita\" },\n { value: \"justify\", label: \"☰\", title: \"Justificar\" },\n];\n\nconst IMAGE_ALIGN_VALUES = new Set<AlignValue>([\"left\", \"center\", \"right\"]);\n\n/**\n * Controles nativos que PRECISAM receber o `mousedown` para abrir seu\n * dropdown/picker. Prevenir o mousedown num `<select>` impede o dropdown de\n * abrir — foi exatamente assim que o seletor de bordas da tabela nasceu morto.\n *\n * A regra vivia duplicada em três handlers com três listas diferentes; fica\n * aqui numa fonte só para não voltar a divergir.\n */\nconst NATIVE_CONTROL_TAGS = new Set([\"SELECT\", \"INPUT\", \"OPTION\"]);\n\nexport function isNativeControl(t: EventTarget | null): boolean {\n const el = t as HTMLElement | null;\n return !!el && NATIVE_CONTROL_TAGS.has(el.tagName);\n}\n\n/**\n * Alvos dentro de um popover que mantêm o próprio `mousedown`. Superconjunto de\n * `isNativeControl` mais `BUTTON` — o resto é fundo de popover, onde o\n * `preventDefault` protege a seleção do editor.\n */\nexport function keepsOwnMouseDown(t: EventTarget | null): boolean {\n const el = t as HTMLElement | null;\n return !!el && (NATIVE_CONTROL_TAGS.has(el.tagName) || el.tagName === \"BUTTON\");\n}\n\nconst IMAGE_LAYOUT_OPTIONS: { value: ImageLayout; label: string; title: string }[] = [\n { value: \"inline\", label: \"↔\", title: \"Inline (no fluxo do texto)\" },\n { value: \"wrap-left\", label: \"◧\", title: \"Texto contorna à direita\" },\n { value: \"wrap-right\", label: \"◨\", title: \"Texto contorna à esquerda\" },\n { value: \"behind\", label: \"▢↓\", title: \"Atrás do texto\" },\n { value: \"front\", label: \"▢↑\", title: \"Na frente do texto\" },\n];\n\nexport interface ToolbarProps {\n className?: string;\n}\n\nexport function Toolbar({ className }: ToolbarProps): JSX.Element {\n const editor = useEditorContext();\n const {\n isMarkActive,\n getActiveMarks,\n toggleMark,\n setMark,\n removeMark,\n getBlockType,\n getBlockAttr,\n setBlockType,\n setBlockAttr,\n setAlign,\n getAlign,\n isListActive,\n toggleList,\n indentList,\n dedentList,\n } = editor;\n const active = getActiveMarks();\n const blockType = getBlockType();\n const blockLevel = getBlockAttr(\"level\");\n const blockAlign = getAlign();\n const blockDir = getBlockAttr(\"dir\");\n\n // Preserve contenteditable focus when clicking the toolbar background or buttons,\n // BUT let native form controls (select/input) receive mousedown normally so their\n // dropdowns/pickers can open.\n const stop = (e: MouseEvent) => {\n if (isNativeControl(e.target)) return;\n e.preventDefault();\n };\n\n const onToggle = (name: MarkName) => (e: MouseEvent) => {\n e.preventDefault();\n toggleMark(name);\n };\n\n const onColorChange = (e: ChangeEvent<HTMLInputElement>) => {\n setMark(\"color\", e.target.value);\n };\n\n const onHighlightChange = (e: ChangeEvent<HTMLInputElement>) => {\n setMark(\"highlight\", e.target.value);\n };\n\n const onFontSizeChange = (e: ChangeEvent<HTMLSelectElement>) => {\n const v = e.target.value;\n if (v === \"\") removeMark(\"fontSize\");\n else setMark(\"fontSize\", v);\n };\n\n const onBlockChange = (e: ChangeEvent<HTMLSelectElement>) => {\n const opt = BLOCK_OPTIONS.find((o) => o.value === e.target.value);\n if (!opt) return;\n setBlockType(opt.type, opt.level !== undefined ? { level: opt.level } : undefined);\n };\n\n const onAlign = (value: AlignValue) => (e: MouseEvent) => {\n e.preventDefault();\n // When an image embed is selected, the three image-friendly alignments\n // (left/center/right) target the embed itself; otherwise fall through to\n // the block-level align attribute (which also handles \"justify\").\n const selectedEmbed = editor.getSelectedEmbed();\n if (selectedEmbed && IMAGE_ALIGN_VALUES.has(value)) {\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n { align: value as \"left\" | \"center\" | \"right\" },\n selectedEmbed.cellIndex,\n );\n return;\n }\n setAlign(value);\n };\n\n const onInsertImageClick = useCallback(\n (e: MouseEvent) => {\n e.preventDefault();\n imageInputRef.current?.click();\n },\n [],\n );\n const onImageInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (file) void editor.insertImageFromFile(file);\n e.target.value = \"\";\n },\n [editor],\n );\n const imageInputRef = useRef<HTMLInputElement>(null);\n\n const onToggleDir = (e: MouseEvent) => {\n e.preventDefault();\n const next: DirValue = blockDir === \"rtl\" ? \"ltr\" : \"rtl\";\n setBlockAttr(\"dir\", next);\n };\n\n const linkActive = isMarkActive(\"link\");\n const onLinkClick = useCallback(\n (e: MouseEvent) => {\n e.preventDefault();\n const currentHref =\n active.link && typeof active.link === \"object\" && \"href\" in active.link\n ? (active.link.href as string)\n : \"\";\n void editor.requestLink(currentHref).then((href) => {\n if (href === null) return;\n if (href === \"\") removeMark(\"link\");\n else setMark(\"link\", { href });\n });\n },\n [active.link, editor, removeMark, setMark],\n );\n\n const colorValue = typeof active.color === \"string\" ? active.color : \"#000000\";\n // Amarelo clássico de marca-texto como padrão do picker quando nada está marcado.\n const highlightValue = typeof active.highlight === \"string\" ? active.highlight : \"#fff176\";\n const fontSizeValue = typeof active.fontSize === \"string\" ? active.fontSize : \"\";\n const blockValue = blockSelectValue(blockType, blockLevel);\n\n // The image layout group is only visible when an image embed is selected.\n // We read it fresh every render so the visibility tracks selection changes.\n const selectedEmbed = editor.getSelectedEmbed();\n const currentImageLayout: ImageLayout = selectedEmbed\n ? (selectedEmbed.embed.layout ?? \"inline\")\n : \"inline\";\n\n const onImageLayoutClick = (layout: ImageLayout) => (e: MouseEvent) => {\n e.preventDefault();\n const sel = editor.getSelectedEmbed();\n if (!sel) return;\n // Reset position offsets when switching modes; the previous values would\n // refer to a different coordinate system (margins vs. left/top).\n const reset =\n layout !== (sel.embed.layout ?? \"inline\")\n ? { offsetX: 0, offsetY: 0 }\n : {};\n editor.setImageAttrs(\n sel.blockIndex,\n sel.offset,\n { layout, ...reset },\n sel.cellIndex,\n );\n };\n\n return (\n <div\n className={className ?? \"ed-toolbar\"}\n role=\"toolbar\"\n aria-label=\"Editor toolbar\"\n onMouseDown={stop}\n >\n <Group>\n <select\n className=\"ed-toolbar-select\"\n value={blockValue}\n onChange={onBlockChange}\n onMouseDown={stop}\n aria-label=\"Tipo de bloco\"\n >\n {blockValue === \"\" && <option value=\"\">— (misto)</option>}\n {BLOCK_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>{o.label}</option>\n ))}\n </select>\n </Group>\n\n <Group>\n <MarkButton title=\"Negrito (⌘B)\" pressed={isMarkActive(\"bold\")} onClick={onToggle(\"bold\")}>\n <strong>B</strong>\n </MarkButton>\n <MarkButton title=\"Itálico (⌘I)\" pressed={isMarkActive(\"italic\")} onClick={onToggle(\"italic\")}>\n <em>I</em>\n </MarkButton>\n <MarkButton title=\"Sublinhado (⌘U)\" pressed={isMarkActive(\"underline\")} onClick={onToggle(\"underline\")}>\n <u>U</u>\n </MarkButton>\n <MarkButton title=\"Tachado (⌘⇧X)\" pressed={isMarkActive(\"strike\")} onClick={onToggle(\"strike\")}>\n <s>S</s>\n </MarkButton>\n </Group>\n\n <Group>\n <label className=\"ed-toolbar-label\">\n <span className=\"ed-toolbar-swatch\" aria-hidden style={{ background: colorValue }} />\n <input type=\"color\" value={colorValue} onChange={onColorChange} onMouseDown={stop} aria-label=\"Cor do texto\" />\n </label>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Remover cor\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n removeMark(\"color\");\n }}\n >\n ⌫\n </button>\n <label className=\"ed-toolbar-label\" title=\"Marca-texto\">\n <span\n className=\"ed-toolbar-swatch ed-toolbar-swatch--highlight\"\n aria-hidden\n style={{ background: highlightValue }}\n />\n <input\n type=\"color\"\n value={highlightValue}\n onChange={onHighlightChange}\n onMouseDown={stop}\n aria-label=\"Marca-texto\"\n />\n </label>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Remover marca-texto\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n removeMark(\"highlight\");\n }}\n >\n ⌫\n </button>\n </Group>\n\n <Group>\n <select\n className=\"ed-toolbar-select\"\n value={fontSizeValue}\n onChange={onFontSizeChange}\n onMouseDown={stop}\n aria-label=\"Tamanho da fonte\"\n >\n <option value=\"\">Tamanho…</option>\n {FONT_SIZES.map((s) => (\n <option key={s} value={s}>{s}</option>\n ))}\n </select>\n </Group>\n\n <Group>\n {ALIGN_OPTIONS.map((a) => (\n <button\n key={a.value}\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title={a.title}\n aria-pressed={blockAlign === a.value}\n onMouseDown={stop}\n onClick={onAlign(a.value)}\n >\n {a.label}\n </button>\n ))}\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={blockDir === \"rtl\"}\n title={blockDir === \"rtl\" ? \"Mudar para LTR\" : \"Mudar para RTL (hebraico)\"}\n onMouseDown={stop}\n onClick={onToggleDir}\n >\n {blockDir === \"rtl\" ? \"RTL\" : \"LTR\"}\n </button>\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={isListActive(\"bullet\")}\n title=\"Lista com marcadores (⌘⇧8)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n toggleList(\"bullet\");\n }}\n >\n •\n </button>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={isListActive(\"ordered\")}\n title=\"Lista numerada (⌘⇧7)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n toggleList(\"ordered\");\n }}\n >\n 1.\n </button>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Diminuir recuo (⇧Tab)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n dedentList();\n }}\n >\n ⇤\n </button>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Aumentar recuo (Tab)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n indentList();\n }}\n >\n ⇥\n </button>\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={linkActive}\n title={linkActive ? \"Remover link (⌘K)\" : \"Adicionar link (⌘K)\"}\n onMouseDown={stop}\n onClick={onLinkClick}\n >\n 🔗\n </button>\n </Group>\n\n <Group>\n <TableMenu />\n <AnswerLinesMenu />\n </Group>\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Inserir imagem\"\n onMouseDown={stop}\n onClick={onInsertImageClick}\n >\n 🖼\n </button>\n <input\n ref={imageInputRef}\n type=\"file\"\n accept=\"image/*\"\n hidden\n onChange={onImageInputChange}\n />\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Inserir fórmula\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n // `insertFormula` retornada (não disparada solta) para cair na\n // MESMA cadeia: um só `.catch` cobre tanto `requestFormula`\n // quanto `insertFormula`. Nenhuma das duas deveria rejeitar hoje\n // (`insertFormula` já protege o import dinâmico com try/catch),\n // mas sem este `.catch` uma rejeição inesperada vira unhandled\n // rejection — ruído em qualquer monitoramento de erro.\n void editor\n .requestFormula()\n .then((r) => (r ? editor.insertFormula(r.latex, r.display) : undefined))\n .catch((err) => {\n console.error(\"Falha ao inserir fórmula:\", err);\n });\n }}\n >\n √x\n </button>\n </Group>\n\n {selectedEmbed && (\n <Group>\n {IMAGE_LAYOUT_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title={opt.title}\n aria-pressed={currentImageLayout === opt.value}\n onMouseDown={stop}\n onClick={onImageLayoutClick(opt.value)}\n >\n {opt.label}\n </button>\n ))}\n {isFormulaEmbed(selectedEmbed.embed) ? (\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Editar fórmula\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n // NÃO é a mesma checagem \"morta\" de antes: o narrowing de\n // `isFormulaEmbed` no ternário (linha acima) não atravessa a\n // fronteira deste closure — `onClick` é uma função separada,\n // e dentro dela `selectedEmbed.embed.formula` volta a ser\n // `FormulaMeta | undefined` pro TS (`tsc --noEmit` reprova\n // sem este re-check; testado). Revalidar aqui é barato e\n // idempotente — nada muda entre o render e o clique.\n if (!isFormulaEmbed(selectedEmbed.embed)) return;\n const f = selectedEmbed.embed.formula;\n // A restauração da seleção do embed através do round-trip do\n // modal (bug #6) mora dentro de `insertFormula`\n // (`useEditor.ts`) — é a única com o `await` no meio, então é\n // a única que consegue segurar a seleção através do gap.\n void editor.requestFormula(f.latex, f.display).then((r) => {\n if (!r) return;\n void editor.insertFormula(r.latex, r.display);\n });\n }}\n >\n Editar fórmula\n </button>\n ) : (\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Legenda da imagem\"\n aria-pressed={!!selectedEmbed.embed.caption}\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n const initialCaption = selectedEmbed.embed.caption ?? \"\";\n const initialAlign = selectedEmbed.embed.captionAlign ?? \"center\";\n void editor.requestImageCaption(initialCaption, initialAlign).then((result) => {\n if (result === null) return; // cancelled\n const empty = result.caption.length === 0;\n editor.setImageAttrs(\n selectedEmbed.blockIndex,\n selectedEmbed.offset,\n {\n caption: empty ? undefined : result.caption,\n captionAlign: empty ? undefined : result.align,\n },\n selectedEmbed.cellIndex,\n );\n });\n }}\n >\n Legenda\n </button>\n )}\n </Group>\n )}\n\n <Group>\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Configurar página (tamanho, margens)\"\n onMouseDown={stop}\n onClick={(e) => {\n e.preventDefault();\n void editor.requestPageConfig();\n }}\n >\n ⚙\n </button>\n </Group>\n </div>\n );\n}\n\n// ---------- Table menu ----------\n\n/**\n * Linhas de resposta: parágrafos pautados para o aluno escrever.\n *\n * Um botão em vez da digitação de underlines porque a régua precisa acompanhar\n * a margem — uma fileira de `_` quebra ao mudar tamanho de página ou fonte.\n */\nfunction AnswerLinesMenu(): JSX.Element {\n const editor = useEditorContext();\n const [open, setOpen] = useState(false);\n const [count, setCount] = useState(5);\n const [spacing, setSpacing] = useState<AnswerLineSpacing>(1.5);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onDown = (e: globalThis.MouseEvent) => {\n if (!rootRef.current) return;\n if (rootRef.current.contains(e.target as Node)) return;\n setOpen(false);\n };\n document.addEventListener(\"mousedown\", onDown);\n return () => document.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n const clamped = Math.max(1, Math.min(ANSWER_LINE_MAX, Math.trunc(count) || 1));\n\n return (\n <div ref={rootRef} className=\"ed-toolbar-tablemenu\">\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Linhas de resposta\"\n aria-pressed={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.preventDefault();\n setOpen((v) => !v);\n }}\n >\n ☰\n </button>\n {open && (\n <div\n className=\"ed-table-popover ed-answerlines-popover\"\n role=\"menu\"\n onMouseDown={(e) => {\n if (!keepsOwnMouseDown(e.target)) e.preventDefault();\n }}\n >\n <label className=\"ed-toolbar-label\">\n Quantidade\n <input\n type=\"number\"\n min={1}\n max={ANSWER_LINE_MAX}\n value={count}\n onChange={(e) => setCount(Number(e.target.value))}\n aria-label=\"Quantidade de linhas\"\n />\n </label>\n <label className=\"ed-toolbar-label\">\n Entrelinha\n <select\n value={String(spacing)}\n onChange={(e) => setSpacing(Number(e.target.value) as AnswerLineSpacing)}\n aria-label=\"Entrelinha\"\n >\n <option value=\"1\">Simples</option>\n <option value=\"1.5\">1,5</option>\n <option value=\"2\">Duplo</option>\n </select>\n </label>\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false);\n editor.insertAnswerLines(clamped, spacing);\n }}\n >\n Inserir {clamped} linha{clamped > 1 ? \"s\" : \"\"}\n </button>\n </div>\n )}\n </div>\n );\n}\n\nfunction TableMenu(): JSX.Element {\n const editor = useEditorContext();\n const inTable = editor.isInTable();\n const [open, setOpen] = useState(false);\n const [bordersOpen, setBordersOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const onDown = (e: globalThis.MouseEvent) => {\n if (!rootRef.current) return;\n if (rootRef.current.contains(e.target as Node)) return;\n setOpen(false);\n };\n document.addEventListener(\"mousedown\", onDown);\n return () => document.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n // Fechar o menu ▦ recolhe o painel: reabrir sempre começa do estado neutro.\n useEffect(() => {\n if (!open) setBordersOpen(false);\n }, [open]);\n\n return (\n <div ref={rootRef} className=\"ed-toolbar-tablemenu\">\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n title=\"Tabela\"\n aria-pressed={open}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.preventDefault();\n setOpen((v) => !v);\n }}\n >\n ▦\n </button>\n {open && (\n <div\n className=\"ed-table-popover\"\n role=\"menu\"\n onMouseDown={(e) => {\n // Do not let mousedown clear the editor selection.\n if (!keepsOwnMouseDown(e.target)) e.preventDefault();\n }}\n >\n <TableSizePicker\n onPick={(rows, cols) => {\n setOpen(false);\n editor.insertTable(rows, cols);\n }}\n />\n {inTable && (\n <div className=\"ed-table-actions\">\n <hr />\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertRowAbove(); }}>\n Inserir linha acima\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertRowBelow(); }}>\n Inserir linha abaixo\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertColumnLeft(); }}>\n Inserir coluna à esquerda\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.insertColumnRight(); }}>\n Inserir coluna à direita\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.deleteCurrentRow(); }}>\n Excluir linha\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.deleteCurrentColumn(); }}>\n Excluir coluna\n </button>\n <hr />\n {editor.hasTableRectSelection() && (\n <button\n type=\"button\"\n onClick={() => { setOpen(false); editor.mergeSelection(); }}\n >\n Mesclar células selecionadas\n </button>\n )}\n <button type=\"button\" onClick={() => { setOpen(false); editor.mergeRight(); }}>\n Mesclar com célula à direita\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.mergeDown(); }}>\n Mesclar com célula abaixo\n </button>\n <button type=\"button\" onClick={() => { setOpen(false); editor.splitCell(); }}>\n Dividir células mescladas\n </button>\n <hr />\n <label className=\"ed-toolbar-label ed-table-bgcolor\">\n <span\n className=\"ed-toolbar-swatch\"\n aria-hidden\n style={{ background: editor.getCellBackground() ?? \"#ffffff\" }}\n />\n Cor de fundo da célula\n <input\n type=\"color\"\n value={editor.getCellBackground() ?? \"#ffffff\"}\n onChange={(e) => editor.setCellBackground(e.target.value)}\n aria-label=\"Cor de fundo da célula\"\n />\n </label>\n <button type=\"button\" onClick={() => { setOpen(false); editor.setCellBackground(null); }}>\n Remover cor de fundo\n </button>\n <hr />\n <button\n type=\"button\"\n className=\"ed-border-toggle\"\n aria-pressed={bordersOpen}\n aria-expanded={bordersOpen}\n title=\"Bordas da tabela\"\n aria-label=\"Bordas da tabela\"\n onClick={(e) => {\n e.preventDefault();\n setBordersOpen((v) => !v);\n }}\n >\n {BORDER_MENU_GLYPH}\n </button>\n {bordersOpen && (\n <TableBorderPanel\n preset={editor.getTableBorderPreset()}\n color={editor.getTableBorderColor() ?? TABLE_BORDER_COLOR}\n onPreset={(p) => editor.setTableBorderPreset(p)}\n onColor={(c) => editor.setTableBorderColor(c)}\n onResetColor={() => editor.setTableBorderColor(null)}\n />\n )}\n <hr />\n <button\n type=\"button\"\n className=\"ed-table-delete\"\n onClick={() => { setOpen(false); editor.deleteTable(); }}\n >\n Excluir tabela\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nconst PICKER_MAX_ROWS = 8;\nconst PICKER_MAX_COLS = 10;\n\ninterface TableSizePickerProps {\n onPick: (rows: number, cols: number) => void;\n}\n\nfunction TableSizePicker({ onPick }: TableSizePickerProps): JSX.Element {\n const [hover, setHover] = useState<{ r: number; c: number } | null>(null);\n const label = hover ? `${hover.r + 1} × ${hover.c + 1}` : \"Inserir tabela\";\n return (\n <div className=\"ed-table-picker\">\n <div className=\"ed-table-picker-label\">{label}</div>\n <div\n className=\"ed-table-picker-grid\"\n style={{\n gridTemplateColumns: `repeat(${PICKER_MAX_COLS}, 18px)`,\n gridTemplateRows: `repeat(${PICKER_MAX_ROWS}, 18px)`,\n }}\n onMouseLeave={() => setHover(null)}\n >\n {Array.from({ length: PICKER_MAX_ROWS }, (_, r) =>\n Array.from({ length: PICKER_MAX_COLS }, (_, c) => {\n const filled = hover != null && r <= hover.r && c <= hover.c;\n return (\n <button\n key={`${r}-${c}`}\n type=\"button\"\n className={`ed-table-picker-cell${filled ? \" filled\" : \"\"}`}\n onMouseEnter={() => setHover({ r, c })}\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.preventDefault();\n onPick(r + 1, c + 1);\n }}\n aria-label={`${r + 1} × ${c + 1}`}\n />\n );\n }),\n )}\n </div>\n </div>\n );\n}\n\nfunction blockSelectValue(\n type: BlockType | \"mixed\",\n level: unknown,\n): string {\n if (type === \"mixed\") return \"\";\n if (type === \"heading\") {\n if (typeof level === \"number\" && level >= 1 && level <= 6) return `h${level}`;\n return \"h1\";\n }\n return type;\n}\n\nfunction Group({ children }: { children: ReactNode }): JSX.Element {\n return <div className=\"ed-toolbar-group\">{children}</div>;\n}\n\ninterface MarkButtonProps {\n title: string;\n pressed: boolean;\n onClick: (e: MouseEvent) => void;\n children: ReactNode;\n}\n\nfunction MarkButton({ title, pressed, onClick, children }: MarkButtonProps): JSX.Element {\n return (\n <button\n type=\"button\"\n className=\"ed-toolbar-btn\"\n aria-pressed={pressed}\n title={title}\n onMouseDown={(e) => e.preventDefault()}\n onClick={onClick}\n >\n {children}\n </button>\n );\n}\n","import type { JSX, MouseEvent } from \"react\";\nimport type { TableBorderPreset } from \"@sofereditor/core\";\n\n/**\n * Vocabulário do `w:tblBorders` do Word. Glifos de texto em vez de biblioteca\n * de ícones para manter o idioma do `Toolbar.tsx` (⇤ ≡ ⇥ ☰ • 🔗 ▦ 🖼 ⚙) e não\n * pendurar uma dependência de ícones num pacote publicado.\n *\n * `⊞` (U+229E) é o quadrado dividido em quatro — o ícone de bordas do Word e do\n * Docs. NÃO trocar por `▦`: é grade fina, e já é o glifo do botão de tabela.\n */\nexport const BORDER_PRESETS: readonly {\n key: TableBorderPreset;\n label: string;\n glyph: string;\n}[] = [\n { key: \"all\", label: \"Todas\", glyph: \"⊞\" },\n { key: \"outer\", label: \"Só externas\", glyph: \"□\" },\n { key: \"horizontal\", label: \"Só horizontais\", glyph: \"▤\" },\n { key: \"vertical\", label: \"Só verticais\", glyph: \"▥\" },\n { key: \"none\", label: \"Nenhuma\", glyph: \"⬚\" },\n];\n\n/** Glifo do gatilho. Mesmo desenho do preset \"all\", como no Word. */\nexport const BORDER_MENU_GLYPH = \"⊞\";\n\nexport interface TableBorderPanelProps {\n preset: TableBorderPreset;\n /** Cor atual da linha, já resolvida (nunca `null`). */\n color: string;\n onPreset: (p: TableBorderPreset) => void;\n onColor: (c: string) => void;\n onResetColor: () => void;\n}\n\n/**\n * Conteúdo do painel de bordas: os cinco presets e a cor da linha.\n *\n * Puro de propósito — sem contexto do editor e sem estado. Quem hospeda decide\n * onde ele aparece e de onde vêm os valores; assim dá para testá-lo por render\n * estática, que é a única forma de teste disponível neste pacote.\n */\nexport function TableBorderPanel({\n preset,\n color,\n onPreset,\n onColor,\n onResetColor,\n}: TableBorderPanelProps): JSX.Element {\n return (\n <div className=\"ed-border-panel\">\n <div className=\"ed-border-presets\">\n {BORDER_PRESETS.map((p) => (\n <button\n key={p.key}\n type=\"button\"\n className=\"ed-border-preset\"\n aria-pressed={preset === p.key}\n title={p.label}\n onClick={(e: MouseEvent) => {\n e.preventDefault();\n onPreset(p.key);\n }}\n >\n <span className=\"ed-border-glyph\" aria-hidden>\n {p.glyph}\n </span>\n {p.label}\n </button>\n ))}\n </div>\n <hr />\n <label className=\"ed-toolbar-label ed-border-color\">\n <span className=\"ed-toolbar-swatch\" aria-hidden style={{ background: color }} />\n Cor da linha\n <input\n type=\"color\"\n value={color}\n onChange={(e) => onColor(e.target.value)}\n aria-label=\"Cor da linha da borda\"\n />\n </label>\n <button\n type=\"button\"\n className=\"ed-border-reset\"\n onClick={(e: MouseEvent) => {\n e.preventDefault();\n onResetColor();\n }}\n >\n Padrão\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_core","import_core","import_core","import_jsx_runtime","import_react","import_react","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_react","import_core","import_react","import_core","import_jsx_runtime","import_react","import_core","import_jsx_runtime","import_jsx_runtime","clampLevel","_","import_react","currentPage","import_react","import_react","import_core","yText","cmdToggleMark","cmdSetMark","cmdRemoveMark","cmdSetBlockType","cmdSetBlockAttr","cmdSetCellAttr","cmdSetBlockAttrAtIndex","cmdToggleList","cmdIndentList","cmdDedentList","cmdSplitListItem","cmdInsertTable","cmdInsertAnswerLines","cmdDeleteTable","cmdInsertRow","cmdDeleteRow","cmdInsertColumn","cmdDeleteColumn","cmdMoveNextCell","cmdMovePrevCell","cmdMergeRight","cmdMergeDown","cmdSplitCell","cmdSetColumnBoundary","cmdSetTableWidth","cmdSetRowHeight","cmdSetRowHeights","cmdMergeSelection","cmdInsertImage","cmdSetImageAttrs","cmdMoveEmbedAnchor","import_jsx_runtime","selectedEmbed","clampListLevel","import_react","import_core","import_jsx_runtime","import_jsx_runtime","selectedEmbed","_"]}