@elabs-ai/components-editor 5.0.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-FO5S3YZM.js → chunk-VTGYYWGH.js} +21 -4
- package/dist/chunk-VTGYYWGH.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/markdown/index.js +1 -1
- package/package.json +7 -5
- package/src/markdown-editor/directive-nodes.ts +30 -3
- package/src/markdown-editor/markdown-editor.directives.test.tsx +12 -0
- package/src/markdown-editor/markdown-editor.insert.test.tsx +37 -0
- package/src/markdown-editor/markdown-editor.tsx +9 -3
- package/dist/chunk-FO5S3YZM.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/editor-context-menu/editor-context-menu.tsx","../src/lib/monaco-theme-bridge.ts","../src/lib/use-data-theme.ts","../src/code-editor/code-editor.tsx","../src/calc-block/calc-editor.ts","../src/calc-block/calc-editor-prose.ts","../src/lib/editor-completions.ts","../src/lib/editor-content-access-prose.ts","../src/lib/markdown/markdown-scale.ts","../src/markdown-iteration/edit-context.ts","../src/lib/markdown/frontmatter.ts","../src/lib/markdown/slugify.ts","../src/markdown-outline/markdown-outline.ts","../src/markdown-outline/document-outline.tsx","../src/markdown-iteration/iteration.tsx","../src/markdown-iteration/iteration-builder.ts","../src/metric-block/metric-block.tsx","../src/markdown-editor/slash/insert-directive.ts","../src/markdown-editor/directive-nodes.ts","../src/markdown-editor/slash/brand-slash-commands.ts","../src/markdown-editor/slash/slash-menu.tsx","../src/markdown-editor/slash/brand-slash-plugin.ts","../src/markdown-editor/slash/shortcut.ts","../src/markdown-editor/slash/slash-widget.tsx","../src/markdown-editor/slash/index.ts","../src/markdown-editor/markdown-editor.tsx","../src/markdown-editor/completions/completions-prose.ts","../src/markdown-editor/completions/completions-widget.tsx","../src/markdown-editor/completions/completions-menu.tsx","../src/markdown-editor/completions/index.ts","../src/markdown-editor/directive-views.tsx","../src/markdown-editor/milkdown-react/editor.tsx","../src/markdown-editor/milkdown-react/use-get-editor.ts","../src/markdown-editor/milkdown-react/use-editor.ts","../src/markdown-editor/milkdown-react/use-instance.ts","../src/markdown-editor/exit-keymap.ts","../src/markdown-editor/paste-embed.ts","../src/markdown-editor/table-view.tsx","../src/copy-button/copy-button.tsx","../src/lib/editor-content-access.ts"],"sourcesContent":["\"use client\";\n\nimport {\n ContextMenu,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuTrigger,\n} from \"@elabs-ai/components-ui\";\nimport type * as Monaco from \"monaco-editor\";\nimport { type ReactNode } from \"react\";\n\nexport interface EditorContextMenuProps {\n /** The Monaco editor instance to act on (null until mounted). */\n editor: Monaco.editor.IStandaloneCodeEditor | null;\n /** Hide editing items (cut/paste/format) for read-only editors. */\n readOnly?: boolean;\n /** The editor element wrapped as the right-click trigger. */\n children: ReactNode;\n}\n\n/**\n * Replaces Monaco's built-in context menu with brand-ui's `ContextMenu`, wired\n * to the editor instance. This is the \"reuse our components instead of Monaco's\n * built-in ones\" path for the right-click menu — set `contextMenu=\"brand\"` on\n * `CodeEditor` (the default) and it disables Monaco's menu and renders this.\n *\n * Clipboard ops use the async Clipboard API + `executeEdits` (reliable in the\n * browser); other entries delegate to the editor's own actions.\n */\nexport function EditorContextMenu({ editor, readOnly = false, children }: EditorContextMenuProps) {\n const selection = () => {\n if (!editor) return null;\n const model = editor.getModel();\n const sel = editor.getSelection();\n return model && sel ? { editor, model, sel } : null;\n };\n\n const copy = async () => {\n const ctx = selection();\n if (!ctx) return;\n const text = ctx.model.getValueInRange(ctx.sel);\n if (text && typeof navigator !== \"undefined\" && navigator.clipboard) {\n await navigator.clipboard.writeText(text);\n }\n ctx.editor.focus();\n };\n\n const cut = async () => {\n const ctx = selection();\n if (!ctx) return;\n const text = ctx.model.getValueInRange(ctx.sel);\n if (text && typeof navigator !== \"undefined\" && navigator.clipboard) {\n await navigator.clipboard.writeText(text);\n ctx.editor.executeEdits(\"brand-cut\", [{ range: ctx.sel, text: \"\", forceMoveMarkers: true }]);\n }\n ctx.editor.focus();\n };\n\n const paste = async () => {\n const ctx = selection();\n if (!ctx || typeof navigator === \"undefined\" || !navigator.clipboard) return;\n const text = await navigator.clipboard.readText();\n ctx.editor.executeEdits(\"brand-paste\", [{ range: ctx.sel, text, forceMoveMarkers: true }]);\n ctx.editor.focus();\n };\n\n const selectAll = () => {\n const ctx = selection();\n if (!ctx) return;\n ctx.editor.setSelection(ctx.model.getFullModelRange());\n ctx.editor.focus();\n };\n\n const runAction = (id: string) => {\n editor?.focus();\n void editor?.getAction(id)?.run();\n };\n\n return (\n <ContextMenu>\n <ContextMenuTrigger asChild>{children}</ContextMenuTrigger>\n <ContextMenuContent className=\"w-52\">\n {!readOnly ? (\n <ContextMenuItem onSelect={() => void cut()}>\n Cut\n <ContextMenuShortcut>⌘X</ContextMenuShortcut>\n </ContextMenuItem>\n ) : null}\n <ContextMenuItem onSelect={() => void copy()}>\n Copy\n <ContextMenuShortcut>⌘C</ContextMenuShortcut>\n </ContextMenuItem>\n {!readOnly ? (\n <ContextMenuItem onSelect={() => void paste()}>\n Paste\n <ContextMenuShortcut>⌘V</ContextMenuShortcut>\n </ContextMenuItem>\n ) : null}\n <ContextMenuSeparator />\n <ContextMenuItem onSelect={selectAll}>\n Select all\n <ContextMenuShortcut>⌘A</ContextMenuShortcut>\n </ContextMenuItem>\n {!readOnly ? (\n <ContextMenuItem onSelect={() => runAction(\"editor.action.formatDocument\")}>\n Format document\n <ContextMenuShortcut>⇧⌥F</ContextMenuShortcut>\n </ContextMenuItem>\n ) : null}\n <ContextMenuSeparator />\n <ContextMenuItem onSelect={() => runAction(\"editor.action.quickCommand\")}>\n Command palette\n <ContextMenuShortcut>F1</ContextMenuShortcut>\n </ContextMenuItem>\n </ContextMenuContent>\n </ContextMenu>\n );\n}\n","\"use client\";\n\nimport type * as Monaco from \"monaco-editor\";\nimport { oklchToHex, resolveThemeIsDark, type ThemeName } from \"@elabs-ai/components-tokens\";\n\n/**\n * Bridges brand-ui's semantic tokens onto Monaco's theming API so the editor\n * surface AND Monaco's own widgets (suggestion dropdown, find box, hovers,\n * context menu) are recolored from the active brand theme.\n *\n * Why this exists: brand tokens are authored in `oklch(...)` but\n * `monaco.editor.defineTheme` only accepts hex. The shared `oklchToHex`\n * (`@elabs-ai/components-tokens`, ADR 0015) converts the oklch tokens dependency-free; a 1×1\n * canvas rasterize remains only as the fallback for non-oklch CSS colors\n * (`rgb()`, named), since `getComputedStyle` does NOT serialize `oklch()` to\n * `rgb()` in Chromium. Results are cached per raw token value.\n */\n\nconst hexCache = new Map<string, string>();\nlet ctx: CanvasRenderingContext2D | null = null;\n\n/** Normalize any CSS color string (incl. `oklch(...)`) to `#rrggbb` / `#rrggbbaa`. */\nfunction resolveCssColor(value: string, fallback = \"#000000\"): string {\n const raw = value.trim();\n if (!raw) return fallback;\n if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;\n const cached = hexCache.get(raw);\n if (cached) return cached;\n const viaOklch = oklchToHex(raw);\n if (viaOklch) {\n hexCache.set(raw, viaOklch);\n return viaOklch;\n }\n if (typeof document === \"undefined\") return fallback;\n\n if (!ctx) {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 1;\n canvas.height = 1;\n ctx = canvas.getContext(\"2d\", { willReadFrequently: true });\n }\n if (!ctx) return fallback;\n\n // An invalid color leaves `fillStyle` unchanged, so prime it with the fallback.\n ctx.fillStyle = \"#000000\";\n ctx.fillStyle = fallback;\n ctx.fillStyle = raw;\n ctx.clearRect(0, 0, 1, 1);\n ctx.fillRect(0, 0, 1, 1);\n const data = ctx.getImageData(0, 0, 1, 1).data;\n const r = data[0] ?? 0;\n const g = data[1] ?? 0;\n const b = data[2] ?? 0;\n const a = data[3] ?? 255;\n const hex =\n a < 255 ? `#${byte(r)}${byte(g)}${byte(b)}${byte(a)}` : `#${byte(r)}${byte(g)}${byte(b)}`;\n hexCache.set(raw, hex);\n return hex;\n}\nconst clamp01 = (n: number) => (Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 1);\nconst byte = (n: number) => n.toString(16).padStart(2, \"0\");\n\n/** Mix `alpha` (0..1) into a `#rrggbb` color, returning `#rrggbbaa`. */\nexport function withAlpha(hex: string, alpha: number): string {\n const base = hex.slice(0, 7);\n return `${base}${byte(Math.round(clamp01(alpha) * 255))}`;\n}\n\n/**\n * Alpha of the translucent cursor-line highlight Monaco paints UNDER every\n * token's text on the active line (`editor.lineHighlightBackground`). Named\n * and shared so the visual overlay and the AA-contrast ground it composites\n * into (`flattenOver`, below) can never drift apart (#88).\n */\nexport const LINE_HIGHLIGHT_ALPHA = 0.05;\n\n/** Strip `#` and any alpha — Monaco token rules want a bare 6-char hex. */\nfunction bare(hex: string): string {\n return hex.replace(\"#\", \"\").slice(0, 6).padEnd(6, \"0\");\n}\n\n// --- Contrast helpers: keep syntax tokens legible on the editor background. ---\nconst channel = (hex: string, i: number) => parseInt(hex.slice(1 + i * 2, 3 + i * 2), 16) || 0;\nconst toLinear = (c: number) => {\n const s = c / 255;\n return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);\n};\nfunction luminance(hex: string): number {\n return (\n 0.2126 * toLinear(channel(hex, 0)) +\n 0.7152 * toLinear(channel(hex, 1)) +\n 0.0722 * toLinear(channel(hex, 2))\n );\n}\nexport function contrast(a: string, b: string): number {\n const la = luminance(a);\n const lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\n/**\n * Composite a translucent `#rrggbbaa` overlay (an alpha-suffixed hex, as\n * `withAlpha` produces) over an opaque `#rrggbb` ground — the same straight,\n * source-over blend a browser/Monaco applies when it paints a translucent\n * decoration on top of the editor surface. Returns the resulting opaque\n * `#rrggbb`.\n *\n * Exists so contrast clamps (and tests) can target the REAL, on-screen ground\n * a syntax color renders against, not the bare, uncomposited surface color —\n * see #88.\n */\nexport function flattenOver(overlayHexWithAlpha: string, groundHex: string): string {\n const overlayBase = overlayHexWithAlpha.slice(0, 7);\n const alphaHex = overlayHexWithAlpha.length >= 9 ? overlayHexWithAlpha.slice(7, 9) : \"ff\";\n const alpha = clamp01((parseInt(alphaHex, 16) || 0) / 255);\n const blend = (i: number) =>\n Math.round(channel(overlayBase, i) * alpha + channel(groundHex, i) * (1 - alpha));\n return `#${byte(blend(0))}${byte(blend(1))}${byte(blend(2))}`;\n}\nfunction mixHex(hex: string, target: string, t: number): string {\n const lerp = (i: number) =>\n Math.round(channel(hex, i) + (channel(target, i) - channel(hex, i)) * t);\n return `#${byte(lerp(0))}${byte(lerp(1))}${byte(lerp(2))}`;\n}\n/**\n * Darken/lighten `hex` toward black (on light backgrounds) or white (on dark)\n * until it clears `minRatio` against `bg`. Keeps on-palette syntax colors\n * legible across themes — notably saturated tokens on a white editor — without\n * hardcoding per-theme values.\n */\nfunction ensureReadable(hex: string, bg: string, minRatio: number): string {\n const base = hex.slice(0, 7);\n if (contrast(base, bg) >= minRatio) return base;\n const target = luminance(bg) > 0.5 ? \"#000000\" : \"#ffffff\";\n let out = base;\n for (let t = 0.1; t <= 1.0001; t += 0.1) {\n out = mixHex(base, target, t);\n if (contrast(out, bg) >= minRatio) break;\n }\n return out;\n}\n\n/**\n * Map the theme active on `rootEl` to Monaco's nearest built-in base.\n *\n * Resolved from the element (the theme's own `color-scheme`), not from a\n * registry lookup on the NAME — so a consumer-authored dark theme gets\n * `vs-dark` without registering anything here (ADR 0029). Getting this wrong is\n * visible: `vs` under a dark theme leaves Monaco's own chrome (the sticky-scroll\n * shadow, the find widget, unstyled decorations) light on a dark editor.\n */\nfunction builtinBase(rootEl?: HTMLElement | null): Monaco.editor.BuiltinTheme {\n return resolveThemeIsDark(rootEl) ? \"vs-dark\" : \"vs\";\n}\n\n/**\n * Build a Monaco theme from the resolved CSS variables on `rootEl`\n * (defaults to `<html>`, where `data-theme` lives).\n */\nexport function buildBrandThemeData(\n rootEl?: HTMLElement | null,\n): Monaco.editor.IStandaloneThemeData {\n const el = rootEl ?? (typeof document !== \"undefined\" ? document.documentElement : null);\n const read = (name: string, fallback?: string) =>\n resolveCssColor(el ? getComputedStyle(el).getPropertyValue(name) : \"\", fallback);\n\n const background = read(\"--background\", \"#ffffff\");\n const foreground = read(\"--foreground\", \"#000000\");\n const muted = read(\"--muted\", background);\n const mutedFg = read(\"--muted-foreground\", foreground);\n const border = read(\"--border\", muted);\n const primary = read(\"--primary\", foreground);\n const ring = read(\"--ring\", primary);\n const ringContour = read(\"--ring-contour\", ring);\n // Monaco's `focusBorder` is a SINGLE colour key — a theme cannot hand it the\n // two-layer compound indicator the DOM gets from the `focus-ring` utility (#67),\n // so it gets whichever layer actually clears the 1.4.11 bar against this editor's\n // own ground. That is exactly the `max(ring, contour)` rule\n // `themes-contrast.test.ts` asserts on the tokens, evaluated here at runtime so it\n // also holds for a consumer theme this package has never heard of: on `light` the\n // ring IS `--primary` (1.36:1) and the contour wins; on `dark` the contour\n // deliberately collapses into the background and the ring wins.\n const focusBorder =\n contrast(ringContour, background) > contrast(ring, background) ? ringContour : ring;\n const popover = read(\"--popover\", background);\n const popoverFg = read(\"--popover-foreground\", foreground);\n const input = read(\"--input\", border);\n const chart1 = read(\"--chart-1\", primary);\n const chart2 = read(\"--chart-2\", primary);\n const chart3 = read(\"--chart-3\", primary);\n const chart4 = read(\"--chart-4\", primary);\n const success = read(\"--success\", chart2);\n const destructive = read(\"--destructive\", \"#ff0000\");\n\n // Monaco paints `editor.lineHighlightBackground` — a translucent overlay —\n // UNDER every token's text on the CURSOR'S line, so the real, on-screen\n // ground a syntax color renders against there is this COMPOSITE, not the\n // bare `background` alone (#88). Computed once and reused for both the\n // `colors` entry below and the AA-clamp ground, so the two can't drift apart.\n const lineHighlight = withAlpha(foreground, LINE_HIGHLIGHT_ALPHA);\n const tokenGround = flattenOver(lineHighlight, background);\n\n // Calc result-inlay color (#220): the computed answer shown after each ```calc\n // line. Themed here (not hardcoded) so it re-applies on theme change with the\n // rest of the editor; AA-clamped against the composited line-highlight ground\n // like syntax tokens (#88) — an inlay on the cursor's line is painted over the\n // same overlay.\n const calcResult = ensureReadable(read(\"--calc-result\", primary), tokenGround, 4.5);\n\n const colors: Monaco.editor.IColors = {\n \"editor.background\": background,\n \"editor.foreground\": foreground,\n \"editorGutter.background\": background,\n \"editorLineNumber.foreground\": withAlpha(mutedFg, 0.6),\n \"editorLineNumber.activeForeground\": foreground,\n \"editorCursor.foreground\": primary,\n \"editor.selectionBackground\": withAlpha(primary, 0.28),\n \"editor.inactiveSelectionBackground\": withAlpha(primary, 0.14),\n \"editor.selectionHighlightBackground\": withAlpha(primary, 0.14),\n \"editor.lineHighlightBackground\": lineHighlight,\n \"editor.lineHighlightBorder\": \"#00000000\",\n \"editorIndentGuide.background1\": withAlpha(border, 0.6),\n \"editorIndentGuide.activeBackground1\": mutedFg,\n \"editorWhitespace.foreground\": withAlpha(mutedFg, 0.35),\n \"editorBracketMatch.background\": withAlpha(primary, 0.2),\n \"editorBracketMatch.border\": withAlpha(primary, 0.45),\n // Widgets — this is what makes Monaco's \"built-in components\" match brand-ui.\n // A shadow lets the popover detach from the editor even on light themes where\n // the popover surface and editor background are nearly identical.\n \"widget.shadow\": \"#0000002e\",\n \"editorWidget.background\": popover,\n \"editorWidget.foreground\": popoverFg,\n \"editorWidget.border\": border,\n \"editorHoverWidget.background\": popover,\n \"editorHoverWidget.foreground\": popoverFg,\n \"editorHoverWidget.border\": border,\n \"editorSuggestWidget.background\": popover,\n \"editorSuggestWidget.foreground\": popoverFg,\n \"editorSuggestWidget.border\": border,\n \"editorSuggestWidget.selectedBackground\": withAlpha(primary, 0.18),\n \"editorSuggestWidget.selectedForeground\": popoverFg,\n \"editorSuggestWidget.highlightForeground\": primary,\n \"input.background\": input,\n \"input.foreground\": foreground,\n \"input.border\": border,\n focusBorder,\n \"dropdown.background\": popover,\n \"dropdown.foreground\": popoverFg,\n \"dropdown.border\": border,\n \"list.hoverBackground\": withAlpha(mutedFg, 0.12),\n \"list.focusBackground\": withAlpha(primary, 0.16),\n // Context menu (Monaco's built-in, used when contextMenu=\"monaco\").\n \"menu.background\": popover,\n \"menu.foreground\": popoverFg,\n \"menu.border\": border,\n \"menu.selectionBackground\": withAlpha(primary, 0.18),\n \"menu.selectionForeground\": popoverFg,\n \"menu.separatorBackground\": withAlpha(border, 0.8),\n \"scrollbarSlider.background\": withAlpha(mutedFg, 0.2),\n \"scrollbarSlider.hoverBackground\": withAlpha(mutedFg, 0.35),\n \"scrollbarSlider.activeBackground\": withAlpha(mutedFg, 0.5),\n // Minimap (off by default; themed for when it's enabled via `options`).\n \"minimap.background\": background,\n \"minimapSlider.background\": withAlpha(mutedFg, 0.18),\n \"minimapSlider.hoverBackground\": withAlpha(mutedFg, 0.3),\n \"minimapSlider.activeBackground\": withAlpha(mutedFg, 0.45),\n \"editorError.foreground\": destructive,\n \"editorWarning.foreground\": read(\"--warning\", chart4),\n // Inlay hints (#220 calc result inlays) — calm, legible, themed from the calc\n // result token; transparent plate so it reads as an annotation, not a chip.\n \"editorInlayHint.foreground\": calcResult,\n \"editorInlayHint.background\": \"#00000000\",\n \"editorInlayHint.typeForeground\": calcResult,\n \"editorInlayHint.parameterForeground\": calcResult,\n // Diff editor — brand the add/remove bands from success/destructive tokens\n // (instead of Monaco's default green/red) at low alpha so syntax reads on top.\n \"diffEditor.insertedTextBackground\": withAlpha(success, 0.16),\n \"diffEditor.removedTextBackground\": withAlpha(destructive, 0.16),\n \"diffEditor.insertedLineBackground\": withAlpha(success, 0.08),\n \"diffEditor.removedLineBackground\": withAlpha(destructive, 0.08),\n \"diffEditorGutter.insertedLineBackground\": withAlpha(success, 0.12),\n \"diffEditorGutter.removedLineBackground\": withAlpha(destructive, 0.12),\n \"diffEditorOverview.insertedForeground\": withAlpha(success, 0.6),\n \"diffEditorOverview.removedForeground\": withAlpha(destructive, 0.6),\n \"diffEditor.border\": border,\n };\n\n // Syntax tokens: enforce AA (4.5:1) against the composited line-highlight\n // ground (#88 — Monaco paints that translucent overlay UNDER every token on\n // the cursor's line, so clamping against the bare `background` targets a\n // ground that is never actually rendered); comments get a softer 3.2:1 so\n // they stay intentionally muted but legible. Keyword/operator/tag stay on\n // the brand primary (identity), readability-clamped too.\n //\n // `AA_MARGIN` adds headroom on top of the nominal ratio: `ensureReadable`\n // stops at the FIRST 10% mix step that clears the bar, so a zero-margin\n // clamp can land a hair below it once axe's own rounding is applied — #88\n // measured `string` short by 0.34:1 for exactly this reason. Modeling the\n // OTHER transient overlays (selection, bracket-match, diff bands) is\n // explicitly out of scope for #88; the margin is the accepted headroom for\n // those too, not a claim they're individually composited in.\n const AA_MARGIN = 0.15;\n const ink = (hex: string, ratio = 4.5) =>\n bare(ensureReadable(hex, tokenGround, ratio + AA_MARGIN));\n const rules: Monaco.editor.ITokenThemeRule[] = [\n { token: \"\", foreground: bare(foreground), background: bare(background) },\n { token: \"comment\", foreground: ink(mutedFg, 3.2), fontStyle: \"italic\" },\n { token: \"keyword\", foreground: ink(primary) },\n { token: \"operator\", foreground: ink(primary) },\n { token: \"string\", foreground: ink(chart2) },\n { token: \"number\", foreground: ink(chart4) },\n { token: \"regexp\", foreground: ink(chart4) },\n { token: \"constant\", foreground: ink(chart4) },\n { token: \"type\", foreground: ink(chart1) },\n { token: \"type.identifier\", foreground: ink(chart1) },\n { token: \"function\", foreground: ink(chart3) },\n { token: \"identifier\", foreground: bare(foreground) },\n { token: \"variable\", foreground: bare(foreground) },\n { token: \"variable.predefined\", foreground: ink(chart3) },\n { token: \"delimiter\", foreground: ink(mutedFg, 3.2) },\n { token: \"tag\", foreground: ink(primary) },\n { token: \"attribute.name\", foreground: ink(chart3) },\n { token: \"attribute.value\", foreground: ink(chart2) },\n { token: \"key\", foreground: ink(chart1) }, // JSON keys\n { token: \"string.key\", foreground: ink(chart1) },\n { token: \"string.value\", foreground: ink(chart2) },\n { token: \"invalid\", foreground: ink(destructive) },\n { token: \"namespace\", foreground: ink(success) },\n // Monaco's built-in `vs`/`vs-dark` bases (inherited at `inherit: true`,\n // below) ship LANGUAGE-SUFFIXED rules — `string.key.json`,\n // `string.value.json`, `keyword.json`, `string.yaml`, `delimiter.html`, …\n // — and Monaco's token-theme trie resolves the DEEPEST matching scope\n // (`ThemeTrieElement.match`), with rules sorted lexicographically before\n // insertion. A shorter brand scope (e.g. `string.key`, above) can\n // therefore NEVER override a longer base scope: every scope a base theme\n // specialises must be re-declared here, or that language renders in\n // stock VS colours (#90). Keep this list in sync with the base themes —\n // `IGNORED_BASE_SCOPES` + the drift guard in `monaco-theme-bridge.test.ts`\n // fail CI if a future `monaco-editor` upgrade adds a new one.\n { token: \"string.key.json\", foreground: ink(chart1) }, // pairs with `key`\n { token: \"string.value.json\", foreground: ink(chart2) }, // pairs with `string`\n { token: \"keyword.json\", foreground: ink(primary) }, // pairs with `keyword`\n // Closes the class, not just JSON — the same base-specialisation gap\n // reaches YAML/HTML/SQL/XML/CSS/SCSS (#90 evidence #7).\n { token: \"string.html\", foreground: ink(chart2) },\n { token: \"string.sql\", foreground: ink(chart2) },\n { token: \"string.yaml\", foreground: ink(chart2) },\n { token: \"delimiter.html\", foreground: ink(mutedFg, 3.2) },\n { token: \"delimiter.xml\", foreground: ink(mutedFg, 3.2) },\n { token: \"attribute.value.html\", foreground: ink(chart2) },\n { token: \"attribute.value.xml\", foreground: ink(chart2) },\n { token: \"attribute.value.number\", foreground: ink(chart4) },\n { token: \"attribute.value.unit\", foreground: ink(chart4) },\n { token: \"attribute.value.number.css\", foreground: ink(chart4) },\n { token: \"attribute.value.unit.css\", foreground: ink(chart4) },\n { token: \"attribute.value.hex.css\", foreground: ink(chart4) },\n { token: \"number.hex\", foreground: ink(chart4) },\n { token: \"keyword.flow\", foreground: ink(primary) },\n { token: \"keyword.flow.scss\", foreground: ink(primary) },\n { token: \"operator.scss\", foreground: ink(primary) },\n { token: \"operator.sql\", foreground: ink(primary) },\n { token: \"operator.swift\", foreground: ink(primary) },\n { token: \"predefined.sql\", foreground: ink(chart3) },\n { token: \"metatag\", foreground: ink(chart3) },\n { token: \"metatag.html\", foreground: ink(chart3) },\n { token: \"metatag.xml\", foreground: ink(chart3) },\n { token: \"metatag.content.html\", foreground: ink(chart3) },\n { token: \"meta.scss\", foreground: ink(chart1) },\n { token: \"meta.tag\", foreground: ink(chart1) },\n // `CodeEditorProps.language` is a plain, unrestricted `string` passed\n // straight to `monaco.editor.setModelLanguage` (`code-editor.tsx`) — NOT\n // limited to `EDITOR_LANGUAGES` — so a consumer really can reach these\n // pug/handlebars scopes (PR #119 review thread 2). See\n // `IGNORED_BASE_SCOPES` below for the one scope that stays un-overridden.\n { token: \"tag.id.pug\", foreground: ink(primary) }, // pairs with `tag`\n { token: \"tag.class.pug\", foreground: ink(primary) },\n { token: \"variable.parameter\", foreground: bare(foreground) }, // pairs with `variable`\n ];\n\n // `base` is a placeholder; `applyBrandTheme` overrides it per theme.\n return { base: \"vs\", inherit: true, colors, rules };\n}\n\n/**\n * Base-specialised dotted scopes (`vs`/`vs_dark`,\n * `monaco-editor/esm/vs/editor/standalone/common/themes.js`) deliberately left\n * un-overridden by `buildBrandThemeData`'s `rules`. Read by the drift-guard\n * test (`monaco-theme-bridge.test.ts`, #90) so a future `monaco-editor`\n * upgrade that adds a genuinely new specialised scope fails CI instead of\n * silently un-branding it.\n *\n * PR #119 review thread 2 (fix-round-2): this set used to also carry\n * `tag.id.pug` / `tag.class.pug` / `variable.parameter` on the premise that\n * \"the language that emits them is NOT in `EDITOR_LANGUAGES`, so nothing in\n * this package can ever render them\" — that premise is FALSE.\n * `CodeEditorProps.language` (`code-editor.tsx`) is a plain, unrestricted\n * `string` forwarded straight to `monaco.editor.setModelLanguage`; the\n * toolbar's `EDITOR_LANGUAGES` list is a curated picker UI, not an\n * enforcement boundary. A consumer passing `language=\"pug\"` or\n * `language=\"handlebars\"` genuinely reaches those scopes and would have\n * inherited stock Monaco colours instead of the token-derived brand theme.\n * They are now branded in `rules` above instead of ignored here.\n *\n * - `metatag.php` — the one scope legitimately still ignored: verified\n * against `monaco-editor/esm/vs/editor/standalone/common/themes.js`, the\n * base themes give it only a `fontStyle` (`bold`), no `foreground` at all\n * — there is no colour to override, so branding it would be a no-op rule\n * with nothing to test.\n *\n * If a future scope needs the same \"unreachable\" reasoning, verify it\n * against `CodeEditorProps.language`'s actual (unrestricted) type before\n * adding it here — not against `EDITOR_LANGUAGES`.\n */\nexport const IGNORED_BASE_SCOPES = new Set([\"metatag.php\"]);\n\n/** The Monaco theme id used for a given brand theme. */\nexport function brandThemeId(theme: ThemeName): string {\n return `brand-${theme}`;\n}\n\n/**\n * Define + activate the Monaco theme for `theme`, reading live token values.\n * `setTheme` is global to all editors, so calling this from any mounted editor\n * is idempotent and keeps every editor in sync.\n */\nexport function applyBrandTheme(\n monaco: typeof Monaco,\n theme: ThemeName,\n rootEl?: HTMLElement | null,\n): string {\n const id = brandThemeId(theme);\n const data = buildBrandThemeData(rootEl);\n data.base = builtinBase(rootEl);\n monaco.editor.defineTheme(id, data);\n monaco.editor.setTheme(id);\n return id;\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { DEFAULT_THEME, type ThemeName } from \"@elabs-ai/components-tokens\";\n\nexport interface DataThemeState {\n /** The active brand theme parsed from `data-theme`. */\n theme: ThemeName;\n /**\n * Increments on every observed `data-theme` mutation (and the initial read),\n * even when the parsed theme name is unchanged. Consumers depend on this to\n * re-run side effects once the attribute settles — e.g. the editor re-applies\n * its Monaco theme after a late attribute write that matches the default,\n * which a name-only dependency would miss.\n */\n revision: number;\n}\n\n/**\n * Reads the active brand theme straight from the `data-theme` attribute and\n * keeps it in sync via a `MutationObserver`. Deliberately decoupled from\n * `ThemeProvider`/`useTheme` so the editor themes correctly whether or not a\n * provider is present (Storybook's theme decorator just sets `data-theme`).\n *\n * @param target Element to watch. Defaults to `<html>` (where `ThemeProvider`\n * writes). Pass a scoped element for nested theming; the document root is also\n * observed as a fallback.\n */\nexport function useDataTheme(target?: HTMLElement | null): DataThemeState {\n const [state, setState] = useState<DataThemeState>({ theme: DEFAULT_THEME, revision: 0 });\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const el = target ?? document.documentElement;\n\n const read = () => {\n const next =\n el.getAttribute(\"data-theme\") ?? document.documentElement.getAttribute(\"data-theme\");\n setState((prev) => ({\n // Any non-empty attribute value is a theme (ADR 0029 — names are open).\n // Only \"no attribute anywhere\" keeps the previous value, which is what\n // makes a late ThemeProvider write a no-op rather than a flash.\n theme: next || prev.theme,\n revision: prev.revision + 1,\n }));\n };\n\n read();\n const observer = new MutationObserver(read);\n observer.observe(el, { attributes: true, attributeFilter: [\"data-theme\"] });\n if (el !== document.documentElement) {\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\"],\n });\n }\n return () => observer.disconnect();\n }, [target]);\n\n return state;\n}\n","\"use client\";\n\n// Type-only: the barrel (`.`) exports this component alongside lightweight\n// chrome (`CopyButton`, `EDITOR_LANGUAGES`) that must stay import-safe without\n// Monaco. A `monaco-editor` VALUE import here would be evaluated the moment\n// anything imports the barrel, pulling megabytes of Monaco + touching browser\n// globals even for a consumer that only wants `CopyButton`. The engine is\n// loaded at RUNTIME via `import(\"monaco-editor\")` inside the mount effect\n// below — see `monacoRef`.\nimport type * as monaco from \"monaco-editor\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n} from \"react\";\nimport { EditorContextMenu } from \"../editor-context-menu\";\nimport { applyBrandTheme } from \"../lib/monaco-theme-bridge\";\nimport { useDataTheme } from \"../lib/use-data-theme\";\n\nexport type MonacoCodeEditor = monaco.editor.IStandaloneCodeEditor;\n\n/** A Monaco editor action (system command + optional hotkey + palette/menu entry). */\nexport type EditorAction = monaco.editor.IActionDescriptor;\n\n/**\n * The runtime value type handed back by `import(\"monaco-editor\")` — same\n * namespace as the type-only `monaco` import above (`typeof` a type-only\n * namespace import resolves to the module's own type), used for `monacoRef`\n * and `onMount`'s second argument.\n */\ntype MonacoNamespace = typeof monaco;\n\nexport interface CodeEditorProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Controlled content. Pair with `onChange`. */\n value?: string;\n /** Initial content for uncontrolled use. */\n defaultValue?: string;\n /** Fires on every edit with the full document text. */\n onChange?: (value: string) => void;\n /** Monaco language id (e.g. \"typescript\", \"json\"). Defaults to \"typescript\". */\n language?: string;\n /** Model path/URI — drives per-file language services + diagnostics. */\n path?: string;\n /** Render the editor read-only. */\n readOnly?: boolean;\n /** Editor height. Number → px. Defaults to \"100%\" (size via the parent). */\n height?: number | string;\n /** Passthrough Monaco construction options (merged over the defaults). */\n options?: monaco.editor.IStandaloneEditorConstructionOptions;\n /**\n * Accessible name for the editor. Maps onto Monaco's `ariaLabel` construction\n * option AND the inner screen-reader `<textarea>` (`aria-label`), so assistive\n * tech announces a name. Spreading `aria-label` via `...props` only lands on the\n * wrapper div and never reaches Monaco's focusable textarea — use this instead.\n */\n ariaLabel?: string;\n /** Sets `aria-invalid` on Monaco's inner `<textarea>` to convey validity. */\n ariaInvalid?: boolean;\n /** Sets `aria-describedby` on Monaco's inner `<textarea>` (e.g. an error id). */\n ariaDescribedBy?: string;\n /**\n * Right-click menu. `\"brand\"` (default) replaces Monaco's built-in menu with\n * brand-ui's `ContextMenu`; `\"monaco\"` keeps Monaco's themed menu; `\"none\"`\n * disables it.\n */\n contextMenu?: \"brand\" | \"monaco\" | \"none\";\n /** Called once the editor instance + monaco namespace are ready. */\n onMount?: (editor: MonacoCodeEditor, monacoApi: MonacoNamespace) => void;\n /**\n * Declarative Monaco editor actions — each registers a command (run on its\n * `keybindings`, in the command palette, and optionally the context menu via\n * `contextMenuGroupId`). Wraps `editor.addAction`; re-registered when the array\n * identity changes, disposed on unmount. Build keybindings with the re-exported\n * `monaco` namespace, e.g. `keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Slash]`.\n *\n * Note: a context-menu entry only shows when `contextMenu=\"monaco\"`, but the\n * keybinding and command palette work regardless. Memoize `actions` to avoid\n * re-registering on every render.\n */\n actions?: EditorAction[];\n}\n\n/**\n * The smallest single edit that turns `oldValue` into `newValue`, found by\n * trimming the common prefix and suffix. Used instead of a wholesale\n * `editor.setValue()` when syncing a controlled `value`: `setValue` replaces\n * the entire model in one shot, which wipes the undo stack and always resets\n * the cursor to the start of the document. Routing the same change through\n * `editor.executeEdits` with just the differing middle span keeps it a single\n * coalescable undo entry and leaves the cursor/selection outside the edited\n * span untouched by Monaco's own position mapping.\n */\nfunction computeMinimalEdit(\n oldValue: string,\n newValue: string,\n): { start: number; endOld: number; text: string } {\n const maxCommon = Math.min(oldValue.length, newValue.length);\n let start = 0;\n while (start < maxCommon && oldValue.charCodeAt(start) === newValue.charCodeAt(start)) {\n start++;\n }\n let endOld = oldValue.length;\n let endNew = newValue.length;\n while (\n endOld > start &&\n endNew > start &&\n oldValue.charCodeAt(endOld - 1) === newValue.charCodeAt(endNew - 1)\n ) {\n endOld--;\n endNew--;\n }\n return { start, endOld, text: newValue.slice(start, endNew) };\n}\n\nconst BASE_OPTIONS: monaco.editor.IStandaloneEditorConstructionOptions = {\n automaticLayout: true,\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n smoothScrolling: true,\n fontLigatures: true,\n fontSize: 13,\n lineNumbersMinChars: 3,\n padding: { top: 12, bottom: 12 },\n scrollbar: { verticalScrollbarSize: 10, horizontalScrollbarSize: 10 },\n};\n\n/**\n * A token-themed Monaco editor wrapped as a brand-ui React component. Monaco\n * renders its own editing surface + widgets; this wrapper owns lifecycle,\n * controlled/uncontrolled value, and applies the brand theme bridge so the\n * editor matches the active `data-theme` (every theme).\n *\n * Workers (for completions/diagnostics) are wired by importing\n * `@elabs-ai/components-editor/monaco-environment` once at the app entry.\n */\nexport const CodeEditor = forwardRef<MonacoCodeEditor | null, CodeEditorProps>(function CodeEditor(\n {\n value,\n defaultValue,\n onChange,\n language = \"typescript\",\n path,\n readOnly = false,\n height = \"100%\",\n options,\n ariaLabel,\n ariaInvalid,\n ariaDescribedBy,\n contextMenu = \"brand\",\n onMount,\n actions,\n className,\n style,\n ...props\n },\n ref,\n) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [editor, setEditor] = useState<MonacoCodeEditor | null>(null);\n const { theme, revision } = useDataTheme();\n // The CURRENT model, tracked outside React state: a `path` change swaps it\n // (see the effect below) without waiting on a re-render, and unmount must\n // dispose whichever model is live at that point, not the one from mount.\n const modelRef = useRef<monaco.editor.ITextModel | null>(null);\n // The dynamically-imported `monaco-editor` module, once loaded. `editor`\n // (React state) is only ever set AFTER this ref is populated (see the mount\n // effect), so every other effect below that reads both may assume: `editor`\n // truthy implies `monacoRef.current` truthy.\n const monacoRef = useRef<MonacoNamespace | null>(null);\n\n // Latest callbacks via refs so the mount effect can run exactly once.\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n const onMountRef = useRef(onMount);\n onMountRef.current = onMount;\n\n useImperativeHandle<MonacoCodeEditor | null, MonacoCodeEditor | null>(ref, () => editor, [\n editor,\n ]);\n\n // Mount once. Monaco itself loads lazily (`import(\"monaco-editor\")`) so the\n // engine is only fetched/evaluated once a `CodeEditor` actually mounts, never\n // merely by importing this module (see the top-of-file note). `cancelled`\n // guards against the component unmounting (or `container` going away) before\n // the dynamic import resolves.\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n let cancelled = false;\n let instance: MonacoCodeEditor | null = null;\n let model: monaco.editor.ITextModel | null = null;\n let sub: { dispose(): void } | null = null;\n\n import(\"monaco-editor\").then((monacoApi) => {\n if (cancelled) return;\n monacoRef.current = monacoApi;\n model = monacoApi.editor.createModel(\n value ?? defaultValue ?? \"\",\n language,\n path ? monacoApi.Uri.parse(`inmemory://brand/${path}`) : undefined,\n );\n modelRef.current = model;\n instance = monacoApi.editor.create(container, {\n ...BASE_OPTIONS,\n readOnly,\n // Disable Monaco's own menu unless explicitly opted into; \"brand\" renders\n // brand-ui's ContextMenu around the editor instead.\n contextmenu: contextMenu === \"monaco\",\n // Monaco's accessible name comes from this construction option (it writes it\n // onto its inner screen-reader <textarea>), not from a wrapper-div attribute.\n ...(ariaLabel !== undefined ? { ariaLabel } : null),\n model,\n ...options,\n });\n sub = instance.onDidChangeModelContent(() => {\n onChangeRef.current?.(instance!.getValue());\n });\n // Monaco now mounts asynchronously, so stamp the initial aria-* onto its\n // textarea right away — otherwise it is briefly exposed without a name\n // until the aria sync effect below runs on the next commit.\n const textarea = instance.getDomNode()?.querySelector(\"textarea\");\n if (textarea) {\n if (ariaLabel !== undefined) textarea.setAttribute(\"aria-label\", ariaLabel);\n if (ariaInvalid !== undefined) textarea.setAttribute(\"aria-invalid\", String(ariaInvalid));\n if (ariaDescribedBy !== undefined)\n textarea.setAttribute(\"aria-describedby\", ariaDescribedBy);\n }\n // `setEditor` triggers the theming effect below; keeping theme application\n // there (not here) guarantees it never blocks editor setup.\n setEditor(instance);\n onMountRef.current?.(instance, monacoApi);\n });\n\n return () => {\n cancelled = true;\n sub?.dispose();\n instance?.dispose();\n model?.dispose();\n modelRef.current = null;\n monacoRef.current = null;\n setEditor(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // `path` drives the model's URI, which Monaco never lets you change on an\n // existing model — a later `path` change (e.g. `CodeWorkspace` switching\n // files) was previously just ignored. Swap in a fresh model carrying the\n // CURRENT value/language under the new URI, and dispose the old one; a\n // per-file undo stack is Monaco's normal behavior for a model swap.\n useEffect(() => {\n const monacoApi = monacoRef.current;\n if (!editor || !monacoApi) return;\n const current = modelRef.current;\n const currentUri = current?.uri?.toString();\n const nextUri = path ? monacoApi.Uri.parse(`inmemory://brand/${path}`).toString() : undefined;\n if (currentUri === nextUri) return;\n\n const nextModel = monacoApi.editor.createModel(\n current?.getValue() ?? value ?? defaultValue ?? \"\",\n language,\n path ? monacoApi.Uri.parse(`inmemory://brand/${path}`) : undefined,\n );\n editor.setModel(nextModel);\n modelRef.current = nextModel;\n current?.dispose();\n // `value`/`defaultValue`/`language` are read once, at the moment of the\n // swap, to seed the new model — not tracked as reactive deps here; the\n // controlled-value and language effects below correct them independently.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [editor, path]);\n\n // Controlled value sync — only when it diverges, and via the smallest\n // `executeEdits` span rather than `setValue()`: a full-document `setValue`\n // wipes the undo stack and always resets the cursor to the start.\n useEffect(() => {\n const monacoApi = monacoRef.current;\n if (!editor || !monacoApi || value === undefined) return;\n const model = editor.getModel();\n if (!model) return;\n const current = model.getValue();\n if (value === current) return;\n const { start, endOld, text } = computeMinimalEdit(current, value);\n const range = monacoApi.Range.fromPositions(\n model.getPositionAt(start),\n model.getPositionAt(endOld),\n );\n editor.executeEdits(\"controlled-value-sync\", [{ range, text }]);\n }, [editor, value]);\n\n useEffect(() => {\n const monacoApi = monacoRef.current;\n const model = editor?.getModel();\n if (model && monacoApi) monacoApi.editor.setModelLanguage(model, language);\n }, [editor, language]);\n\n useEffect(() => {\n editor?.updateOptions({ readOnly, contextmenu: contextMenu === \"monaco\" });\n }, [editor, readOnly, contextMenu]);\n\n // `options` after mount: the construction-time spread only ever applied it\n // once. A caller changing `options` (e.g. toggling `minimap`) now reaches\n // the live editor via `updateOptions`, same as `readOnly`/`contextMenu` above.\n useEffect(() => {\n if (!editor || !options) return;\n editor.updateOptions(options);\n }, [editor, options]);\n\n useEffect(() => {\n const monacoApi = monacoRef.current;\n if (!editor || !monacoApi) return;\n try {\n applyBrandTheme(monacoApi, theme);\n } catch (err) {\n console.error(\"[@elabs-ai/components-editor] failed to apply brand theme\", err);\n }\n // `revision` forces a re-apply after the data-theme attribute settles (even\n // when the parsed theme name equals the default), so tokens are re-read.\n }, [editor, theme, revision]);\n\n // Forward accessibility attributes onto Monaco's focusable screen-reader\n // <textarea> (its real interactive surface), kept in sync as props change.\n // `ariaLabel` flows through Monaco's option, BUT Monaco only writes it onto the\n // textarea when accessibilitySupport isn't \"off\" (auto-detection can disable it,\n // e.g. headless). So we also set aria-label directly on the textarea — belt and\n // suspenders — alongside aria-invalid/aria-describedby (which aren't Monaco options).\n useEffect(() => {\n if (!editor) return;\n if (ariaLabel !== undefined) editor.updateOptions({ ariaLabel });\n const textarea = editor.getDomNode()?.querySelector(\"textarea\");\n if (!textarea) return;\n if (ariaLabel === undefined) textarea.removeAttribute(\"aria-label\");\n else textarea.setAttribute(\"aria-label\", ariaLabel);\n if (ariaInvalid === undefined) textarea.removeAttribute(\"aria-invalid\");\n else textarea.setAttribute(\"aria-invalid\", String(ariaInvalid));\n if (ariaDescribedBy === undefined) textarea.removeAttribute(\"aria-describedby\");\n else textarea.setAttribute(\"aria-describedby\", ariaDescribedBy);\n }, [editor, ariaLabel, ariaInvalid, ariaDescribedBy]);\n\n // Declarative actions: register each on the editor instance and dispose on\n // array-identity change or unmount. Re-registering when the array reference\n // changes is intentional and correct (unlike onChange, a re-register is cheap\n // and desired). Consumers should memoize `actions` to avoid unnecessary churn.\n useEffect(() => {\n if (!editor || !actions || actions.length === 0) return;\n const disposables = actions.map((a) => editor.addAction(a));\n return () => disposables.forEach((d) => d.dispose());\n }, [editor, actions]);\n\n const resolvedStyle: CSSProperties = {\n height: typeof height === \"number\" ? `${height}px` : height,\n ...style,\n };\n\n const editorEl = (\n <div\n ref={containerRef}\n data-testid=\"code-editor\"\n className={cn(\"h-full w-full overflow-hidden bg-background text-foreground\", className)}\n style={resolvedStyle}\n {...props}\n />\n );\n\n if (contextMenu === \"brand\") {\n return (\n <EditorContextMenu editor={editor} readOnly={readOnly}>\n {editorEl}\n </EditorContextMenu>\n );\n }\n return editorEl;\n});\n","/**\n * Shared, engine-agnostic helpers for the calc AUTHORING surfaces (#220).\n *\n * Both editor surfaces — the Monaco source editor and the Milkdown WYSIWYG\n * editor — need the same things: find the ```calc fences, ask the consumer's\n * hooks for highlight tokens + results, and turn those into decorations. The\n * decoration *application* differs per engine (Monaco `inlineClassName` vs a\n * ProseMirror `DecorationSet`), but the *logic* here is pure and shared, so it is\n * unit-testable without rendering either engine (Monaco can't render in jsdom).\n *\n * Governing rule (D5): the library decorates, the consumer computes. Nothing here\n * does any math — it only calls the consumer-supplied {@link CalcEditorHooks} and\n * shapes the results. A throwing hook degrades to \"no decorations\" rather than\n * blanking the editor (mirrors how `CalcBlock` never throws on a bad block).\n */\nimport type {\n CalcContext,\n CalcEditorHooks,\n CalcLineResult,\n CalcSheet,\n CalcToken,\n CalcTokenKind,\n} from \"./types\";\n\n/** The fenced-code-block info string this feature decorates. */\nexport const CALC_FENCE_LANG = \"calc\";\n\n/** A located ```calc fence within a markdown document. Lines are 1-based. */\nexport interface CalcFence {\n /** 1-based line of the opening fence (` ```calc `). */\n openLine: number;\n /** 1-based line of the closing fence, or `lines + 1` when unterminated. */\n closeLine: number;\n /** 1-based line of the first body line (`openLine + 1`). */\n bodyStartLine: number;\n /** 1-based line of the last body line (`closeLine - 1`); `< bodyStartLine` if empty. */\n bodyEndLine: number;\n /** The fence body (body lines joined by `\"\\n\"`). */\n source: string;\n}\n\nconst OPEN_FENCE = /^(\\s*)(`{3,}|~{3,})[ \\t]*calc\\b[ \\t]*$/i;\n\n/**\n * Scan a markdown string for ```calc fences. Tolerant of leading indentation,\n * `~~~` fences, and an unterminated trailing fence (treated as running to EOF) —\n * which is the normal state while the author is still typing the block.\n */\nexport function findCalcFences(text: string): CalcFence[] {\n const lines = text.split(\"\\n\");\n const fences: CalcFence[] = [];\n let i = 0;\n while (i < lines.length) {\n const open = OPEN_FENCE.exec(lines[i] ?? \"\");\n if (!open) {\n i++;\n continue;\n }\n const run = open[2] ?? \"```\";\n const marker = run[0] ?? \"`\";\n const closeRe = new RegExp(`^\\\\s*\\\\${marker}{${run.length},}\\\\s*$`);\n let j = i + 1;\n while (j < lines.length && !closeRe.test(lines[j] ?? \"\")) j++;\n const openLine = i + 1;\n const closeLine = j < lines.length ? j + 1 : lines.length + 1;\n fences.push({\n openLine,\n closeLine,\n bodyStartLine: openLine + 1,\n bodyEndLine: closeLine - 1,\n source: lines.slice(i + 1, j).join(\"\\n\"),\n });\n i = j + 1;\n }\n return fences;\n}\n\n/** One body line's text plus its character offset from the fence-body start. */\nexport interface CalcLineSpan {\n text: string;\n /** Character offset of this line's first char within `source` (newlines counted). */\n offset: number;\n}\n\n/** Lay out a fence body into per-line `{ text, offset }` — used to map columns to positions. */\nexport function calcLineLayout(source: string): CalcLineSpan[] {\n const spans: CalcLineSpan[] = [];\n let offset = 0;\n for (const text of source.split(\"\\n\")) {\n spans.push({ text, offset });\n offset += text.length + 1; // + the newline\n }\n return spans;\n}\n\n/** Evaluate `source` to a per-line result map (1-based line → result). Never throws. */\nexport function resolveResults(\n hooks: CalcEditorHooks,\n source: string,\n ctx?: CalcContext,\n): Map<number, CalcLineResult> {\n const map = new Map<number, CalcLineResult>();\n if (!hooks.evaluate) return map;\n let sheet: CalcSheet;\n try {\n sheet = hooks.evaluate(source, ctx);\n } catch {\n return map;\n }\n for (const r of sheet.results ?? []) map.set(r.line, r);\n return map;\n}\n\n/**\n * Resolve highlight tokens for every body line. Prefers the per-line `tokenize`\n * hook (cheap, works mid-typing on a line that doesn't yet evaluate); falls back\n * to `evaluate`'s per-line `tokens` so a consumer who only wrote `evaluate` still\n * gets highlighting. Returns one `CalcToken[]` per line. Never throws.\n */\nexport function resolveHighlight(\n hooks: CalcEditorHooks,\n source: string,\n ctx?: CalcContext,\n): CalcToken[][] {\n const lines = source.split(\"\\n\");\n if (hooks.tokenize) {\n const { tokenize } = hooks;\n return lines.map((line) => {\n try {\n return tokenize(line, ctx) ?? [];\n } catch {\n return [];\n }\n });\n }\n if (hooks.evaluate) {\n const byLine = resolveResults(hooks, source, ctx);\n return lines.map((_line, i) => byLine.get(i + 1)?.tokens ?? []);\n }\n return lines.map(() => []);\n}\n\n/** A resolved result inlay for one body line (the computed answer shown after it). */\nexport interface CalcInlay {\n /** 1-based line within the fence body. */\n lineNumber: number;\n /** The inlay text — `\"= <display>\"` for a value, `\"error: …\"` for a per-line error. */\n text: string;\n /** True when the line evaluated to an error rather than a value. */\n isError: boolean;\n}\n\n/** The inlay text for one result line, or `null` when there's nothing to show. */\nexport function inlayTextForResult(result: CalcLineResult | undefined): {\n text: string;\n isError: boolean;\n} | null {\n if (!result) return null;\n if (result.error) return { text: `error: ${result.error.message}`, isError: true };\n if (result.value) return { text: `= ${result.value.display}`, isError: false };\n return null;\n}\n\n/** Resolve every body line's result inlay (skips lines with no value/error). */\nexport function resolveInlays(\n hooks: CalcEditorHooks,\n source: string,\n ctx?: CalcContext,\n): CalcInlay[] {\n const byLine = resolveResults(hooks, source, ctx);\n const out: CalcInlay[] = [];\n for (const [lineNumber, result] of byLine) {\n const inlay = inlayTextForResult(result);\n if (inlay) out.push({ lineNumber, text: inlay.text, isError: inlay.isError });\n }\n out.sort((a, b) => a.lineNumber - b.lineNumber);\n return out;\n}\n\n/**\n * Class string for a highlight token. Hue comes from the `--calc-*` tokens (via\n * `calc-editor.css`), but — exactly like `CalcBlock.tokenClass` — hue is never\n * the SOLE cue: a `var-def` carries weight and an unresolved token a dotted\n * underline, so roles stay distinct in the high-contrast theme (where the calc\n * colors collapse toward the foreground).\n */\nexport function calcTokenClassName(kind: CalcTokenKind, resolved: boolean): string {\n const base = `brand-calc-tok brand-calc-tok--${kind}`;\n return resolved ? base : `${base} brand-calc-tok--unresolved`;\n}\n\n/** The identifier prefix immediately before `column` in `line` (the word being typed). */\nexport function identifierPrefix(line: string, column: number): string {\n const upto = line.slice(0, Math.max(0, column));\n const m = /[A-Za-z_][A-Za-z0-9_]*$/.exec(upto);\n return m ? m[0] : \"\";\n}\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\n\n/**\n * An engine-neutral highlight span: a 1-based model line + 1-based start/end\n * columns (Monaco-style; `end` exclusive) + the CSS class. The Monaco layer maps\n * these to `monaco.Range` decorations; kept pure here so the column math is\n * unit-testable without rendering Monaco.\n */\nexport interface CalcDecorationSpec {\n lineNumber: number;\n startColumn: number;\n endColumn: number;\n className: string;\n}\n\n/**\n * Build highlight specs for one fence body. `bodyStartLine` is the 1-based model\n * line of the first body line; `bodyLineTexts` are the EOL-free line strings\n * (read from the model, so columns stay accurate regardless of CRLF/LF).\n */\nexport function calcDecorationSpecs(\n hooks: CalcEditorHooks,\n bodyStartLine: number,\n bodyLineTexts: string[],\n ctx?: CalcContext,\n): CalcDecorationSpec[] {\n const tokensByLine = resolveHighlight(hooks, bodyLineTexts.join(\"\\n\"), ctx);\n const specs: CalcDecorationSpec[] = [];\n for (let i = 0; i < bodyLineTexts.length; i++) {\n const lineText = bodyLineTexts[i] ?? \"\";\n for (const t of tokensByLine[i] ?? []) {\n const start = clamp(t.start, 0, lineText.length);\n const end = clamp(t.end, start, lineText.length);\n if (end <= start) continue;\n specs.push({\n lineNumber: bodyStartLine + i,\n startColumn: start + 1,\n endColumn: end + 1,\n className: calcTokenClassName(t.kind, t.resolved),\n });\n }\n }\n return specs;\n}\n\n/** An engine-neutral inlay placement: a 1-based model line + end-of-line column. */\nexport interface CalcInlaySpec {\n lineNumber: number;\n /** 1-based column at the end of the line (where the inlay sits). */\n column: number;\n text: string;\n isError: boolean;\n}\n\n/** Build result-inlay specs for one fence body (positioned at each line's end). */\nexport function calcInlaySpecs(\n hooks: CalcEditorHooks,\n bodyStartLine: number,\n bodyLineTexts: string[],\n ctx?: CalcContext,\n): CalcInlaySpec[] {\n return resolveInlays(hooks, bodyLineTexts.join(\"\\n\"), ctx).map((inlay) => {\n const i = inlay.lineNumber - 1;\n const lineText = bodyLineTexts[i] ?? \"\";\n return {\n lineNumber: bodyStartLine + i,\n column: lineText.length + 1,\n text: inlay.text,\n isError: inlay.isError,\n };\n });\n}\n","\"use client\";\n\n/**\n * Milkdown / ProseMirror calc layer (#220) — live highlighting + result inlays for\n * ```calc fences inside the WYSIWYG editor.\n *\n * A self-owned raw `$prose` plugin (zero new deps) that builds a `DecorationSet`\n * over every `code_block` whose language is `calc`:\n * - inline decorations color each token from the `--calc-*` tokens (calc-editor.css);\n * - a widget decoration after each line shows the computed result inlay.\n * The set is recomputed when the document changes. Hooks are read through a getter\n * so a fresh `calc` prop identity never rebuilds the editor (mirrors `slashConfigRef`).\n *\n * The position math reuses the engine-neutral helpers in `calc-editor.ts`; a\n * ProseMirror code block stores its body as plain text with literal `\\n`, so a\n * line's absolute position is `codeBlockContentStart + lineOffset + column`.\n */\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport type { Node as ProseNode } from \"@milkdown/kit/prose/model\";\nimport { Plugin, PluginKey } from \"@milkdown/kit/prose/state\";\nimport { Decoration, DecorationSet } from \"@milkdown/kit/prose/view\";\nimport { $prose } from \"@milkdown/kit/utils\";\n\nimport \"./calc-editor.css\";\n\nimport {\n CALC_FENCE_LANG,\n calcLineLayout,\n calcTokenClassName,\n resolveHighlight,\n resolveInlays,\n type CalcInlay,\n} from \"./calc-editor\";\nimport type { CalcEditorHooks } from \"./types\";\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\n\nconst calcDecorationKey = new PluginKey<DecorationSet>(\"brand-calc-decorations\");\n\n/**\n * Build the inline result-inlay widget DOM (non-editable, announced as text).\n *\n * a11y: the computed value is rendered as TEXT (never color-only) and is NOT\n * `aria-hidden`, so AT reads it in browse mode; a native `title` labels it for\n * mouse users. It is a ProseMirror decoration (not a model node), so a host that\n * must guarantee results to AT in active-editing/forms mode should also surface\n * them via a `role=\"status\"` live region outside the editor.\n */\nfunction inlayWidget(inlay: CalcInlay): HTMLElement {\n const span = document.createElement(\"span\");\n span.className = inlay.isError ? \"brand-calc-inlay brand-calc-inlay--error\" : \"brand-calc-inlay\";\n // The number itself is the signal (rendered as text); color is enhancement only.\n span.textContent = inlay.text;\n span.setAttribute(\"contenteditable\", \"false\");\n span.setAttribute(\"title\", inlay.isError ? \"Calc error\" : \"Calc result\");\n return span;\n}\n\n/** Walk the doc, decorating every ```calc code block with highlight + inlays. */\nfunction buildCalcDecorations(doc: ProseNode, hooks: CalcEditorHooks | undefined): DecorationSet {\n if (!hooks || (!hooks.tokenize && !hooks.evaluate)) return DecorationSet.empty;\n const decorations: Decoration[] = [];\n\n doc.descendants((node, pos) => {\n if (node.type.name !== \"code_block\" || node.attrs.language !== CALC_FENCE_LANG) {\n return undefined;\n }\n const source = node.textContent;\n const contentStart = pos + 1; // first char inside the code block\n const layout = calcLineLayout(source);\n const tokensByLine = resolveHighlight(hooks, source);\n\n layout.forEach((span, i) => {\n const lineStart = contentStart + span.offset;\n for (const t of tokensByLine[i] ?? []) {\n const start = clamp(t.start, 0, span.text.length);\n const end = clamp(t.end, start, span.text.length);\n if (end <= start) continue;\n decorations.push(\n Decoration.inline(lineStart + start, lineStart + end, {\n class: calcTokenClassName(t.kind, t.resolved),\n }),\n );\n }\n });\n\n for (const inlay of resolveInlays(hooks, source)) {\n const span = layout[inlay.lineNumber - 1];\n if (!span) continue;\n const at = contentStart + span.offset + span.text.length;\n decorations.push(\n Decoration.widget(at, () => inlayWidget(inlay), {\n side: 1,\n ignoreSelection: true,\n key: `brand-calc-inlay:${String(inlay.lineNumber)}:${inlay.text}`,\n }),\n );\n }\n return false; // a code block's content is plain text — don't descend\n });\n\n return DecorationSet.create(doc, decorations);\n}\n\n/**\n * Build the calc decoration plugin. `getHooks` is read on every recompute so the\n * editor never rebuilds when the `calc` prop identity changes. Returns a Milkdown\n * plugin array to `.use()` (only when calc authoring is enabled).\n */\nexport function calcProsePlugins(getHooks: () => CalcEditorHooks | undefined): MilkdownPlugin[] {\n const plugin = $prose(\n () =>\n new Plugin<DecorationSet>({\n key: calcDecorationKey,\n state: {\n init: (_config, state) => buildCalcDecorations(state.doc, getHooks()),\n apply: (tr, value, _old, newState) =>\n tr.docChanged ? buildCalcDecorations(newState.doc, getHooks()) : value,\n },\n props: {\n decorations(state) {\n return calcDecorationKey.getState(state);\n },\n },\n }),\n );\n return [plugin as unknown as MilkdownPlugin];\n}\n","/**\n * Engine-agnostic completion-provider contract (#283) — the `completions` prop\n * on `MarkdownWorkspace` (and, via it, the WYSIWYG `MarkdownEditor`).\n *\n * ISOLATION INVARIANT (mirrors `editor-content-access.ts`): this file imports\n * NOTHING engine-specific — no `monaco-editor`, no `@milkdown`. It is the\n * dependency-light leaf both engine adapters (`editor-completions-monaco.ts` for\n * Monaco, `markdown-editor/completions/` for Milkdown/ProseMirror) build on, so a\n * consumer can import the TYPES with zero engine imports (#283 acceptance: \"zero\n * `monaco-editor` imports in app code\").\n *\n * D5 / the calc `complete` hook precedent: the library only REGISTERS the\n * provider and RENDERS its suggestions — candidate list, filtering/ranking, and\n * insert text are entirely consumer-owned. Nothing here fetches anything; a\n * throwing/rejecting `provide()` degrades to \"no suggestions\" (never blanks or\n * throws), the same degrade-safely contract as `resolveResults`/`CalcBlock`.\n */\n\n/** One completion candidate a provider offers at the caret. */\nexport interface EditorCompletionItem {\n /** Text shown in the suggestion list. */\n label: string;\n /** Text inserted when the item is chosen. */\n insertText: string;\n /** Secondary detail shown muted beside the label. */\n detail?: string;\n /**\n * Start column (1-based, Monaco convention) of the already-typed token to\n * replace. Defaults to the start of the \"trigger query\" — the run of text\n * typed since the provider's trigger character (see {@link triggerQueryStart}).\n */\n replaceFrom?: number;\n}\n\n/** What a provider knows at the caret when it is asked for completions. */\nexport interface EditorCompletionContext {\n /** The full document text. */\n source: string;\n /** 1-based line number. */\n line: number;\n /** 1-based column. */\n column: number;\n /** The full text of the current line. */\n lineText: string;\n}\n\n/**\n * A declarative completion source. Registered ONCE per `MarkdownWorkspace` (or\n * `MarkdownEditor`) tree via the `completions` prop — the library owns the\n * engine registration lifecycle (Monaco's `registerCompletionItemProvider` is\n * global-per-language, refcounted here; see `editor-completions-monaco.ts`).\n */\nexport interface EditorCompletionProvider {\n /** Stable id — surfaced for consumer bookkeeping (not currently rendered). */\n id: string;\n /** Characters that (re)open the suggestion list, e.g. `[\"[\"]`. */\n triggerCharacters?: string[];\n /** Return the candidates for the caret described by `ctx`. May be async. */\n provide(ctx: EditorCompletionContext): EditorCompletionItem[] | Promise<EditorCompletionItem[]>;\n}\n\n/**\n * Find the 1-based column right after the LAST occurrence, before `column`, of\n * any of `triggerCharacters` in `lineText` — the start of the \"trigger query\"\n * (the text typed since the trigger character). `null` when no trigger\n * character precedes the caret on this line (falls back to inserting at the\n * bare caret — see {@link resolveReplaceRange}).\n *\n * Deliberately the LAST occurrence, not the first: `[[note` (triggerCharacters\n * `[\"[\"]`) resolves the query start to right after the SECOND `[`, so the\n * inserted text replaces only `note`, preserving the `[[` the user typed.\n */\nexport function triggerQueryStart(\n lineText: string,\n column: number,\n triggerCharacters: string[] | undefined,\n): number | null {\n if (!triggerCharacters || triggerCharacters.length === 0) return null;\n const before = lineText.slice(0, Math.max(0, column - 1));\n let bestIndex = -1;\n for (const ch of triggerCharacters) {\n if (!ch) continue;\n const idx = before.lastIndexOf(ch);\n if (idx > bestIndex) bestIndex = idx;\n }\n if (bestIndex === -1) return null;\n return bestIndex + 2; // 0-based char index → 1-based column, then step past it\n}\n\n/** A 1-based, Monaco-`IRange`-shaped span (structural — no monaco import). */\nexport interface CompletionReplaceRange {\n startLineNumber: number;\n startColumn: number;\n endLineNumber: number;\n endColumn: number;\n}\n\n/**\n * Resolve the range an item's `insertText` replaces: `item.replaceFrom` when\n * given, else the trigger-query start (see {@link triggerQueryStart}), else the\n * bare caret (a pure insert, nothing replaced). Always ends at the caret, on\n * the caret's line — a completion never spans multiple lines.\n */\nexport function resolveReplaceRange(\n item: EditorCompletionItem,\n position: { lineNumber: number; column: number },\n lineText: string,\n triggerCharacters: string[] | undefined,\n): CompletionReplaceRange {\n const start =\n item.replaceFrom ??\n triggerQueryStart(lineText, position.column, triggerCharacters) ??\n position.column;\n return {\n startLineNumber: position.lineNumber,\n // Clamp into [1, column] — a bad/stale `replaceFrom` degrades to \"insert at\n // caret\" rather than producing a backwards/out-of-range edit.\n startColumn: Math.min(Math.max(1, start), position.column),\n endLineNumber: position.lineNumber,\n endColumn: position.column,\n };\n}\n\n/** One resolved completion item, paired with the provider that produced it. */\nexport interface CompletionMatch {\n provider: EditorCompletionProvider;\n item: EditorCompletionItem;\n}\n\n/**\n * Call every provider's `provide(ctx)`, collect the results, and pair each item\n * back with its provider (so a caller can resolve a per-provider replace range\n * via that provider's `triggerCharacters`). Never throws: a provider whose\n * `provide()` throws or whose returned promise rejects contributes zero items\n * (mirrors `resolveResults`'s calc-hook degrade-safely contract) — one bad\n * provider never blanks another's suggestions.\n */\nexport async function collectCompletions(\n providers: EditorCompletionProvider[],\n ctx: EditorCompletionContext,\n): Promise<CompletionMatch[]> {\n const perProvider = await Promise.all(\n providers.map(async (provider): Promise<CompletionMatch[]> => {\n try {\n const items = (await provider.provide(ctx)) ?? [];\n return items.map((item) => ({ provider, item }));\n } catch {\n return [];\n }\n }),\n );\n return perProvider.flat();\n}\n","/**\n * ProseMirror / Milkdown adapter for the engine-agnostic EditorContentAccess.\n *\n * ISOLATION: this file is MARKDOWN-ONLY — it imports `@milkdown/kit/*` at runtime.\n * It is exported ONLY from `packages/editor/src/markdown/index.ts` (`./markdown`\n * subpath) and NEVER from `src/index.ts` (`.` barrel), so `@milkdown` never leaks\n * into the Monaco-only graph (#271-inverse). The TYPES from `editor-content-access`\n * are type-erased imports — no runtime edge back to the monaco file.\n */\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport { Plugin, PluginKey } from \"@milkdown/kit/prose/state\";\nimport type { EditorView } from \"@milkdown/kit/prose/view\";\nimport { $prose } from \"@milkdown/kit/utils\";\n\nimport type { EditorContentAccess, EditorSelection } from \"./editor-content-access\";\n\nexport interface ProseMirrorContentAccessOptions {\n /** \"markdown\" (default) round-trips formatting; \"plainText\" uses raw text only. */\n fidelity?: \"markdown\" | \"plainText\";\n}\n\n/**\n * Dependency-injected shape for the Milkdown content-access adapter.\n *\n * The handle owns the serialize/parse closures (built via `getInstance().action(ctx\n * => ...)`) and passes them here. This keeps the adapter free of `Ctx` token\n * plumbing and matches the `readBodyMarkdown`/`writeBodyMarkdown` technique in\n * `directive-views.tsx` exactly.\n */\nexport interface ProseMirrorContentAccessDeps {\n /** Return the live `EditorView`, or null while the engine is booting. */\n getView: () => EditorView | null;\n /** Return the full document serialized to markdown. */\n getText: () => string;\n /**\n * Serialize the CURRENT selection's slice to markdown (or \"\" for a collapsed\n * selection). Built by the handle from `serializerCtx`.\n */\n serializeSlice: (view: EditorView) => string;\n /**\n * Parse `md` as a markdown fragment and replace the current selection with the\n * parsed content. On parse failure degrades to a plain-text insert. Built by\n * the handle from `parserCtx`.\n */\n parseAndReplace: (view: EditorView, md: string) => void;\n /**\n * The registered `onSelectionChange` listeners set. Shared between the adapter\n * and the `selectionWatchPlugin` so the plugin can notify subscribers without\n * coupling to the adapter instance.\n */\n listeners: Set<(sel: EditorSelection) => void>;\n}\n\nconst SELECTION_WATCH_KEY = new PluginKey(\"editorContentAccess_selectionWatch\");\n\n/**\n * Build the ProseMirror selection-watch plugin as a Milkdown `$prose` plugin.\n * Compares `prevState.selection` to `view.state.selection` on every transaction and\n * notifies listeners in the shared `listeners` set when they differ.\n *\n * Add to the editor `.use(...)` chain ONCE (always-present; the listeners set is\n * populated / depopulated at subscription time — no per-subscription state in the\n * plugin). Follows the `calcProsePlugins` pattern exactly.\n *\n * @param getListeners - Thunk returning the current listener set. Using a thunk\n * (rather than the set directly) lets the adapter swap the set out if needed —\n * in practice the set is stable; the thunk keeps this composable.\n * @param getSerializeSlice - Thunk: serialize the current selection → markdown.\n */\nexport function selectionWatchPlugin(\n getListeners: () => Set<(sel: EditorSelection) => void>,\n getSerializeSlice: () => (view: EditorView) => string,\n): MilkdownPlugin {\n const plugin = $prose(\n () =>\n new Plugin({\n key: SELECTION_WATCH_KEY,\n view() {\n return {\n update(view, prevState) {\n const ls = getListeners();\n if (ls.size === 0) return;\n if (prevState.selection.eq(view.state.selection)) return;\n const { selection } = view.state;\n const text = selection.empty ? \"\" : getSerializeSlice()(view);\n const sel: EditorSelection = { text, empty: selection.empty };\n ls.forEach((l) => l(sel));\n },\n };\n },\n }),\n );\n return plugin as unknown as MilkdownPlugin;\n}\n\n/**\n * Wrap a Milkdown ProseMirror view as {@link EditorContentAccess}.\n *\n * Receives dependency-injected closures (`serializeSlice`, `parseAndReplace`) that\n * the `MarkdownEditorHandle` builds via `getInstance().action(ctx => ...)`, reusing\n * the `serializerCtx`/`parserCtx` round-trip already shipped in `directive-views.tsx`.\n *\n * The `selectionWatchPlugin` must be added to the editor's `.use(...)` chain so it\n * is installed when the editor boots. The `deps.listeners` set is shared between the\n * plugin and this adapter.\n *\n * Markdown fidelity (default): selection → markdown, incoming text parsed as\n * markdown fragment, with a graceful plain-text fallback on parse failure.\n * Normalization (the WI-1 lesson): the serializer may re-emit formatting differently\n * (e.g. `*` vs `_`). This is the same normalization the WYSIWYG editor applies on\n * every keystroke — consistent, not new. For raw text use `plainText`.\n *\n * D5: content manipulation only — no model/transport.\n */\nexport function proseMirrorContentAccess(\n deps: ProseMirrorContentAccessDeps,\n options: ProseMirrorContentAccessOptions = {},\n): EditorContentAccess {\n const { getView, getText, serializeSlice, parseAndReplace, listeners } = deps;\n // \"plainText\" bypasses the injected markdown closures and uses pure ProseMirror\n // (`doc.textBetween` / `tr.insertText`) — no `serializerCtx`/`parserCtx`, so a\n // selection reads as raw text and an insert lands as literal characters.\n const plainText = options.fidelity === \"plainText\";\n\n function readSelection(view: EditorView): EditorSelection {\n const { selection } = view.state;\n if (selection.empty) return { text: \"\", empty: true };\n const text = plainText\n ? view.state.doc.textBetween(selection.from, selection.to, \"\\n\")\n : serializeSlice(view);\n return { text, empty: false };\n }\n\n // replaceSelection and insertAtCursor are the same primitive (replace the active\n // range; an empty range is the caret) — two names for two reader intents.\n const apply = (text: string): void => {\n const view = getView();\n if (!view) return;\n try {\n if (plainText) {\n view.dispatch(view.state.tr.insertText(text).scrollIntoView());\n } else {\n parseAndReplace(view, text);\n }\n } catch {\n // Graceful degradation: parseAndReplace handles parse failures internally,\n // but if anything throws, never propagate to the caller (spec: never throws).\n }\n };\n\n return {\n getText,\n\n getSelection(): EditorSelection {\n const view = getView();\n if (!view) return { text: \"\", empty: true };\n return readSelection(view);\n },\n\n replaceSelection: apply,\n insertAtCursor: apply,\n\n focus(): void {\n getView()?.focus();\n },\n\n onSelectionChange(listener: (selection: EditorSelection) => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n","/**\n * markdown-scale — the markdown visual scale shared by BOTH renderers of the\n * brand markdown dialect:\n *\n * • the Streamdown preview (React: `prose/prose.tsx` Heading + the `components` map)\n * • the Milkdown WYSIWYG editor (CSS: `markdown-editor/markdown-editor.css`)\n *\n * The two are independent renderers (the editor is ProseMirror-native by design and\n * cannot import the React components — see directive-nodes.ts), so without a shared,\n * machine-consumable scale they drift: switching Source → Split → Preview-edit\n * visibly re-skins headings / measure (issue #18).\n *\n * Since #188 the numbers are DERIVED, not re-hardcoded: @elabs-ai/components-ui owns the canonical\n * reading scale (`PROSE_HEADING_REM` in `components/typography/prose.tsx`, itself\n * pinned to the `--text-<role>` tokens where the rungs coincide). This module is the\n * editor-side seam: it re-exports those numbers and emits them as CSS variables (set\n * on `.milkdown-host` via `markdownScaleVars()`); `markdown-scale.test.ts` fails the\n * moment either renderer diverges.\n */\nimport {\n PROSE_HEADING_REM,\n PROSE_HEADING_TRACKING,\n PROSE_HEADING_WEIGHT,\n} from \"@elabs-ai/components-ui\";\n\nexport type MarkdownHeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\n/**\n * Canonical heading font-size per level, in rem — derived from the @elabs-ai/components-ui\n * prose reading scale (h2/h4/h5/h6 == the title/subtitle/body role rems;\n * h1/h3 are intermediate reading rungs).\n */\nexport const MARKDOWN_HEADING_REM: Record<MarkdownHeadingLevel, number> = PROSE_HEADING_REM;\n\n/** Canonical heading weight (Tailwind `font-semibold`) — derived from @elabs-ai/components-ui. */\nexport const MARKDOWN_HEADING_WEIGHT = PROSE_HEADING_WEIGHT;\n\n/** Canonical heading letter-spacing (Tailwind `tracking-tight`) — derived from @elabs-ai/components-ui. */\nexport const MARKDOWN_HEADING_TRACKING = PROSE_HEADING_TRACKING;\n\n/** Canonical reading measure (max content width). Mirrors `max-w-3xl`. */\nexport const MARKDOWN_MEASURE = \"48rem\";\n\n/**\n * The scale as CSS custom properties, to set on the editor host (`.milkdown-host`)\n * so `markdown-editor.css` reads the SAME numbers as the prose components instead of\n * hardcoding its own. Spread onto a `style` prop:\n * `<div className=\"milkdown-host\" style={markdownScaleVars()} />`\n */\nexport function markdownScaleVars(): Record<string, string> {\n return {\n \"--md-h1\": `${MARKDOWN_HEADING_REM[1]}rem`,\n \"--md-h2\": `${MARKDOWN_HEADING_REM[2]}rem`,\n \"--md-h3\": `${MARKDOWN_HEADING_REM[3]}rem`,\n \"--md-h4\": `${MARKDOWN_HEADING_REM[4]}rem`,\n \"--md-h5\": `${MARKDOWN_HEADING_REM[5]}rem`,\n \"--md-h6\": `${MARKDOWN_HEADING_REM[6]}rem`,\n \"--md-heading-weight\": String(MARKDOWN_HEADING_WEIGHT),\n \"--md-heading-tracking\": MARKDOWN_HEADING_TRACKING,\n \"--md-measure\": MARKDOWN_MEASURE,\n };\n}\n","\"use client\";\n\n/**\n * The seam between an iteration node-view's `⋯` \"Edit template…\" affordance and\n * the {@link IterationTemplateDialog} that fulfils it.\n *\n * Decoupled ON PURPOSE: the node-view (inside `MarkdownEditor`) must NOT import the\n * dialog (which embeds a whole `MarkdownWorkspace` — a heavy, cyclic dependency).\n * Instead the node-view emits an edit REQUEST through this context; the consumer\n * (above the editor) renders the dialog and writes the result back via `onSave`.\n * When no handler is provided, the `⋯` button is hidden and the template stays\n * editable inline. This keeps `@elabs-ai/components-editor` a presentation layer — the app owns\n * how/where the modal mounts.\n */\nimport { createContext } from \"react\";\n\nexport interface IterationEditRequest {\n /** Which directive asked to be edited. */\n kind: \"iterate\" | \"pivot\";\n /** The current template markdown (the node body, serialized). */\n template: string;\n /** Apply the edited template back to the node. */\n onSave: (template: string) => void;\n /**\n * The directive's current attributes (value lists, bind name, layout) — present\n * when the node-view supports GUIDED re-editing (A5). The\n * `IterationBuilderProvider` reads these to reopen the builder with its data;\n * the template-only `IterationTemplateProvider` ignores them.\n */\n attributes?: Record<string, string>;\n /**\n * Write back BOTH the attributes and the template (the guided builder path). When\n * present it is preferred over {@link onSave} (which only rewrites the body).\n */\n onSaveData?: (next: { attributes: Record<string, string>; template: string }) => void;\n /**\n * Directly rewrite the node's attributes (e.g. the node-menu's \"Change layout\" /\n * \"Transpose\" actions) without touching the template body. Additive (#223) —\n * present alongside {@link onSave}/{@link onSaveData} for back-compat; a\n * consumer-side surface (a future dialog action) can reach the same write the\n * node-view's own menu uses.\n */\n onSetAttributes?: (attributes: Record<string, string>) => void;\n /**\n * Replace the ENTIRE directive node with plain markdown — e.g. the node-menu's\n * \"Convert to static\" action (the evaluated, populated result). After this the\n * node stops being a `:::iterate`/`:::pivot` directive. Additive (#223).\n */\n onReplaceWithMarkdown?: (markdown: string) => void;\n}\n\n/** A consumer handler that opens the template editor for a request. */\nexport type IterationEditHandler = (request: IterationEditRequest) => void;\n\n/** Provide a handler to enable the node-view `⋯` re-edit affordance. */\nexport const IterationEditContext = createContext<IterationEditHandler | null>(null);\n","/**\n * YAML frontmatter parse/serialize. Splits the leading `---` block off a markdown\n * document so the body can be rendered/edited and the metadata can drive a form.\n *\n * `parseFrontmatter` throws on malformed YAML — callers (the app) catch and map it\n * to a typed error for the metadata panel.\n */\nimport { dump, load } from \"js-yaml\";\n\nexport interface ParsedDocument {\n /** Parsed frontmatter object ({} when there is no frontmatter block). */\n frontmatter: Record<string, unknown>;\n /** Markdown body with the frontmatter block removed. */\n content: string;\n hasFrontmatter: boolean;\n}\n\n// Optional leading BOM (\\uFEFF), then a `---` … `---` block at the very top.\nconst FRONTMATTER_RE = /^\\uFEFF?---[ \\t]*\\r?\\n([\\s\\S]*?)\\r?\\n---[ \\t]*(?:\\r?\\n|$)/;\n\nexport function parseFrontmatter(source: string): ParsedDocument {\n const match = source.match(FRONTMATTER_RE);\n if (!match) {\n return { frontmatter: {}, content: source, hasFrontmatter: false };\n }\n\n const parsed = load(match[1] ?? \"\");\n const frontmatter =\n parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n\n return {\n frontmatter,\n content: source.slice(match[0].length),\n hasFrontmatter: true,\n };\n}\n\nexport function serializeFrontmatter(\n frontmatter: Record<string, unknown>,\n content: string,\n): string {\n const body = content.replace(/^\\s+/, \"\");\n if (!frontmatter || Object.keys(frontmatter).length === 0) {\n return body;\n }\n const yaml = dump(frontmatter, { lineWidth: -1, noRefs: true }).trimEnd();\n return `---\\n${yaml}\\n---\\n\\n${body}`;\n}\n","/**\n * Shared heading-slug helpers.\n *\n * The same slug algorithm is used by:\n * - `parseMarkdownOutline` (static markdown → outline items, slug as `id`)\n * - `MarkdownEditorHandle.scrollToHeading` (ProseMirror doc walk, WYSIWYG)\n *\n * Keeping both on a single helper guarantees the slugs agree so\n * `scrollToHeading(slug)` finds the right node when called with an id produced\n * by `parseMarkdownOutline`. (#273)\n */\n\n/** Strip the inline-markdown syntax that commonly decorates headings. */\nexport function plainText(raw: string): string {\n return raw\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/[`*_~]/g, \"\")\n .trim();\n}\n\n/**\n * GitHub-style heading slug: lowercase, strip non-letter/number/space/hyphen,\n * collapse whitespace to hyphens. Falls back to `\"section\"` for empty text.\n */\nexport function slugifyHeading(text: string): string {\n return (\n text\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\s-]/gu, \"\")\n .trim()\n .replace(/\\s+/g, \"-\") || \"section\"\n );\n}\n\n/**\n * Produce a unique slug for a heading, given a Map of already-used base slugs\n * (values are the count seen so far). Mutates `used`.\n *\n * The algorithm matches `parseMarkdownOutline`: first occurrence is the bare\n * slug; duplicates get a `-n` numeric suffix starting at 1.\n */\nexport function uniqueSlug(base: string, used: Map<string, number>): string {\n const seen = used.get(base) ?? 0;\n used.set(base, seen + 1);\n return seen === 0 ? base : `${base}-${seen}`;\n}\n","/**\n * Markdown outline extraction (#L6) — the pure half of `DocumentOutline`.\n *\n * `parseMarkdownOutline` walks ATX headings (`#` … `######`) outside fenced\n * code blocks and returns the document outline. Line numbers are 1-based and\n * relative to the frontmatter-STRIPPED body — the same coordinate space as the\n * `data-sourcepos` attributes `MarkdownPreview` stamps on rendered blocks, so\n * outline → block lookup is a direct equality on the start line.\n */\nimport { parseFrontmatter } from \"../lib/markdown/frontmatter\";\nimport { plainText, slugifyHeading, uniqueSlug } from \"../lib/markdown/slugify\";\n\nexport interface MarkdownOutlineItem {\n /** Stable slug (GitHub-style, deduped with `-n` suffixes). */\n id: string;\n /** Plain heading text (inline markdown stripped). */\n text: string;\n /** Heading level 1–6. */\n level: 1 | 2 | 3 | 4 | 5 | 6;\n /** 1-based line in the frontmatter-stripped source (matches `data-sourcepos`). */\n line: number;\n}\n\nconst HEADING_RE = /^(#{1,6})\\s+(.*?)\\s*#*\\s*$/;\nconst FENCE_RE = /^(```|~~~)/;\n\nexport function parseMarkdownOutline(markdown: string): MarkdownOutlineItem[] {\n let body = markdown;\n try {\n body = parseFrontmatter(markdown).content;\n } catch {\n // Transient-invalid frontmatter while typing — outline the raw source.\n }\n const lines = body.split(\"\\n\");\n const items: MarkdownOutlineItem[] = [];\n const used = new Map<string, number>();\n let inFence = false;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (FENCE_RE.test(line.trimStart())) {\n inFence = !inFence;\n continue;\n }\n if (inFence) continue;\n const match = HEADING_RE.exec(line);\n if (!match) continue;\n const text = plainText(match[2] ?? \"\");\n if (!text) continue;\n const base = slugifyHeading(text);\n const id = uniqueSlug(base, used);\n items.push({\n id,\n text,\n level: match[1]!.length as MarkdownOutlineItem[\"level\"],\n line: i + 1,\n });\n }\n return items;\n}\n","\"use client\";\n\n/**\n * DocumentOutline (#L6) — the quiet table-of-contents rail for a markdown\n * document. Pure presentation: pass `items` from `parseMarkdownOutline` /\n * `useMarkdownOutline`, drive `activeId` from your scroll observer, and handle\n * `onSelect` (e.g. scroll the matching `data-sourcepos` block into view).\n */\nimport { useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { forwardRef, useMemo, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { parseMarkdownOutline, type MarkdownOutlineItem } from \"./markdown-outline\";\n\n/** Memoized outline of a markdown source. */\nexport function useMarkdownOutline(markdown: string): MarkdownOutlineItem[] {\n return useMemo(() => parseMarkdownOutline(markdown), [markdown]);\n}\n\nexport interface DocumentOutlineProps extends Omit<HTMLAttributes<HTMLElement>, \"onSelect\"> {\n items: MarkdownOutlineItem[];\n /** The outline item currently in view. */\n activeId?: string;\n onSelect?: (item: MarkdownOutlineItem) => void;\n /**\n * Per-entry hover affordances (pin/copy-link…), rendered at the row end.\n * Revealed on row hover/focus; stays visible while it contains a pressed\n * toggle (`aria-pressed=\"true\"`).\n */\n itemActions?: (item: MarkdownOutlineItem) => ReactNode;\n}\n\nexport const DocumentOutline = forwardRef<HTMLElement, DocumentOutlineProps>(\n function DocumentOutline({ items, activeId, onSelect, itemActions, className, ...props }, ref) {\n const { t } = useLocale();\n const minLevel = items.reduce<number>((min, it) => Math.min(min, it.level), 6);\n return (\n <nav\n ref={ref}\n aria-label={t(\"editor.documentOutline.label\")}\n className={cn(\"text-body\", className)}\n {...props}\n >\n {/* TOC grammar: a structural hairline rail; each entry overlays it with a\n 2px segment — primary for the active heading, visible on hover — so\n position-in-document reads at a glance (the accent-rail channel from\n the separation grammar, not a generic list). */}\n <ul className=\"m-0 list-none border-s border-border p-0\">\n {items.map((item) => {\n const actions = itemActions?.(item);\n return (\n <li key={item.id} className={cn(actions && \"group/outline-item relative\")}>\n <button\n type=\"button\"\n aria-current={item.id === activeId ? \"true\" : undefined}\n onClick={() => onSelect?.(item)}\n className={cn(\n \"-ms-px block w-full truncate border-s-2 py-1 pe-2 text-start text-caption\",\n \"transition-colors duration-fast ease-standard motion-reduce:transition-none\",\n \"focus-ring-inset\",\n item.id === activeId\n ? \"border-s-primary font-medium text-foreground\"\n : \"border-s-transparent text-muted-foreground hover:border-s-border-strong hover:text-foreground\",\n actions && \"pe-8\",\n )}\n style={{ paddingInlineStart: `${(item.level - minLevel) * 0.875 + 0.75}rem` }}\n >\n {item.text}\n </button>\n {actions ? (\n <span className=\"absolute end-0.5 top-1/2 -translate-y-1/2 opacity-0 transition-opacity duration-fast ease-standard focus-within:opacity-100 group-hover/outline-item:opacity-100 has-[[aria-pressed=true]]:opacity-100 motion-reduce:transition-none\">\n {actions}\n </span>\n ) : null}\n </li>\n );\n })}\n </ul>\n {items.length === 0 ? (\n <p className=\"px-2 py-1 text-caption text-muted-foreground\">\n {t(\"editor.documentOutline.empty\")}\n </p>\n ) : null}\n </nav>\n );\n },\n);\n","\"use client\";\n\n/**\n * IterationBlock — a `:::iterate` / `:::pivot` repeater.\n *\n * Renders a per-cell markdown TEMPLATE once per data row (iterate) or per\n * row×column cross-tab cell (pivot). Both domain concerns stay in the consumer\n * (the calc/citation precedent — *the library renders, the app computes*):\n * - `evaluate(spec) => IterationData` resolves the data source (the app's DB /\n * query / binding lives there);\n * - `interpolate(template, context) => string` fills the template (the app's\n * templating engine; a minimal `{{path}}` default is provided).\n * The library owns only layout + rendering. `render` turns a resolved cell's\n * markdown into a node (a nested `MarkdownPreview` when wired through the preview).\n *\n * `grid` / `matrix` reuse the shared `@elabs-ai/components-ui` `Table` primitive (the same base\n * `@elabs-ai/components-data`'s DataTable builds on — `@elabs-ai/components-editor` can't import the sibling\n * `@elabs-ai/components-data`, so we compose the shared primitive rather than fork it). `bento`\n * lays the cells out as varied-size `@elabs-ai/components-ui` `BentoGrid` tiles.\n */\nimport {\n BentoGrid,\n BentoGridItem,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n type BentoGridSize,\n} from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { Repeat2 } from \"lucide-react\";\nimport { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport type IterationLayout = \"stacked\" | \"grid\" | \"matrix\" | \"bento\";\n\n/** The parsed `:::iterate` / `:::pivot` specification handed to `evaluate`. */\nexport interface IterationSpec {\n /** `iterate` (1D repeat) or `pivot` (2D cross-tab). */\n kind: \"iterate\" | \"pivot\";\n /** How the cells are laid out. */\n layout: IterationLayout;\n /** The per-cell markdown template (the directive body). */\n template: string;\n /** Loop-variable name exposed to `interpolate` (default `\"item\"`). */\n as: string;\n /** Opaque data-source reference; resolved by the consumer's `evaluate`. */\n source?: string;\n /** Pivot row / column dimension field names (`pivot`). */\n rows?: string;\n cols?: string;\n /** Explicit grid column count (`grid`). */\n columns?: number;\n /** All raw directive attributes (escape hatch for `evaluate`). */\n attributes: Record<string, string>;\n}\n\n/** One resolved cell. */\nexport interface IterationCell {\n /** Interpolation context (the row record / pivot-cell scope). */\n context: Record<string, unknown>;\n /** Stable key (defaults to the index). */\n key?: string;\n /** `matrix`: the row / column header this cell sits at. */\n row?: string;\n col?: string;\n /** `bento`: tile size preset. Defaults to a deterministic per-index rhythm. */\n size?: BentoGridSize;\n /** Pre-rendered markdown — skips `interpolate` when the app already templated. */\n markdown?: string;\n}\n\n/** What the consumer's `evaluate` returns. */\nexport interface IterationData {\n /** Cells in render order. */\n cells: IterationCell[];\n /** `matrix`: ordered row + column header labels. */\n rowHeaders?: string[];\n colHeaders?: string[];\n /** `grid`: column header labels (also sets the column count). */\n columns?: string[];\n}\n\n/** Consumer hook: resolve a spec to its data, or `null` when unavailable. */\nexport type EvaluateIteration = (spec: IterationSpec) => IterationData | null;\n\n/** Consumer hook: fill a template with a cell context. */\nexport type InterpolateTemplate = (template: string, context: Record<string, unknown>) => string;\n\n/* ------------------------------------------------------------------ */\n/* Default templating (minimal; the app should bring its own engine) */\n/* ------------------------------------------------------------------ */\n\nconst TOKEN_RE = /\\{\\{\\s*([\\w.$]+)\\s*\\}\\}/g;\n\n/**\n * Minimal `{{ path.to.value }}` substitution over the cell context (dot paths).\n * Safe (no eval). An UNRESOLVED token is left **literal** — so a nested\n * `:::iterate`'s tokens survive the outer pass and resolve in the inner one (use\n * the `as` loop variable in nested templates to avoid scope collisions). Replace\n * it with your own engine (Mustache/Handlebars/…) via the `interpolate` prop.\n */\nexport function defaultInterpolate(template: string, context: Record<string, unknown>): string {\n return template.replace(TOKEN_RE, (match, path: string) => {\n const value = path.split(\".\").reduce<unknown>((obj, key) => {\n if (obj && typeof obj === \"object\") return (obj as Record<string, unknown>)[key];\n return undefined;\n }, context);\n return value == null ? match : String(value);\n });\n}\n\n/** Build the interpolation scope for a cell (fields + `as`-nested + row/col/index). */\nfunction cellScope(\n spec: IterationSpec,\n cell: IterationCell,\n index: number,\n): Record<string, unknown> {\n return {\n ...cell.context,\n [spec.as]: cell.context,\n index,\n row: cell.row,\n col: cell.col,\n };\n}\n\nfunction safeEvaluate(evaluate: EvaluateIteration, spec: IterationSpec): IterationData | null {\n try {\n return evaluate(spec);\n } catch {\n // A throwing hook degrades to the empty state — it never crashes the page.\n return null;\n }\n}\n\nfunction chunk<T>(arr: T[], size: number): T[][] {\n if (size <= 0) return [arr];\n const out: T[][] = [];\n for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));\n return out;\n}\n\n/**\n * Deterministic bento tile-size rhythm by index — a 2×2 feature tile to open each\n * six-cell cycle and a 2×1 wide tile mid-cycle, the rest 1×1. `grid-auto-flow:\n * dense` packs them and col-spans clamp to the available tracks on narrow widths.\n * A cell can override this via {@link IterationCell.size}.\n */\nfunction bentoSize(index: number): BentoGridSize {\n const m = index % 6;\n if (m === 0) return \"lg\";\n if (m === 3) return \"md\";\n return \"sm\";\n}\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport interface IterationBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** The parsed iteration spec (from the directive, or hand-built). */\n spec: IterationSpec;\n /** Resolve the spec to data (consumer data source). */\n evaluate: EvaluateIteration;\n /** Fill a template with a cell context. Default: `{{path}}` substitution. */\n interpolate?: InterpolateTemplate;\n /** Render a resolved cell's markdown → node (e.g. a nested `MarkdownPreview`). */\n render: (markdown: string) => ReactNode;\n /** Override `spec.layout`. */\n layout?: IterationLayout;\n /** Empty-state message when there are no cells. */\n emptyLabel?: string;\n}\n\nexport const IterationBlock = forwardRef<HTMLDivElement, IterationBlockProps>(\n function IterationBlock(\n {\n spec,\n evaluate,\n interpolate = defaultInterpolate,\n render,\n layout,\n emptyLabel,\n className,\n ...props\n },\n ref,\n ) {\n const data = safeEvaluate(evaluate, spec);\n const cells = data?.cells ?? [];\n const resolvedLayout = layout ?? spec.layout;\n\n const renderCell = (cell: IterationCell, index: number): ReactNode => {\n const md = cell.markdown ?? interpolate(spec.template, cellScope(spec, cell, index));\n return render(md);\n };\n\n const label = spec.kind === \"pivot\" ? \"Pivot\" : \"Iteration\";\n\n if (cells.length === 0) {\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={label}\n data-iteration={spec.kind}\n className={cn(\"my-4 flex items-center gap-2 text-meta text-muted-foreground\", className)}\n {...props}\n >\n <Repeat2 className=\"size-4 shrink-0\" aria-hidden=\"true\" />\n <span>\n {emptyLabel ?? `Nothing to ${spec.kind === \"pivot\" ? \"pivot\" : \"iterate\"} yet.`}\n </span>\n </div>\n );\n }\n\n // matrix needs both header axes; fall back to grid when they're absent.\n const isMatrix =\n resolvedLayout === \"matrix\" && !!data?.rowHeaders?.length && !!data?.colHeaders?.length;\n\n let body: ReactNode;\n if (resolvedLayout === \"stacked\") {\n body = (\n <ol className=\"space-y-6\">\n {cells.map((cell, i) => (\n <li key={cell.key ?? i} className=\"[&>*:first-child]:mt-0 [&>*:last-child]:mb-0\">\n {renderCell(cell, i)}\n </li>\n ))}\n </ol>\n );\n } else if (resolvedLayout === \"bento\") {\n body = (\n <BentoGrid>\n {cells.map((cell, i) => (\n <BentoGridItem key={cell.key ?? i} size={cell.size ?? bentoSize(i)}>\n <div className=\"h-full overflow-auto p-4 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0\">\n {renderCell(cell, i)}\n </div>\n </BentoGridItem>\n ))}\n </BentoGrid>\n );\n } else if (isMatrix) {\n const rowHeaders = data!.rowHeaders!;\n const colHeaders = data!.colHeaders!;\n const at = new Map<string, { cell: IterationCell; index: number }>();\n cells.forEach((cell, i) => {\n if (cell.row != null && cell.col != null)\n at.set(JSON.stringify([cell.row, cell.col]), { cell, index: i });\n });\n body = (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead aria-hidden=\"true\" />\n {colHeaders.map((c) => (\n <TableHead key={c} scope=\"col\">\n {c}\n </TableHead>\n ))}\n </TableRow>\n </TableHeader>\n <TableBody>\n {rowHeaders.map((r) => (\n <TableRow key={r}>\n <TableHead scope=\"row\" className=\"font-medium text-foreground\">\n {r}\n </TableHead>\n {colHeaders.map((c) => {\n const hit = at.get(JSON.stringify([r, c]));\n return (\n <TableCell key={c} className=\"align-top\">\n {hit ? renderCell(hit.cell, hit.index) : null}\n </TableCell>\n );\n })}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n );\n } else {\n // grid (and matrix fallback): cells laid row-major into N columns.\n const headers = data?.columns;\n const colCount = (headers?.length ?? spec.columns ?? Math.min(cells.length, 3)) || 1;\n const rows = chunk(cells, colCount);\n body = (\n <Table>\n {headers ? (\n <TableHeader>\n <TableRow>\n {headers.map((h) => (\n <TableHead key={h} scope=\"col\">\n {h}\n </TableHead>\n ))}\n </TableRow>\n </TableHeader>\n ) : null}\n <TableBody>\n {rows.map((row, r) => (\n <TableRow key={r}>\n {row.map((cell, c) => (\n <TableCell key={cell.key ?? c} className=\"align-top\">\n {renderCell(cell, r * colCount + c)}\n </TableCell>\n ))}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n );\n }\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={label}\n data-iteration={spec.kind}\n data-iteration-layout={resolvedLayout}\n className={cn(\"my-4\", className)}\n {...props}\n >\n {body}\n </div>\n );\n },\n);\n","/**\n * Iteration BUILDER model (A5) — the data layer behind the guided\n * `:::iterate` / `:::pivot` authoring dialog.\n *\n * The existing `IterationTemplateDialog` only edits the per-cell TEMPLATE. The\n * guided builder also collects the DATA (the value list(s) + bind name + the\n * pivot's second axis + layout) and writes a **fully-bound** directive whose\n * value lists live in its attributes — so the block renders a populated result\n * with the built-in {@link evaluateEmbedded} (no consumer data engine needed) and\n * the `⋯` re-edit can reopen it losslessly.\n *\n * Pure + React-free so it is unit-testable on its own:\n * serialize → `:::iterate{as=\"item\" layout=\"stacked\" values=\"A, B, C\"}\\n…\\n:::`\n * `:::pivot{layout=\"matrix\" rows=\"Q1, Q2\" cols=\"N, S\"}\\n…\\n:::`\n * parse ← the inverse (for the `⋯` reopen)\n * evaluate → {@link evaluateEmbedded} turns the embedded lists into cells.\n *\n * Value lists are comma-separated in the attribute (values containing a literal\n * comma aren't supported — the dialog enters one value per line and joins them).\n */\nimport {\n defaultInterpolate,\n type InterpolateTemplate,\n type IterationCell,\n type IterationData,\n type IterationLayout,\n type IterationSpec,\n} from \"./iteration\";\n\n/** The dialog's working value — the collected data + template for one directive. */\nexport interface IterationBuilderValue {\n kind: \"iterate\" | \"pivot\";\n /** Loop-variable name (iterate). Default `\"item\"`. */\n as: string;\n layout: IterationLayout;\n /** iterate: the list of items. pivot: the ROW values. */\n values: string[];\n /** pivot: the COLUMN values. */\n cols?: string[];\n /** The per-cell markdown template (directive body). */\n template: string;\n}\n\nconst DEFAULT_LAYOUT: Record<\"iterate\" | \"pivot\", IterationLayout> = {\n iterate: \"stacked\",\n pivot: \"matrix\",\n};\n\n/**\n * The layouts offered per kind (the guided builder's toggle group AND the\n * node-menu's \"Change layout\" submenu share this single list so they can't\n * drift). `iterate` has no `matrix` (it has only one axis); `pivot` leads with\n * `matrix` (its natural shape).\n */\nexport const ITERATION_LAYOUTS: Record<\"iterate\" | \"pivot\", IterationLayout[]> = {\n iterate: [\"stacked\", \"grid\", \"bento\"],\n pivot: [\"matrix\", \"grid\", \"bento\"],\n};\n\n/** Default per-cell template per kind (so a fresh block renders something). */\nexport const DEFAULT_TEMPLATE: Record<\"iterate\" | \"pivot\", string> = {\n iterate: \"{{item.name}}\",\n pivot: \"{{row}} · {{col}}\",\n};\n\n/** Split a comma-separated attribute into trimmed, non-empty values. */\nexport function splitList(value: string | undefined): string[] {\n return (value ?? \"\")\n .split(\",\")\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n}\n\n/** Parse a directive attribute string (`a=\"x\" b=\"y\"`) into a record. */\nexport function parseAttributes(attrString: string): Record<string, string> {\n const out: Record<string, string> = {};\n const re = /([\\w-]+)\\s*=\\s*\"([^\"]*)\"/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(attrString))) {\n const key = m[1];\n if (key) out[key] = m[2] ?? \"\";\n }\n return out;\n}\n\n/** Serialize an attributes record back to `key=\"value\"` (stable, escaped). */\nfunction attrString(attrs: Record<string, string>): string {\n return Object.entries(attrs)\n .filter(([, v]) => v != null && v !== \"\")\n .map(([k, v]) => `${k}=\"${String(v).replace(/\"/g, \"'\")}\"`)\n .join(\" \");\n}\n\n/** Build the `{ attributes, template }` for a builder value (the write-back shape). */\nexport function directivePartsFromValue(value: IterationBuilderValue): {\n attributes: Record<string, string>;\n template: string;\n} {\n const attributes: Record<string, string> =\n value.kind === \"pivot\"\n ? {\n layout: value.layout,\n rows: value.values.join(\", \"),\n cols: (value.cols ?? []).join(\", \"),\n }\n : {\n as: value.as || \"item\",\n layout: value.layout,\n values: value.values.join(\", \"),\n };\n return { attributes, template: value.template.trim() };\n}\n\n/**\n * The longest run of leading colons (≥3 — a nested CONTAINER-directive fence such\n * as `:::card` / `:::callout` / a nested `:::iterate`) on any line of the template.\n * `remark-directive` closes a container at the nearest fence of EQUAL length, so an\n * outer fence the same length as a nested one is terminated early (the body is\n * truncated and everything after the nested block is dropped). Returns 0 when the\n * template has no nested container directive.\n */\nfunction maxColonRun(template: string): number {\n let max = 0;\n // Up to 3 leading spaces are allowed before a directive fence (CommonMark).\n const re = /^ {0,3}(:{3,})/;\n for (const line of template.split(/\\r?\\n/)) {\n const m = re.exec(line);\n if (m && m[1]!.length > max) max = m[1]!.length;\n }\n return max;\n}\n\n/**\n * Pick the OUTER directive fence: at least 3 colons, and always STRICTLY longer\n * than any nested container fence in the template — so a nested `:::card`'s closing\n * `:::` can't terminate the outer `:::iterate` early. A template with no nested\n * container directive keeps the canonical `:::` (backward compatible).\n */\nexport function outerDirectiveFence(template: string): string {\n return \":\".repeat(Math.max(3, maxColonRun(template) + 1));\n}\n\n/** Serialize a builder value to a fully-bound `:::iterate` / `:::pivot` directive. */\nexport function serializeIterationDirective(value: IterationBuilderValue): string {\n const { attributes, template } = directivePartsFromValue(value);\n const fence = outerDirectiveFence(template);\n return `${fence}${value.kind}{${attrString(attributes)}}\\n${template}\\n${fence}`;\n}\n\n/** Build a builder value from a directive's kind + attributes + body template. */\nexport function builderValueFromParts(\n kind: \"iterate\" | \"pivot\",\n attributes: Record<string, string>,\n template: string,\n): IterationBuilderValue {\n const layout = (attributes.layout as IterationLayout) || DEFAULT_LAYOUT[kind];\n if (kind === \"pivot\") {\n return {\n kind,\n as: attributes.as?.trim() || \"item\",\n layout,\n values: splitList(attributes.rows),\n cols: splitList(attributes.cols),\n template: template.trim() || DEFAULT_TEMPLATE.pivot,\n };\n }\n return {\n kind,\n as: attributes.as?.trim() || \"item\",\n layout,\n values: splitList(attributes.values),\n template: template.trim() || DEFAULT_TEMPLATE.iterate,\n };\n}\n\n/**\n * Parse a full `:::iterate` / `:::pivot` directive string into a builder value.\n *\n * The fence length is variable (`:{3,}`) and the close is matched by BACKREFERENCE\n * (`\\1`) to the same length as the open, so a directive whose body contains a\n * nested `:::card` (3 colons) is opened/closed with `::::` (4) and the inner fence\n * is not mistaken for the outer close. The non-greedy body anchored to end-of-input\n * keeps the canonical 3-colon form round-tripping unchanged.\n */\nexport function parseIterationDirective(markdown: string): IterationBuilderValue | null {\n const m = markdown.match(/^(:{3,})(iterate|pivot)\\{([^}]*)\\}\\n?([\\s\\S]*?)\\n?\\1\\s*$/);\n if (!m) return null;\n return builderValueFromParts(\n m[2] as \"iterate\" | \"pivot\",\n parseAttributes(m[3] ?? \"\"),\n m[4] ?? \"\",\n );\n}\n\n/** An empty builder value for a fresh insert. */\nexport function emptyBuilderValue(kind: \"iterate\" | \"pivot\"): IterationBuilderValue {\n return {\n kind,\n as: \"item\",\n layout: DEFAULT_LAYOUT[kind],\n values: [],\n cols: kind === \"pivot\" ? [] : undefined,\n template: DEFAULT_TEMPLATE[kind],\n };\n}\n\n/**\n * Built-in iteration evaluator: resolve the value list(s) embedded in the\n * directive attributes into cells — so a block authored with the guided builder\n * renders a populated result with no consumer data engine. iterate reads\n * `values=`; pivot reads `rows=` × `cols=`. Assignable to `EvaluateIteration`\n * (it never returns null — it degrades to an empty cell list).\n */\nexport function evaluateEmbedded(spec: IterationSpec): IterationData {\n if (spec.kind === \"pivot\") {\n const rows = splitList(spec.rows ?? spec.attributes.rows);\n const cols = splitList(spec.cols ?? spec.attributes.cols);\n if (rows.length === 0 || cols.length === 0) return { cells: [] };\n const cells = rows.flatMap((row) =>\n cols.map((col) => ({ context: { row, col }, row, col, key: `${row}|${col}` })),\n );\n return { cells, rowHeaders: rows, colHeaders: cols };\n }\n const values = splitList(spec.attributes.values ?? spec.source);\n if (values.length === 0) return { cells: [] };\n return {\n cells: values.map((value, i) => ({ context: { value, name: value }, key: `${i}:${value}` })),\n };\n}\n\n/**\n * Swap a pivot's ROW and COLUMN value lists (the node-menu \"Transpose\" action).\n * A no-op for `iterate` (it has no second axis) — the menu item hides itself in\n * that case, but the function stays total (never throws) so a stale/misrouted\n * call is harmless. `transposeIterationValue(transposeIterationValue(v)) === v`\n * for any pivot value (a lossless round-trip).\n */\nexport function transposeIterationValue(value: IterationBuilderValue): IterationBuilderValue {\n if (value.kind !== \"pivot\") return value;\n return { ...value, values: value.cols ?? [], cols: value.values };\n}\n\n/** Build the interpolation scope for one resolved cell (mirrors `IterationBlock`'s). */\nfunction cellScope(as: string, cell: IterationCell, index: number): Record<string, unknown> {\n return { ...cell.context, [as]: cell.context, index, row: cell.row, col: cell.col };\n}\n\n/** Escape a cell's markdown for use inside a GFM table cell (single logical line). */\nfunction escapeTableCell(markdown: string): string {\n return markdown\n .replace(/\\|/g, \"\\\\|\")\n .replace(/\\r?\\n+/g, \" \")\n .trim();\n}\n\n/**\n * Render a builder value's RESOLVED cells as plain, static markdown — the\n * \"Convert to static\" node-menu action. Reuses {@link evaluateEmbedded} (the same\n * resolver the live preview renders through), so the output matches what the\n * block currently shows, with no ProseMirror/dialog dependency:\n * - `matrix` (with both header axes present) → a GFM table.\n * - anything else (`stacked` / `grid` / `bento`) → cells joined as sequential\n * markdown blocks, separated by a blank line.\n * An empty result set (no values entered yet) returns an empty string.\n */\nexport function staticMarkdownFromValue(\n value: IterationBuilderValue,\n interpolate: InterpolateTemplate = defaultInterpolate,\n): string {\n const { attributes } = directivePartsFromValue(value);\n const spec: IterationSpec = {\n kind: value.kind,\n layout: value.layout,\n template: value.template,\n as: value.as,\n attributes,\n };\n const data = evaluateEmbedded(spec);\n if (data.cells.length === 0) return \"\";\n\n const cellMarkdown = (cell: IterationCell, index: number): string =>\n interpolate(value.template, cellScope(value.as, cell, index)).trim();\n\n if (value.layout === \"matrix\" && data.rowHeaders?.length && data.colHeaders?.length) {\n const rowHeaders = data.rowHeaders;\n const colHeaders = data.colHeaders;\n const at = new Map<string, string>();\n data.cells.forEach((cell, i) => {\n if (cell.row != null && cell.col != null) {\n at.set(`${cell.row}|${cell.col}`, cellMarkdown(cell, i));\n }\n });\n const headerRow = [\"\", ...colHeaders.map(escapeTableCell)];\n const dividerRow = headerRow.map(() => \"---\");\n const bodyRows = rowHeaders.map((row) => [\n escapeTableCell(row),\n ...colHeaders.map((col) => escapeTableCell(at.get(`${row}|${col}`) ?? \"\")),\n ]);\n return [headerRow, dividerRow, ...bodyRows].map((r) => `| ${r.join(\" | \")} |`).join(\"\\n\");\n }\n\n return data.cells.map((cell, i) => cellMarkdown(cell, i)).join(\"\\n\\n\");\n}\n","/**\n * MetricBlock — thin alias for the canonical `@elabs-ai/components-ui` MetricCard.\n *\n * Previously a fork that added `description` + avoided an editor→charts sideways\n * dep. Now that MetricCard lives in `@elabs-ai/components-ui` (which @elabs-ai/components-editor already depends\n * on), this wrapper preserves the `MetricBlock` / `MetricBlockProps` public names so\n * `directive-views.tsx` and `markdown/index.ts` keep working unchanged.\n *\n * ADR: docs/ADR/0012-metric-card-canonical-home.md\n */\nimport { MetricCard, type MetricCardProps } from \"@elabs-ai/components-ui\";\n\nexport type MetricBlockProps = MetricCardProps;\nexport const MetricBlock = MetricCard;\n","/**\n * Pure ProseMirror insertion commands for the slash menu.\n *\n * The load-bearing logic of the slash menu lives here, deliberately decoupled\n * from Milkdown's context and from React: an `insertBrandDirective` factory that\n * builds the right `brand_container_directive` / `brand_leaf_directive` node (from\n * the schemas in `directive-nodes.ts`) with sensible default attrs + a placeholder\n * body, replaces the active slash range (or inserts at the cursor), and drops the\n * caret into the first editable field. Because it takes the NodeTypes/Schema as\n * arguments (not a Milkdown `Ctx`), it is unit-testable against a hand-built\n * ProseMirror state and its output round-trips losslessly through the editor's\n * existing `toMarkdown` runners (asserted in insert-directive.test.ts).\n *\n * `resolveBrandInsert` is the thin Milkdown-aware adapter: it reads the NodeTypes\n * from the editor `Ctx` and returns the same pure command — so the slash plugin\n * and any direct caller share one implementation.\n */\nimport type { Ctx } from \"@milkdown/kit/ctx\";\nimport { editorViewCtx, parserCtx } from \"@milkdown/kit/core\";\nimport type { Node as ProseNode, NodeType, Schema } from \"@milkdown/kit/prose/model\";\nimport { Fragment } from \"@milkdown/kit/prose/model\";\nimport type { Command, Transaction } from \"@milkdown/kit/prose/state\";\nimport { TextSelection } from \"@milkdown/kit/prose/state\";\n\nimport type { IterationEditHandler } from \"../../markdown-iteration/edit-context\";\nimport {\n builderValueFromParts,\n emptyBuilderValue,\n serializeIterationDirective,\n} from \"../../markdown-iteration/iteration-builder\";\nimport { containerDirectiveSchema, leafDirectiveSchema } from \"../directive-nodes\";\nimport type { BasicBlockId } from \"./brand-slash-commands\";\n\n/** A `[from, to)` document range to replace (the typed `/query`), or `null`. */\nexport interface SlashRange {\n from: number;\n to: number;\n}\n\n/** Default attributes per brand directive — mirror the toolbar snippet shapes. */\nconst DIRECTIVE_DEFAULTS: Record<string, Record<string, string>> = {\n card: { title: \"Title\" },\n callout: { type: \"info\", title: \"Note\" },\n timeline: {},\n metric: { label: \"Label\", value: \"0\", description: \"detail\" },\n iterate: { as: \"item\", layout: \"stacked\" },\n pivot: { layout: \"matrix\" },\n};\n\n/** Per-directive seed body text (the editable template for iterate/pivot). */\nconst DIRECTIVE_BODY_SEED: Record<string, string> = {\n iterate: \"{{item.name}}\",\n pivot: \"{{cell}}\",\n};\n\n/** Brand container directives (`:::name`, editable body). */\nconst CONTAINER_DIRECTIVES = new Set([\"card\", \"callout\", \"timeline\", \"iterate\", \"pivot\"]);\n/** Brand leaf directives (`::name`, atomic). */\nconst LEAF_DIRECTIVES = new Set([\"metric\"]);\n\n/** The directive names the slash menu can insert as brand blocks. */\nexport type InsertableDirective = \"card\" | \"callout\" | \"timeline\" | \"metric\" | \"iterate\" | \"pivot\";\n\n/**\n * Build the default body content for a container directive: a single paragraph\n * (the `content: \"block+\"` schema requires at least one block) carrying a short\n * placeholder so the inserted block is never empty. Timeline seeds a 3-step\n * bullet list (matching the toolbar snippet) when the schema has `bullet_list`.\n */\nfunction containerBody(schema: Schema, name: string): Fragment {\n const paragraph = schema.nodes.paragraph;\n if (name === \"timeline\") {\n const bulletList = schema.nodes.bullet_list;\n const listItem = schema.nodes.list_item;\n if (bulletList && listItem && paragraph) {\n const steps = [\"(done) Step one\", \"(active) Step two\", \"(pending) Step three\"];\n const items = steps.map((text) =>\n listItem.create(null, paragraph.create(null, schema.text(text))),\n );\n return Fragment.from(bulletList.create(null, items));\n }\n }\n // iterate / pivot: seed the editable TEMPLATE body with a token placeholder so\n // the inserted block renders something the moment data is wired.\n const seed = DIRECTIVE_BODY_SEED[name];\n if (seed && paragraph) return Fragment.from(paragraph.create(null, schema.text(seed)));\n\n // card / callout (and timeline fallback): one empty placeholder paragraph\n // (the `content: \"block+\"` schema requires at least one block; the user types\n // the body). If the schema somehow lacks a paragraph, return an empty fragment\n // and let the schema fill its required content.\n if (!paragraph) return Fragment.empty;\n return Fragment.from(paragraph.create());\n}\n\n/**\n * The pure insertion command. Builds the directive node from the given\n * NodeTypes + schema and replaces `range` (or the current selection when\n * `range` is null), then positions the caret in the first editable field\n * (the body's start for containers; just after the atomic leaf for metric).\n *\n * Returns a ProseMirror `Command` `(state, dispatch?) => boolean`.\n */\nexport function insertBrandDirective(\n name: InsertableDirective,\n containerType: NodeType,\n leafType: NodeType,\n schema: Schema,\n range: SlashRange | null,\n): Command {\n return (state, dispatch) => {\n const isContainer = CONTAINER_DIRECTIVES.has(name);\n const isLeaf = LEAF_DIRECTIVES.has(name);\n if (!isContainer && !isLeaf) return false;\n\n const attributes = { ...(DIRECTIVE_DEFAULTS[name] ?? {}) };\n const node = isContainer\n ? containerType.create({ name, attributes }, containerBody(schema, name))\n : leafType.create({ name, attributes });\n if (!node) return false;\n\n if (!dispatch) return true;\n\n const { from, to } = range ?? { from: state.selection.from, to: state.selection.to };\n const tr: Transaction = state.tr.replaceRangeWith(from, to, node);\n\n // Place the caret: containers → first text position inside the body\n // (from + 1 enters the node, +1 more enters its first child block);\n // leaf (atomic) → just after the inserted node.\n const caret = isContainer\n ? Math.min(from + 2, tr.doc.content.size)\n : Math.min(from + node.nodeSize, tr.doc.content.size);\n const sel = TextSelection.near(tr.doc.resolve(caret), 1);\n tr.setSelection(sel).scrollIntoView();\n\n dispatch(tr);\n return true;\n };\n}\n\n/** Map a basic-block id to a ProseMirror command that inserts the native node. */\nexport function insertBasicBlock(\n id: BasicBlockId,\n schema: Schema,\n range: SlashRange | null,\n): Command {\n return (state, dispatch) => {\n const n = schema.nodes;\n const { from, to } = range ?? { from: state.selection.from, to: state.selection.to };\n const tr = state.tr;\n\n const replaceWith = (node: ReturnType<NodeType[\"create\"]> | null, caretOffset: number) => {\n if (!node) return false;\n if (!dispatch) return true;\n tr.replaceRangeWith(from, to, node);\n const caret = Math.min(from + caretOffset, tr.doc.content.size);\n tr.setSelection(TextSelection.near(tr.doc.resolve(caret), 1)).scrollIntoView();\n dispatch(tr);\n return true;\n };\n\n switch (id) {\n case \"heading\":\n return replaceWith(n.heading?.create({ level: 2 }) ?? null, 1);\n case \"bullet-list\": {\n const li = n.list_item?.create(null, n.paragraph?.create());\n return replaceWith(li ? (n.bullet_list?.create(null, li) ?? null) : null, 2);\n }\n case \"ordered-list\": {\n const li = n.list_item?.create(null, n.paragraph?.create());\n return replaceWith(li ? (n.ordered_list?.create(null, li) ?? null) : null, 2);\n }\n case \"quote\":\n return replaceWith(n.blockquote?.create(null, n.paragraph?.create()) ?? null, 2);\n case \"code\":\n return replaceWith(n.code_block?.create() ?? null, 1);\n case \"divider\": {\n const hr = n.hr ?? n.horizontal_rule;\n if (!hr) return false;\n if (!dispatch) return true;\n // A divider is atomic: insert it, then add a trailing empty paragraph so\n // the caret has somewhere to land below the rule.\n const node = hr.create();\n tr.replaceRangeWith(from, to, node);\n const para = n.paragraph?.create();\n if (para) tr.insert(from + node.nodeSize, para);\n const caret = Math.min(from + node.nodeSize + 1, tr.doc.content.size);\n tr.setSelection(TextSelection.near(tr.doc.resolve(caret), 1)).scrollIntoView();\n dispatch(tr);\n return true;\n }\n default:\n return false;\n }\n };\n}\n\n/** Seed body for a fresh ```calc fence — a tiny ledger so highlight + inlays show at once. */\nexport const CALC_FENCE_SEED = [\"items = 3\", \"price = 4.50\", \"total = items * price\"].join(\"\\n\");\n\n/**\n * Insert a ```calc fence (a `code_block` with `language: \"calc\"`) seeded with a\n * short example, then drop the caret into the body. The editor's calc layer\n * (`calc-editor-prose`) highlights + adds result inlays once `calc` hooks are wired;\n * with none it is just a fenced code block (and renders as a `CalcBlock` in preview\n * when `evaluate` is supplied) — so inserting one is always safe.\n */\nexport function insertCalcFence(\n schema: Schema,\n range: SlashRange | null,\n seed: string = CALC_FENCE_SEED,\n): Command {\n return (state, dispatch) => {\n const codeBlock = schema.nodes.code_block;\n if (!codeBlock) return false;\n const node = seed\n ? codeBlock.create({ language: \"calc\" }, schema.text(seed))\n : codeBlock.create({ language: \"calc\" });\n if (!node) return false;\n if (!dispatch) return true;\n\n const { from, to } = range ?? { from: state.selection.from, to: state.selection.to };\n const tr = state.tr.replaceRangeWith(from, to, node);\n // Caret at the body start (from + 1 enters the code block's text).\n const caret = Math.min(from + 1, tr.doc.content.size);\n tr.setSelection(TextSelection.near(tr.doc.resolve(caret), 1)).scrollIntoView();\n dispatch(tr);\n return true;\n };\n}\n\n/** Milkdown-aware adapter for {@link insertCalcFence}. */\nexport function resolveCalcInsert(ctx: Ctx, range: SlashRange | null): boolean {\n const view = ctx.get(editorViewCtx);\n const command = insertCalcFence(view.state.schema, range);\n return command(view.state, view.dispatch.bind(view));\n}\n\n/**\n * Milkdown-aware adapter: resolve the directive NodeTypes from the editor `Ctx`\n * and run {@link insertBrandDirective} against the live view. Used by the slash\n * plugin's command runners.\n */\nexport function resolveBrandInsert(\n ctx: Ctx,\n name: InsertableDirective,\n range: SlashRange | null,\n): boolean {\n const view = ctx.get(editorViewCtx);\n const containerType = containerDirectiveSchema.type(ctx);\n const leafType = leafDirectiveSchema.type(ctx);\n const schema = view.state.schema;\n const command = insertBrandDirective(name, containerType, leafType, schema, range);\n return command(view.state, view.dispatch.bind(view));\n}\n\n/** Milkdown-aware adapter for the basic native blocks. */\nexport function resolveBasicInsert(ctx: Ctx, id: BasicBlockId, range: SlashRange | null): boolean {\n const view = ctx.get(editorViewCtx);\n const command = insertBasicBlock(id, view.state.schema, range);\n return command(view.state, view.dispatch.bind(view));\n}\n\n/**\n * Parse a FULLY-BOUND directive markdown string (e.g. from\n * `serializeIterationDirective`) and insert it at `range` (or the caret when\n * `range` is null) — the insertion counterpart to the node-view's\n * `writeBodyMarkdown` edit-in-place. Used by {@link resolveGuidedIterationInsert}\n * (#223). Never throws; returns `false` when the markdown fails to parse.\n */\nexport function insertParsedMarkdown(\n ctx: Ctx,\n markdown: string,\n range: SlashRange | null,\n): boolean {\n try {\n const view = ctx.get(editorViewCtx);\n const parse = (ctx as { get: (t: unknown) => (md: string) => ProseNode | null }).get(parserCtx);\n const parsed = parse(markdown);\n if (!parsed) return false;\n\n const { from, to } = range ?? { from: view.state.selection.from, to: view.state.selection.to };\n const tr = view.state.tr.replaceWith(from, to, parsed.content);\n const caret = Math.min(from + 2, tr.doc.content.size);\n tr.setSelection(TextSelection.near(tr.doc.resolve(caret), 1)).scrollIntoView();\n view.dispatch(tr);\n return true;\n } catch {\n // Parser unavailable / stale range — leave the document untouched.\n return false;\n }\n}\n\n/**\n * The GUIDED `/iterate` `/pivot` insert path (#223): instead of inserting a\n * bare directive immediately, emit an edit REQUEST — seeded blank\n * (`emptyBuilderValue`) — through the consumer's `IterationEditContext`\n * handler, opening whichever modal it wires up (the node-view `⋯` re-edit's\n * exact mechanism, reused for a fresh insert). BOTH writers are supplied so\n * this works with either provider:\n * - `onSaveData` (the guided `IterationBuilderProvider`) — inserts the\n * fully-bound directive (value lists + layout + template).\n * - `onSave` (the template-only `IterationTemplateProvider`) — inserts a\n * directive built from DEFAULT (empty) attributes + the edited template.\n * Nothing is INSERTED until the modal actually saves, so Cancel leaves no\n * directive behind — unlike the direct-insert fallback, which always drops a\n * placeholder template immediately. The typed `/query` trigger text itself is\n * consumed up front by the caller (`runSlashCommand` in `brand-slash-plugin.ts`,\n * BEFORE this resolver runs) — that's why `range` arrives as `null` here: by\n * the time this fires, the range has already been deleted and the caret sits\n * where the query used to start, so a save inserts there and a Cancel leaves a\n * clean caret rather than the literal `/query` text.\n */\nexport function resolveGuidedIterationInsert(\n ctx: Ctx,\n kind: \"iterate\" | \"pivot\",\n range: SlashRange | null,\n handler: IterationEditHandler,\n): void {\n const seed = emptyBuilderValue(kind);\n handler({\n kind,\n template: seed.template,\n attributes: {},\n onSave: (template) => {\n const value = builderValueFromParts(kind, {}, template);\n insertParsedMarkdown(ctx, serializeIterationDirective(value), range);\n },\n onSaveData: ({ attributes, template }) => {\n const value = builderValueFromParts(kind, attributes, template);\n insertParsedMarkdown(ctx, serializeIterationDirective(value), range);\n },\n });\n}\n\n/** Exported for the unit test: the per-directive default attributes. */\nexport const __DIRECTIVE_DEFAULTS = DIRECTIVE_DEFAULTS;\n\n/** Re-export for callers that need to know which names are containers vs leaves. */\nexport function isContainerDirective(name: string): boolean {\n return CONTAINER_DIRECTIVES.has(name);\n}\nexport function isLeafDirective(name: string): boolean {\n return LEAF_DIRECTIVES.has(name);\n}\n","/**\n * WYSIWYG support for the brand `:::` directives inside the Milkdown editor.\n *\n * Adds remark-directive to Milkdown's pipeline (so `:::card` / `:::callout` /\n * `:::timeline` / `::metric` PARSE and SERIALIZE — lossless round-trip), plus two\n * generic ProseMirror node schemas:\n * - a CONTAINER node (editable body) for `:::` block directives\n * - a LEAF node (atomic) for `::` directives like `::metric`\n *\n * Rendering is ProseMirror-native `toDOM` chrome styled from semantic tokens\n * (see markdown-editor.css) — deliberately NOT a React node-view adapter, so the\n * editor has zero extra runtime deps and the chrome stays token-driven. The\n * branded PREVIEW still renders the real @brand React components; this gives the\n * WYSIWYG surface a matching, editable representation.\n */\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport { $nodeSchema, $remark } from \"@milkdown/kit/utils\";\nimport remarkDirective from \"remark-directive\";\n\ninterface DirectiveMdast {\n type: string;\n name?: string;\n attributes?: Record<string, string> | null;\n children?: unknown[];\n}\n\n/** Add remark-directive to Milkdown's unified processor (parse + stringify). */\nexport const directiveRemark = $remark(\"brandDirective\", () => remarkDirective);\n\n/** `:::name` block directives → an editable container node with branded chrome. */\nexport const containerDirectiveSchema = $nodeSchema(\"brand_container_directive\", () => ({\n content: \"block+\",\n group: \"block\",\n defining: true,\n attrs: {\n name: { default: \"card\" },\n attributes: { default: {} as Record<string, string> },\n },\n parseDOM: [\n {\n tag: \"div[data-brand-directive]\",\n getAttrs: (dom: HTMLElement | string) => {\n if (typeof dom === \"string\") return false;\n return {\n name: dom.getAttribute(\"data-brand-directive\") ?? \"card\",\n attributes: JSON.parse(dom.getAttribute(\"data-brand-attrs\") ?? \"{}\"),\n };\n },\n },\n ],\n toDOM: (node) => {\n const name = String(node.attrs.name);\n const attrs = (node.attrs.attributes ?? {}) as Record<string, string>;\n const heading = attrs.title || (name === \"callout\" ? (attrs.type ?? \"note\") : \"\");\n const chrome: unknown[] = [\n \"div\",\n {\n \"data-brand-directive\": name,\n \"data-brand-attrs\": JSON.stringify(attrs),\n class: `brand-directive brand-directive--${name}`,\n \"data-callout-type\": name === \"callout\" ? (attrs.type ?? \"note\") : null,\n },\n ];\n if (heading)\n chrome.push([\"div\", { class: \"brand-directive__title\", contenteditable: \"false\" }, heading]);\n chrome.push([\"div\", { class: \"brand-directive__body\" }, 0]);\n return chrome as never;\n },\n parseMarkdown: {\n match: (node) => (node as DirectiveMdast).type === \"containerDirective\",\n runner: (state, node, type) => {\n const d = node as DirectiveMdast;\n state.openNode(type, { name: d.name ?? \"card\", attributes: d.attributes ?? {} });\n state.next((d.children ?? []) as never);\n state.closeNode();\n },\n },\n toMarkdown: {\n match: (node) => node.type.name === \"brand_container_directive\",\n runner: (state, node) => {\n state.openNode(\"containerDirective\", undefined, {\n name: node.attrs.name,\n attributes: node.attrs.attributes,\n });\n state.next(node.content);\n state.closeNode();\n },\n },\n}));\n\n/** `::name` leaf directives (e.g. `::metric`) → an atomic node with branded chrome. */\nexport const leafDirectiveSchema = $nodeSchema(\"brand_leaf_directive\", () => ({\n group: \"block\",\n atom: true,\n isolating: true,\n attrs: {\n name: { default: \"metric\" },\n attributes: { default: {} as Record<string, string> },\n },\n parseDOM: [\n {\n tag: \"div[data-brand-leaf]\",\n getAttrs: (dom: HTMLElement | string) => {\n if (typeof dom === \"string\") return false;\n return {\n name: dom.getAttribute(\"data-brand-leaf\") ?? \"metric\",\n attributes: JSON.parse(dom.getAttribute(\"data-brand-attrs\") ?? \"{}\"),\n };\n },\n },\n ],\n toDOM: (node) => {\n const name = String(node.attrs.name);\n const a = (node.attrs.attributes ?? {}) as Record<string, string>;\n return [\n \"div\",\n {\n \"data-brand-leaf\": name,\n \"data-brand-attrs\": JSON.stringify(a),\n class: `brand-directive brand-directive--leaf brand-directive--${name}`,\n contenteditable: \"false\",\n },\n [\"div\", { class: \"brand-metric__label\" }, a.label ?? name],\n [\"div\", { class: \"brand-metric__value\" }, a.value ?? \"\"],\n ...(a.description ? [[\"div\", { class: \"brand-metric__desc\" }, a.description]] : []),\n ] as never;\n },\n parseMarkdown: {\n match: (node) => (node as DirectiveMdast).type === \"leafDirective\",\n runner: (state, node, type) => {\n const d = node as DirectiveMdast;\n state.addNode(type, { name: d.name ?? \"metric\", attributes: d.attributes ?? {} });\n },\n },\n toMarkdown: {\n match: (node) => node.type.name === \"brand_leaf_directive\",\n runner: (state, node) => {\n state.addNode(\"leafDirective\", undefined, undefined, {\n name: node.attrs.name,\n attributes: node.attrs.attributes,\n });\n },\n },\n}));\n\n/** All plugins needed to parse, render and serialize brand directives in the editor. */\nexport const directivePlugins: MilkdownPlugin[] = [\n directiveRemark,\n containerDirectiveSchema,\n leafDirectiveSchema,\n].flat() as MilkdownPlugin[];\n","/**\n * The brand slash-command registry — pure, typed, tree-shakeable data.\n *\n * Each command is a label + group + match keywords + an icon + a `run` that\n * inserts the corresponding block at the caret. `run` is given a\n * {@link SlashInsertContext} (the Milkdown `Ctx` + the active `/query` range);\n * brand directives delegate to `resolveBrandInsert`, basic blocks to\n * `resolveBasicInsert` — so the SAME registry drives the menu and any\n * programmatic insert, and the load-bearing insertion logic stays in\n * `insert-directive.ts` (unit-tested there).\n *\n * Defaults: card · callout · metric · timeline (\"Brand blocks\") +\n * heading · bullet-list · ordered-list · quote · code · divider (\"Basic\").\n * Consumers can extend or replace the list via the `slashMenu` prop.\n *\n * `runInSource` (#299) is an OPTIONAL, ADDITIVE second handler for the Monaco\n * source pane — `run` (Milkdown) stays untouched, so the 12 default commands\n * need zero migration. `MonacoCodeEditor` / `IRange` / `EditorContentAccess`\n * below are TYPE-ONLY imports (erased at build) — this file stays free of any\n * `monaco-editor` RUNTIME import, so the Milkdown-facing `slash/index.ts`\n * barrel this file feeds still pulls zero Monaco code.\n */\nimport type { Ctx } from \"@milkdown/kit/ctx\";\nimport {\n Calculator,\n Grid3x3,\n Heading2,\n List,\n ListOrdered,\n Minus,\n Quote,\n Repeat2,\n SquareCode,\n type LucideIcon,\n} from \"lucide-react\";\nimport type { IRange } from \"monaco-editor\";\nimport { createElement, type ReactNode } from \"react\";\n\nimport type { MonacoCodeEditor } from \"../../code-editor\";\nimport type { EditorContentAccess } from \"../../lib/editor-content-access\";\nimport type { IterationEditHandler } from \"../../markdown-iteration/edit-context\";\nimport {\n CALC_FENCE_SEED,\n resolveBasicInsert,\n resolveBrandInsert,\n resolveCalcInsert,\n resolveGuidedIterationInsert,\n type InsertableDirective,\n type SlashRange,\n} from \"./insert-directive\";\n\n/** The native (non-directive) blocks the slash menu can insert. */\nexport type BasicBlockId =\n | \"heading\"\n | \"bullet-list\"\n | \"ordered-list\"\n | \"quote\"\n | \"code\"\n | \"divider\";\n\n/**\n * Context handed to a {@link SlashCommand.run}. `ctx` is the live Milkdown editor\n * context; `range` is the `[from, to)` document range of the typed trigger +\n * query (so the command replaces it), or `null` to insert at the caret.\n */\nexport interface SlashInsertContext {\n ctx: Ctx;\n range: SlashRange | null;\n}\n\n/**\n * Context handed to a {@link SlashCommand.runInSource} (#299) — the Monaco\n * SOURCE-pane counterpart to {@link SlashInsertContext}. By the time this runs,\n * the typed `/query` trigger has already been stripped from the document (the\n * same edit `cancel` uses), so the handler starts from a clean doc.\n */\nexport interface SourceSlashContext {\n /** The raw Monaco editor instance — full imperative access when needed. */\n editor: MonacoCodeEditor;\n /**\n * Model range the typed `/query` trigger occupied (already removed by the\n * time this fires) — `null` on the hotkey path, where no `/` was ever typed.\n */\n range: IRange | null;\n /** Engine-agnostic content access (get/replace-selection/insert-at-cursor). */\n content: EditorContentAccess;\n}\n\n/** One entry in the slash menu. Pure data + an insertion `run`. */\nexport interface SlashCommand {\n /** Stable id (used as the React key + `aria-activedescendant` target). */\n id: string;\n /** Visible label, e.g. \"Card\". */\n label: string;\n /** Grouping header, e.g. \"Brand blocks\" | \"Basic\". */\n group?: string;\n /** A short description shown under the label. */\n description?: string;\n /** Extra fuzzy-match aids beyond the label (matched case-insensitively). */\n keywords?: string[];\n /** Leading icon (a Lucide glyph or any node). */\n icon?: ReactNode;\n /**\n * Markdown SNIPPET the source / split toolbar's Insert menu inserts (text mode).\n * Optional: commands without one — the basic blocks, which already have their\n * own toolbar buttons — don't appear in that menu. Brand blocks set this so the\n * source toolbar inserts the SAME directive / fence the WYSIWYG `run` builds, so\n * the two surfaces stay at parity. (A4)\n */\n snippet?: string;\n /** Insert the block at the caret. WYSIWYG (Milkdown) — UNCHANGED (#299). */\n run: (ctx: SlashInsertContext) => void;\n /**\n * OPTIONAL source-pane (Monaco) handler (#299) — for a RUN-ONLY command (no\n * `snippet`, e.g. an \"Ask AI\" entry) that can't express itself as a markdown\n * insert. When present, selecting the command in the Monaco source-pane menu\n * (`MonacoSlashMenu`) strips the typed `/query` first, then calls this with\n * the live editor + the (now-stripped) trigger range + engine-agnostic\n * content access — instead of the `snippet`-only `commitSnippet` fallback. A\n * command needs only ONE of `snippet` / `runInSource` to appear in source (both\n * is fine too — `runInSource` wins there since it's the more capable handler).\n */\n runInSource?: (ctx: SourceSlashContext) => void;\n /**\n * OPTIONAL guided variant (#223, WYSIWYG only). When present AND the\n * consumer has wired an `IterationEditContext` handler (an\n * `IterationBuilderProvider` / `IterationTemplateProvider` above the\n * editor), this REPLACES `run` — it opens the guided modal seeded blank\n * instead of inserting a bare directive. Falls back to `run` when no\n * handler is available, so the 12 non-guided default commands need zero\n * changes. Set on the `iterate` / `pivot` commands only.\n */\n guided?: (insert: SlashInsertContext, handler: IterationEditHandler) => void;\n}\n\n/** Build a Lucide icon element at the menu's standard size. */\nfunction glyph(icon: LucideIcon): ReactNode {\n return createElement(icon, { className: \"size-4\", \"aria-hidden\": \"true\" });\n}\n\n/**\n * Source-mode markdown snippet per brand directive — the text the Insert menu\n * drops into the Monaco pane. Mirrors the WYSIWYG insert (the `DIRECTIVE_DEFAULTS`\n * attrs + body seeds in `insert-directive.ts`) so both surfaces produce the same\n * `:::` markdown.\n */\nconst DIRECTIVE_SNIPPET: Record<InsertableDirective, string> = {\n card: `:::card{title=\"Title\"}\\nContent\\n:::`,\n callout: `:::callout{type=\"info\" title=\"Note\"}\\nMessage\\n:::`,\n metric: `::metric{label=\"Label\" value=\"0\" description=\"detail\"}`,\n timeline: `:::timeline\\n- (done) Step one\\n- (active) Step two\\n- (pending) Step three\\n:::`,\n iterate: `:::iterate{as=\"item\" layout=\"stacked\"}\\n{{item.name}}\\n:::`,\n pivot: `:::pivot{layout=\"matrix\"}\\n{{cell}}\\n:::`,\n};\n\n/** Directives with a guided authoring modal (#223) — see {@link SlashCommand.guided}. */\nconst GUIDED_DIRECTIVES = new Set<InsertableDirective>([\"iterate\", \"pivot\"]);\n\n/** A brand-directive command (card/callout/metric/timeline/iterate/pivot). */\nfunction brandCommand(\n name: InsertableDirective,\n label: string,\n description: string,\n keywords: string[],\n icon: ReactNode,\n): SlashCommand {\n const command: SlashCommand = {\n id: `brand-${name}`,\n label,\n group: \"Brand blocks\",\n description,\n keywords,\n icon,\n snippet: DIRECTIVE_SNIPPET[name],\n run: ({ ctx, range }) => resolveBrandInsert(ctx, name, range),\n };\n if (GUIDED_DIRECTIVES.has(name)) {\n command.guided = ({ ctx, range }, handler) =>\n resolveGuidedIterationInsert(ctx, name as \"iterate\" | \"pivot\", range, handler);\n }\n return command;\n}\n\n/** A basic native-block command. */\nfunction basicCommand(\n id: BasicBlockId,\n label: string,\n description: string,\n keywords: string[],\n icon: LucideIcon,\n): SlashCommand {\n return {\n id: `basic-${id}`,\n label,\n group: \"Basic\",\n description,\n keywords,\n icon: glyph(icon),\n run: ({ ctx, range }) => resolveBasicInsert(ctx, id, range),\n };\n}\n\n/**\n * The default command list. Brand blocks first (the wedge), then the basics.\n * The brand-block icons reuse the `data-brand-directive` chrome look via Lucide\n * stand-ins (the editor renders the real component once inserted).\n */\nexport const BRAND_SLASH_COMMANDS: SlashCommand[] = [\n brandCommand(\n \"card\",\n \"Card\",\n \"A titled content card\",\n [\"card\", \"panel\", \"box\", \"section\"],\n glyph(SquareCode),\n ),\n brandCommand(\n \"callout\",\n \"Callout\",\n \"A highlighted note / alert\",\n [\"callout\", \"alert\", \"note\", \"info\", \"warning\", \"tip\"],\n glyph(Quote),\n ),\n brandCommand(\n \"metric\",\n \"Metric\",\n \"A single KPI value\",\n [\"metric\", \"kpi\", \"stat\", \"number\", \"value\"],\n glyph(Minus),\n ),\n brandCommand(\n \"timeline\",\n \"Timeline\",\n \"A list of steps with status\",\n [\"timeline\", \"steps\", \"milestones\", \"roadmap\", \"progress\"],\n glyph(List),\n ),\n brandCommand(\n \"iterate\",\n \"Iterate\",\n \"Repeat a template per data row\",\n [\"iterate\", \"repeat\", \"loop\", \"for-each\", \"foreach\", \"map\", \"list\", \"template\"],\n glyph(Repeat2),\n ),\n brandCommand(\n \"pivot\",\n \"Pivot\",\n \"A row × column cross-tab\",\n [\"pivot\", \"matrix\", \"cross-tab\", \"crosstab\", \"table\", \"grid\"],\n glyph(Grid3x3),\n ),\n {\n // Inserts a ```calc fence (not a `:::` directive), so it runs its own command\n // rather than `resolveBrandInsert`. Highlight + result inlays come from the\n // editor's calc hooks; the fence is harmless when none are wired.\n id: \"brand-calc\",\n label: \"Calc\",\n group: \"Brand blocks\",\n description: \"A live calculation block\",\n keywords: [\"calc\", \"calculation\", \"math\", \"formula\", \"sum\", \"ledger\", \"budget\"],\n icon: glyph(Calculator),\n snippet: [\"```calc\", CALC_FENCE_SEED, \"```\"].join(\"\\n\"),\n run: ({ ctx, range }) => resolveCalcInsert(ctx, range),\n },\n basicCommand(\"heading\", \"Heading\", \"Section heading\", [\"heading\", \"title\", \"h2\"], Heading2),\n basicCommand(\n \"bullet-list\",\n \"Bullet list\",\n \"An unordered list\",\n [\"bullet\", \"list\", \"unordered\", \"ul\"],\n List,\n ),\n basicCommand(\n \"ordered-list\",\n \"Numbered list\",\n \"An ordered list\",\n [\"numbered\", \"ordered\", \"list\", \"ol\"],\n ListOrdered,\n ),\n basicCommand(\"quote\", \"Quote\", \"A block quotation\", [\"quote\", \"blockquote\", \"citation\"], Quote),\n basicCommand(\n \"code\",\n \"Code block\",\n \"A fenced code block\",\n [\"code\", \"snippet\", \"pre\", \"fence\"],\n SquareCode,\n ),\n basicCommand(\n \"divider\",\n \"Divider\",\n \"A horizontal rule\",\n [\"divider\", \"rule\", \"hr\", \"separator\"],\n Minus,\n ),\n];\n\n/**\n * Filter a command list by a query (case-insensitive substring over label +\n * keywords). An empty query returns the full list (in registry order).\n */\nexport function filterSlashCommands(commands: SlashCommand[], query: string): SlashCommand[] {\n const q = query.trim().toLowerCase();\n if (!q) return commands;\n return commands.filter((c) => {\n if (c.label.toLowerCase().includes(q)) return true;\n return (c.keywords ?? []).some((k) => k.toLowerCase().includes(q));\n });\n}\n\n/** Preserve registry order while grouping; used by the menu to render headers. */\nexport function groupSlashCommands(\n commands: SlashCommand[],\n): { group: string; commands: SlashCommand[] }[] {\n const order: string[] = [];\n const byGroup = new Map<string, SlashCommand[]>();\n for (const c of commands) {\n const g = c.group ?? \"Other\";\n if (!byGroup.has(g)) {\n byGroup.set(g, []);\n order.push(g);\n }\n byGroup.get(g)!.push(c);\n }\n return order.map((group) => ({ group, commands: byGroup.get(group)! }));\n}\n","\"use client\";\n\n/**\n * SlashMenu — the branded popup the slash plugin renders at the caret.\n *\n * Presentational + controlled: the ProseMirror plugin owns the keyboard\n * (↑/↓/Enter/Esc) and the open/query/active state, so this component just renders\n * the filtered, grouped commands and exposes `onSelect`. It is a token-styled\n * `listbox`/`option` list (NOT `@elabs-ai/components-ui` `Command`/cmdk): a cmdk instance would\n * fight the editor for DOM focus and arrow-key handling, since the caret must stay\n * in the ProseMirror `textbox` while the user types `/query`. The plugin keeps the\n * active option in view and wires `aria-activedescendant` on the editor surface.\n *\n * Semantic tokens only; matches the `Command` popover look (bg-popover, accent\n * selection, muted group headings).\n */\nimport { useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { forwardRef, type HTMLAttributes } from \"react\";\n\nimport { groupSlashCommands, type SlashCommand } from \"./brand-slash-commands\";\n\nexport interface SlashMenuProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n /** The (already filtered) commands to show. */\n commands: SlashCommand[];\n /** The id of the active (highlighted) command, for `aria-selected`. */\n activeId?: string;\n /** Called when a command is chosen (click or via the plugin's Enter). */\n onSelect: (command: SlashCommand) => void;\n /** DOM id prefix so each option id is stable + unique (matches `aria-activedescendant`). */\n idPrefix?: string;\n /** Shown when the query matches nothing. */\n emptyLabel?: string;\n}\n\n/** Build the stable DOM id for a command's option element. */\nexport function slashOptionId(idPrefix: string, commandId: string): string {\n return `${idPrefix}-${commandId}`;\n}\n\nexport const SlashMenu = forwardRef<HTMLDivElement, SlashMenuProps>(function SlashMenu(\n {\n commands,\n activeId,\n onSelect,\n idPrefix = \"brand-slash\",\n emptyLabel: emptyLabelProp,\n className,\n ...props\n },\n ref,\n) {\n const { t } = useLocale();\n const emptyLabel = emptyLabelProp ?? t(\"editor.slashMenu.noMatchingBlocks\");\n const groups = groupSlashCommands(commands);\n\n return (\n <div\n ref={ref}\n // The editor surface carries `role=\"textbox\"`; this list is its popup. The\n // plugin sets `aria-controls`/`aria-activedescendant` on the textbox.\n role=\"listbox\"\n aria-label={t(\"editor.slashMenu.insertBlock\")}\n className={cn(\n \"max-h-[min(320px,60vh)] w-72 overflow-y-auto overflow-x-hidden rounded-md bg-popover p-1 text-popover-foreground shadow-ring-md\",\n className,\n )}\n {...props}\n >\n {commands.length === 0 ? (\n // A `role=\"listbox\"` may only own `option`/`group` children (WCAG 1.3.1,\n // #157): a bare `<div>` here is an unannounced, dangling message — an\n // AT user who types a query matching nothing gets silence. Shaping it\n // as a disabled, unselectable option keeps the listbox contract AND\n // makes the message reachable by an accessible-name query.\n <div\n role=\"option\"\n aria-disabled=\"true\"\n aria-selected=\"false\"\n className=\"cursor-default select-none px-2 py-6 text-center text-caption text-muted-foreground\"\n >\n {emptyLabel}\n </div>\n ) : (\n groups.map(({ group, commands: groupCommands }) => (\n <div key={group} role=\"group\" aria-label={group} className=\"overflow-hidden p-1\">\n <div className=\"px-2 py-1.5 text-meta font-medium text-muted-foreground\">{group}</div>\n {groupCommands.map((command) => {\n const selected = command.id === activeId;\n return (\n <div\n key={command.id}\n id={slashOptionId(idPrefix, command.id)}\n role=\"option\"\n aria-selected={selected}\n data-selected={selected ? \"true\" : undefined}\n // The editor keeps focus, so select on mousedown (before the\n // editor would steal focus back) and don't let the click move\n // the selection out of the document.\n onMouseDown={(e) => {\n e.preventDefault();\n onSelect(command);\n }}\n className={cn(\n \"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-body outline-none transition-colors duration-fast\",\n \"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground\",\n )}\n >\n {command.icon ? (\n <span className=\"flex size-5 shrink-0 items-center justify-center text-muted-foreground [&_svg]:size-4\">\n {command.icon}\n </span>\n ) : null}\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{command.label}</span>\n {command.description ? (\n <span className=\"truncate text-meta text-muted-foreground\">\n {command.description}\n </span>\n ) : null}\n </span>\n </div>\n );\n })}\n </div>\n ))\n )}\n </div>\n );\n});\n","\"use client\";\n\n/**\n * The brand slash-menu Milkdown plugin (the ProseMirror half).\n *\n * A self-owned raw ProseMirror plugin (`$prose`) — NOT `@milkdown/kit/plugin/slash`\n * — so the editor pulls zero new dependencies and we keep full control of the\n * interaction. It owns the STATE and the KEYBOARD; the React widget\n * (slash-widget.tsx) owns the rendered popup. They share one\n * {@link SlashController} (the command list + a Milkdown `Ctx` getter + the\n * insert/close helpers), built once in `MarkdownEditorView` so both the plugin's\n * Enter handler and the widget's click handler run a command the SAME way.\n *\n * Responsibilities here:\n * 1. Detect a `/` typed at a textblock START or after whitespace; track\n * `{ active, from, query, index }` as the user keeps typing (a literal `/`\n * mid-word is never hijacked; a space ends the query).\n * 2. Render the React `<SlashMenu>` as a ProseMirror WIDGET decoration at the\n * caret (via the `useWidgetViewFactory()` factory passed in).\n * 3. ↑/↓ move the selection, Enter inserts the active command (replacing the\n * `/query` range), Esc / deleting the `/` closes.\n */\nimport type { Ctx, MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport { $prose } from \"@milkdown/kit/utils\";\nimport type { ReactWidgetViewComponent, useWidgetViewFactory } from \"@prosemirror-adapter/react\";\nimport type { Node as ProseNode } from \"@milkdown/kit/prose/model\";\nimport type { Transaction } from \"@milkdown/kit/prose/state\";\nimport { Plugin, PluginKey } from \"@milkdown/kit/prose/state\";\nimport type { EditorView } from \"@milkdown/kit/prose/view\";\nimport { DecorationSet } from \"@milkdown/kit/prose/view\";\n\nimport type { IterationEditHandler } from \"../../markdown-iteration/edit-context\";\nimport { filterSlashCommands, type SlashCommand } from \"./brand-slash-commands\";\nimport { matchesKeyboardEvent } from \"./shortcut\";\n\n/**\n * The factory `useWidgetViewFactory()` returns: `(options) => (pos, spec?) =>\n * Decoration`. We borrow the exact type from the hook so we never name the\n * (un-re-exported) `@prosemirror-adapter/core` internals.\n */\nexport type SlashWidgetFactory = ReturnType<typeof useWidgetViewFactory>;\n\n/** Plugin state. `active === false` means the menu is closed. */\nexport interface SlashPluginState {\n active: boolean;\n /** Document position of the trigger `/` (char mode) or the caret (shortcut mode). */\n from: number;\n /** Text typed after the `/` (the filter query). */\n query: string;\n /** Index into the CURRENTLY-FILTERED list of the highlighted command. */\n index: number;\n /**\n * How the menu was opened:\n * - `\"char\"` — the user typed the trigger character (default); the `from`\n * position holds the `/`, which is deleted when the range is inserted.\n * - `\"shortcut\"` — opened programmatically via the keyboard shortcut; `from`\n * is the bare caret position (no leading `/`), so `range` must be `null`\n * on insert (insert-at-caret, delete nothing).\n */\n triggered: \"char\" | \"shortcut\";\n}\n\n/** The closed (inactive) plugin state. Exported for unit tests; not barreled. */\nexport const CLOSED: SlashPluginState = {\n active: false,\n from: 0,\n query: \"\",\n index: 0,\n triggered: \"char\",\n};\n\nexport const slashPluginKey = new PluginKey<SlashPluginState>(\"brand-slash\");\n\n/**\n * The shared controller. Built once in React so the plugin (keyboard) and the\n * widget (mouse) run commands identically with the live Milkdown `Ctx`. The\n * `ctx` is captured by the plugin's `$prose((ctx) => …)` callback (see\n * {@link brandSlashPlugin}) and read back through `getCtx()`.\n */\nexport interface SlashController {\n /** The command list driving both the menu and the keyboard. */\n commands: SlashCommand[];\n /** The trigger character (default `\"/\"`). */\n trigger: string;\n /**\n * Optional keyboard shortcut string (e.g. `\"Mod-/\"`) that opens the menu\n * programmatically at the caret (shortcut mode, no leading `/` in the doc).\n * Parsed by `matchesKeyboardEvent` in `handleKeyDown`.\n */\n shortcut?: string;\n /** Resolve the live Milkdown `Ctx` (set by the plugin at build time). */\n getCtx: () => Ctx;\n /** Internal: the plugin assigns the captured `Ctx` here. */\n _ctx?: Ctx;\n /**\n * Programmatically open the slash menu at the current caret position.\n * Dispatches the `\"open\"` meta — equivalent to pressing the shortcut key.\n * No-op if no ProseMirror view is available yet.\n */\n openMenu?: (view: EditorView) => void;\n /**\n * Resolve the LIVE `IterationEditContext` handler (#223) — read lazily (a\n * ref-backed getter) so a fresh Provider identity each render never needs the\n * controller (and its editor) to be rebuilt. Set by `MarkdownEditorView`;\n * returns `null`/`undefined` when the consumer hasn't wired one, in which case\n * `runSlashCommand` falls back to a command's plain `run`.\n */\n getIterationEditHandler?: () => IterationEditHandler | null | undefined;\n}\n\n/** Create a {@link SlashController} with a settable, lazily-read `Ctx`. */\nexport function createSlashController(\n commands: SlashCommand[],\n trigger = \"/\",\n shortcut?: string,\n getIterationEditHandler?: () => IterationEditHandler | null | undefined,\n): SlashController {\n const controller: SlashController = {\n commands,\n trigger,\n shortcut,\n getIterationEditHandler,\n getCtx: () => {\n if (!controller._ctx) {\n throw new Error(\"Brand slash controller used before the editor Ctx was captured.\");\n }\n return controller._ctx;\n },\n openMenu: (view: EditorView) => {\n const pos = view.state.selection.from;\n view.dispatch(\n view.state.tr.setMeta(slashPluginKey, {\n _open: true,\n from: pos,\n triggered: \"shortcut\",\n }),\n );\n },\n };\n return controller;\n}\n\n/** Compute the `[from, to)` range covering the trigger + query (to be replaced). */\nexport function slashRange(state: SlashPluginState): { from: number; to: number } {\n return { from: state.from, to: state.from + 1 + state.query.length };\n}\n\n/** Run a command: insert (replacing the `/query` range or at caret), then close the menu. */\nexport function runSlashCommand(\n view: EditorView,\n controller: SlashController,\n state: SlashPluginState,\n command: SlashCommand,\n): void {\n // Char mode: replace the whole `/query` run (slashRange covers the `/` + query).\n // Shortcut mode: there is no leading `/`, but any filter the user typed IS real\n // document text [from, from+query.length) — replace that span so the inserted\n // block lands at `from` with the typed query removed (empty query → insert at\n // caret, delete nothing). NOT range:null, which would leave the query behind.\n const range =\n state.triggered === \"shortcut\"\n ? { from: state.from, to: state.from + state.query.length }\n : slashRange(state);\n // #223: a `guided` command (currently `/iterate` `/pivot`) opens its modal\n // instead of inserting a bare directive — but ONLY when the consumer has\n // actually wired an `IterationEditContext` handler; with none, fall back to\n // the plain `run` (today's direct-insert behaviour, unchanged).\n const iterationHandler = controller.getIterationEditHandler?.();\n if (command.guided && iterationHandler) {\n // The guided modal doesn't dispatch an insert transaction until it SAVES\n // (Cancel must leave the document untouched) — so the typed `/query` range\n // has to be consumed NOW, or it is left behind as literal text forever\n // when the user cancels. Delete it up front (collapsing the selection to\n // `range.from`) and hand the resolver `range: null` so its eventual save\n // inserts at the now-current caret rather than a stale, already-consumed span.\n view.dispatch(view.state.tr.delete(range.from, range.to));\n command.guided({ ctx: controller.getCtx(), range: null }, iterationHandler);\n } else {\n // The command's `run` dispatches its own insert transaction via the view;\n // then close the (now stale) menu. If the command already removed the\n // range, the close meta is harmless.\n command.run({ ctx: controller.getCtx(), range });\n }\n if (slashPluginKey.getState(view.state)?.active) {\n view.dispatch(view.state.tr.setMeta(slashPluginKey, \"close\"));\n }\n view.focus();\n}\n\n/** A `/` is a valid trigger only at a textblock start or right after whitespace. */\nfunction triggerAllowed(doc: ProseNode, triggerPos: number): boolean {\n const $pos = doc.resolve(triggerPos);\n if (!$pos.parent.isTextblock) return false;\n if ($pos.parent.type.spec.code) return false;\n if ($pos.parentOffset === 0) return true; // start of block\n const before = $pos.parent.textBetween(Math.max(0, $pos.parentOffset - 1), $pos.parentOffset);\n return /\\s/.test(before);\n}\n\n/**\n * Recompute open/query/index from a transaction. Opens when the trigger is typed\n * in an allowed spot; tracks the query; closes when the `/` is removed, the caret\n * leaves the trigger run/block, the selection is non-empty, or a space is typed.\n *\n * Also handles the `_open` meta (programmatic open via shortcut / `openMenu()`):\n * sets `triggered: \"shortcut\"` so `runSlashCommand` uses `range: null`.\n */\nexport function nextState(\n prev: SlashPluginState,\n tr: Transaction,\n trigger: string,\n): SlashPluginState {\n const meta = tr.getMeta(slashPluginKey) as\n | (Partial<SlashPluginState> & { _open?: boolean })\n | \"close\"\n | undefined;\n\n if (meta === \"close\") return CLOSED;\n\n if (meta && typeof meta === \"object\") {\n // Programmatic open (shortcut / openMenu): open unconditionally at caret.\n if (meta._open) {\n return {\n active: true,\n from: (meta as { from: number }).from,\n query: \"\",\n index: 0,\n triggered: \"shortcut\",\n };\n }\n // Nav meta (index change) only applies while still active.\n return prev.active ? { ...prev, ...meta } : prev;\n }\n\n const sel = tr.selection;\n if (!sel.empty) return prev.active ? CLOSED : prev;\n\n const pos = sel.from;\n const $pos = tr.doc.resolve(pos);\n\n if (prev.active) {\n if (pos <= prev.from) return CLOSED;\n const start = tr.doc.resolve(prev.from);\n if (start.parent !== $pos.parent) return CLOSED;\n\n if (prev.triggered === \"shortcut\") {\n // In shortcut mode there is no leading trigger char — read the query as\n // text typed AFTER the caret-open position. Skip the triggerChar check.\n const query = $pos.parent.textBetween(start.parentOffset, $pos.parentOffset);\n if (/\\s/.test(query)) return CLOSED;\n const index = query === prev.query ? prev.index : 0;\n return { active: true, from: prev.from, query, index, triggered: \"shortcut\" };\n }\n\n // Char mode: the first character at `from` must still be the trigger.\n const triggerChar = start.parent.textBetween(start.parentOffset, start.parentOffset + 1);\n if (triggerChar !== trigger) return CLOSED;\n const query = $pos.parent.textBetween(start.parentOffset + 1, $pos.parentOffset);\n if (/\\s/.test(query)) return CLOSED;\n const index = query === prev.query ? prev.index : 0;\n return { active: true, from: prev.from, query, index, triggered: \"char\" };\n }\n\n if (!tr.docChanged) return prev;\n const justTyped = tr.doc.textBetween(Math.max(0, pos - 1), pos);\n if (justTyped !== trigger) return prev;\n const triggerPos = pos - 1;\n if (!triggerAllowed(tr.doc, triggerPos)) return prev;\n return { active: true, from: triggerPos, query: \"\", index: 0, triggered: \"char\" };\n}\n\n/** Options for {@link brandSlashPlugin}. */\nexport interface BrandSlashPluginOptions {\n /** The widget factory from `useWidgetViewFactory()` (created under the adapter provider). */\n widgetFactory: SlashWidgetFactory;\n /** The React component rendered as the widget (built with the controller). */\n widgetComponent: ReactWidgetViewComponent;\n /** The shared controller (commands + ctx getter + trigger). */\n controller: SlashController;\n}\n\n/**\n * Build the slash plugin. The widget factory + component + controller are created\n * in React (so the factory lives under `<ProsemirrorAdapterProvider>` and the\n * component closes over the controller). Returns a Milkdown plugin to `.use()`.\n */\nexport function brandSlashPlugin(options: BrandSlashPluginOptions): MilkdownPlugin {\n const { widgetFactory, widgetComponent, controller } = options;\n const trigger = controller.trigger || \"/\";\n\n return $prose((ctx) => {\n // Capture the live editor Ctx so command `run`s (keyboard + mouse) can\n // resolve NodeTypes and the view.\n controller._ctx = ctx;\n return new Plugin<SlashPluginState>({\n key: slashPluginKey,\n state: {\n init: () => CLOSED,\n apply: (tr, value) => nextState(value, tr, trigger),\n },\n props: {\n decorations: (state) => {\n const s = slashPluginKey.getState(state);\n if (!s?.active) return DecorationSet.empty;\n const factory = widgetFactory({ component: widgetComponent, as: \"span\" });\n // Anchor: in char mode `from + 1` skips the `/`; in shortcut mode\n // anchor at `from` (the bare caret — no `/` to step over).\n const anchor = s.triggered === \"shortcut\" ? s.from : s.from + 1;\n // `spec` goes on the factory CALL (the Decoration.widget spec), not the\n // factory options. After the caret; don't let the widget disturb the\n // editor selection. Key on from+query+index so ProseMirror re-renders the\n // widget when the filter OR the highlighted row changes (decoration\n // diffing reuses a widget with an unchanged key, which would freeze the\n // ↑/↓ highlight).\n const decoration = factory(anchor, {\n side: 1,\n ignoreSelection: true,\n key: `brand-slash:${s.from}:${s.query}:${s.index}`,\n });\n return DecorationSet.create(state.doc, [decoration]);\n },\n handleKeyDown: (view, event) => {\n // --- Shortcut open (BEFORE the active guard) ---\n // If a shortcut is configured and this event matches it, open the menu\n // at the current caret position in shortcut mode. This runs before keymaps\n // so it beats commonmark/gfm bindings (the exit-keymap.ts precedent).\n if (controller.shortcut && matchesKeyboardEvent(controller.shortcut, event)) {\n const pos = view.state.selection.from;\n view.dispatch(\n view.state.tr.setMeta(slashPluginKey, {\n _open: true,\n from: pos,\n triggered: \"shortcut\",\n }),\n );\n event.preventDefault();\n return true;\n }\n\n const s = slashPluginKey.getState(view.state);\n if (!s?.active) return false;\n const filtered = filterSlashCommands(controller.commands, s.query);\n\n if (event.key === \"Escape\") {\n view.dispatch(view.state.tr.setMeta(slashPluginKey, \"close\"));\n event.preventDefault();\n return true;\n }\n if (filtered.length === 0) return false;\n\n if (event.key === \"ArrowDown\") {\n const index = (s.index + 1) % filtered.length;\n view.dispatch(view.state.tr.setMeta(slashPluginKey, { index }));\n event.preventDefault();\n return true;\n }\n if (event.key === \"ArrowUp\") {\n const index = (s.index - 1 + filtered.length) % filtered.length;\n view.dispatch(view.state.tr.setMeta(slashPluginKey, { index }));\n event.preventDefault();\n return true;\n }\n if (event.key === \"Enter\") {\n const command = filtered[Math.min(s.index, filtered.length - 1)];\n if (command) {\n runSlashCommand(view, controller, s, command);\n event.preventDefault();\n return true;\n }\n }\n if (event.key === \"Tab\") {\n // Tab also selects the active command (common in Notion-style menus).\n const command = filtered[Math.min(s.index, filtered.length - 1)];\n if (command) {\n runSlashCommand(view, controller, s, command);\n event.preventDefault();\n return true;\n }\n }\n return false;\n },\n },\n });\n }) as unknown as MilkdownPlugin;\n}\n","/**\n * Shortcut helpers for the cross-pane slash menu (#271) — the PURE half.\n *\n * No React, no Milkdown, **no Monaco**. This module is imported by\n * `brand-slash-plugin.ts` (and therefore by the Milkdown `MarkdownEditor` import\n * graph), so it must NOT pull `monaco-editor` — doing so drags the Monaco runtime\n * (and its jsdom-hostile clipboard module) into every WYSIWYG test. The\n * Monaco-keybinding conversion lives in the sibling `./shortcut-monaco.ts`, which\n * only the Monaco-side surfaces (the workspace) import.\n *\n * INTERNAL — not exported from package barrels.\n */\n\n/**\n * The default keyboard shortcut for the slash command menu.\n * `Mod` = Cmd on macOS, Ctrl elsewhere (Monaco's `CtrlCmd`).\n *\n * NOTE: this is NOT `Mod-/` — Monaco binds `Mod-/` to \"Toggle Line Comment\" by\n * default, which eats the keystroke before the slash action sees it (the source\n * pane's primary trigger is typing `/`; this hotkey is the secondary path). `Mod-Shift-O`\n * is free in Chrome/macOS; it DOES collide with the bookmarks shortcut in Firefox\n * and on Windows/Linux Chrome, so consumers shipping cross-browser should override\n * `slashMenu.shortcut`. The conflict-free trigger is typing `/`.\n */\nexport const DEFAULT_SLASH_SHORTCUT = \"Mod-Shift-O\";\n\n/**\n * Returns `true` when a DOM `KeyboardEvent` matches a shortcut string.\n *\n * - `Mod` matches `event.metaKey` (macOS) OR `event.ctrlKey` (other platforms).\n * - `Shift` / `Alt` match the corresponding event fields.\n * - The final key matches `event.key` (case-insensitive); `\"/\"` matches\n * `event.key === \"/\"`.\n *\n * Used by the WYSIWYG plugin's `handleKeyDown` (which receives DOM events) to\n * decide whether to open the menu — pure, so it never pulls the monaco namespace.\n */\nexport function matchesKeyboardEvent(shortcut: string, event: KeyboardEvent): boolean {\n const parts = shortcut.split(\"-\");\n const keyPart = parts[parts.length - 1] ?? \"\";\n const modifiers = new Set(parts.slice(0, -1).map((m) => m.toLowerCase()));\n\n // Check modifier flags.\n const wantsMod = modifiers.has(\"mod\");\n const wantsShift = modifiers.has(\"shift\");\n const wantsAlt = modifiers.has(\"alt\");\n\n if (wantsMod && !(event.metaKey || event.ctrlKey)) return false;\n if (!wantsMod && (event.metaKey || event.ctrlKey)) return false;\n if (wantsShift && !event.shiftKey) return false;\n if (!wantsShift && event.shiftKey) return false;\n if (wantsAlt && !event.altKey) return false;\n if (!wantsAlt && event.altKey) return false;\n\n // Check the key itself (case-insensitive).\n return event.key.toLowerCase() === keyPart.toLowerCase();\n}\n","\"use client\";\n\n/**\n * SlashWidget — the React widget the slash plugin mounts at the caret.\n *\n * The adapter renders this (no props) inside a ProseMirror widget decoration; it\n * reads the live `EditorView` from `useWidgetViewContext()`, pulls the slash\n * plugin state, and renders the branded `<SlashMenu>` floating just below the\n * caret. Clicking an option runs the command through the shared\n * {@link SlashController} (same path as the plugin's Enter handler), so the mouse\n * and keyboard insert identically.\n *\n * Built via `createSlashWidget(controller)` so the component closes over the\n * controller (commands + Milkdown `Ctx` getter) — no module-level mutable state,\n * no circular import with the plugin.\n */\nimport type { ReactWidgetViewComponent } from \"@prosemirror-adapter/react\";\nimport { useWidgetViewContext } from \"@prosemirror-adapter/react\";\nimport { useLayoutEffect, useRef } from \"react\";\n\nimport { filterSlashCommands, type SlashCommand } from \"./brand-slash-commands\";\nimport { SlashMenu, slashOptionId } from \"./slash-menu\";\nimport {\n runSlashCommand,\n slashPluginKey,\n type SlashController,\n type SlashPluginState,\n} from \"./brand-slash-plugin\";\n\nconst ID_PREFIX = \"brand-slash\";\n\n/** Build the widget component bound to a controller. */\nexport function createSlashWidget(controller: SlashController): ReactWidgetViewComponent {\n function SlashWidget() {\n const { view } = useWidgetViewContext();\n const wrapperRef = useRef<HTMLSpanElement>(null);\n\n const state = slashPluginKey.getState(view.state) as SlashPluginState | undefined;\n const query = state?.query ?? \"\";\n const filtered = filterSlashCommands(controller.commands, query);\n const activeIndex = state ? Math.min(state.index, Math.max(0, filtered.length - 1)) : 0;\n const active = filtered[activeIndex];\n const activeId = active?.id;\n\n // Mirror the active option onto the editor's `textbox` for AT\n // (`aria-activedescendant` + `aria-controls`), then clean up on unmount.\n useLayoutEffect(() => {\n const dom = view.dom as HTMLElement;\n const listEl = wrapperRef.current?.querySelector<HTMLElement>('[role=\"listbox\"]');\n if (listEl && !listEl.id) listEl.id = `${ID_PREFIX}-listbox`;\n dom.setAttribute(\"aria-expanded\", \"true\");\n if (listEl) dom.setAttribute(\"aria-controls\", listEl.id);\n if (activeId) dom.setAttribute(\"aria-activedescendant\", slashOptionId(ID_PREFIX, activeId));\n else dom.removeAttribute(\"aria-activedescendant\");\n return () => {\n dom.removeAttribute(\"aria-expanded\");\n dom.removeAttribute(\"aria-controls\");\n dom.removeAttribute(\"aria-activedescendant\");\n };\n }, [view, activeId]);\n\n // Keep the highlighted option scrolled into view as ↑/↓ moves it.\n useLayoutEffect(() => {\n if (!activeId) return;\n const el = wrapperRef.current?.querySelector<HTMLElement>(\n `#${CSS.escape(slashOptionId(ID_PREFIX, activeId))}`,\n );\n el?.scrollIntoView({ block: \"nearest\" });\n }, [activeId]);\n\n const onSelect = (command: SlashCommand) => {\n if (!state) return;\n runSlashCommand(view, controller, state, command);\n };\n\n return (\n // The widget anchor is zero-width inline; the menu floats below the caret.\n <span\n ref={wrapperRef}\n contentEditable={false}\n // Keep the widget out of the accessibility tree as a node — the listbox\n // inside carries the semantics, and the editor textbox owns the relationship.\n className=\"brand-slash-anchor relative inline-block h-0 w-0 align-baseline\"\n >\n <span className=\"absolute left-0 top-1 z-50 block\">\n <SlashMenu\n commands={filtered}\n activeId={activeId}\n onSelect={onSelect}\n idPrefix={ID_PREFIX}\n />\n </span>\n </span>\n );\n }\n return SlashWidget;\n}\n","\"use client\";\n\n/**\n * Brand slash-menu — public surface for the WYSIWYG markdown editor.\n *\n * Typing `/` at a block start (or after whitespace) opens a branded command menu\n * that inserts the brand `:::` directives + basic blocks live in the editor. The\n * load-bearing pieces:\n * - `brand-slash-commands` — the typed, tree-shakeable command registry.\n * - `insert-directive` — the pure ProseMirror insertion commands (unit-tested).\n * - `brand-slash-plugin` — the `$prose` plugin (state + keyboard).\n * - `slash-widget` / `slash-menu` — the React widget + the branded popup.\n *\n * `brandSlashViewPlugins(widgetFactory, options)` wires it all together — call it\n * with the factory from `useWidgetViewFactory()` (so it runs under\n * `<ProsemirrorAdapterProvider>`), exactly like `directiveViewPlugins`.\n */\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\n\nimport type { IterationEditHandler } from \"../../markdown-iteration/edit-context\";\nimport { BRAND_SLASH_COMMANDS, type SlashCommand } from \"./brand-slash-commands\";\nimport {\n brandSlashPlugin,\n createSlashController,\n type SlashWidgetFactory,\n} from \"./brand-slash-plugin\";\nimport { DEFAULT_SLASH_SHORTCUT } from \"./shortcut\";\nimport { createSlashWidget } from \"./slash-widget\";\n\n/** Options for {@link brandSlashViewPlugins}. */\nexport interface BrandSlashViewOptions {\n /** Override the default command list. */\n commands?: SlashCommand[];\n /** The trigger character. Defaults to `\"/\"`. */\n trigger?: string;\n /**\n * Keyboard shortcut that opens the menu at the caret in BOTH panes (#271).\n * Defaults to `DEFAULT_SLASH_SHORTCUT` (`\"Mod-Shift-O\"`). Set to `undefined` or\n * an empty string to disable the shortcut binding.\n */\n shortcut?: string;\n /**\n * Resolve the live `IterationEditContext` handler (#223, internal —\n * `MarkdownEditorView` wires this from `useContext`). When present, a\n * `guided` command (`/iterate` `/pivot`) opens its modal instead of a bare\n * insert. Not part of the public `slashMenu` prop surface.\n */\n getIterationEditHandler?: () => IterationEditHandler | null | undefined;\n}\n\n/**\n * Build the slash-menu Milkdown plugins. Call with the widget factory from\n * `useWidgetViewFactory()` (must run inside a `<ProsemirrorAdapterProvider>`),\n * then `.use()` the result on the editor.\n */\nexport function brandSlashViewPlugins(\n widgetFactory: SlashWidgetFactory,\n options: BrandSlashViewOptions = {},\n): MilkdownPlugin[] {\n const commands = options.commands ?? BRAND_SLASH_COMMANDS;\n const shortcut = \"shortcut\" in options ? options.shortcut : DEFAULT_SLASH_SHORTCUT;\n const controller = createSlashController(\n commands,\n options.trigger ?? \"/\",\n shortcut,\n options.getIterationEditHandler,\n );\n const widgetComponent = createSlashWidget(controller);\n return [brandSlashPlugin({ widgetFactory, widgetComponent, controller })];\n}\n\nexport {\n BRAND_SLASH_COMMANDS,\n filterSlashCommands,\n groupSlashCommands,\n type SlashCommand,\n type SlashInsertContext,\n type SourceSlashContext,\n type BasicBlockId,\n} from \"./brand-slash-commands\";\nexport {\n insertBrandDirective,\n insertBasicBlock,\n insertCalcFence,\n resolveBrandInsert,\n resolveBasicInsert,\n resolveCalcInsert,\n isContainerDirective,\n isLeafDirective,\n CALC_FENCE_SEED,\n type InsertableDirective,\n type SlashRange,\n} from \"./insert-directive\";\nexport { SlashMenu, slashOptionId, type SlashMenuProps } from \"./slash-menu\";\nexport {\n slashPluginKey,\n slashRange,\n createSlashController,\n type SlashController,\n type SlashPluginState,\n type SlashWidgetFactory,\n} from \"./brand-slash-plugin\";\n// NOTE: `MonacoSlashMenu` is deliberately NOT re-exported here. It pulls the\n// Monaco runtime (via `markdown-commands`), and this barrel is imported by the\n// Milkdown `MarkdownEditor` graph, which must stay Monaco-free. It is exported\n// from the heavy `@elabs-ai/components-editor/markdown` subpath instead (which already pulls\n// Monaco via `MarkdownWorkspace`).\n","\"use client\";\n\n/**\n * MarkdownEditor — a headless Milkdown (ProseMirror) WYSIWYG markdown surface,\n * vendored onto brand-ui. Companion to the Monaco-based CodeEditor: same package,\n * different engine (Monaco = source/diff; Milkdown = direct-manipulation markdown).\n *\n * - Engine dep: only `@milkdown/kit` (headless). React glue is vendored under\n * ./milkdown-react so we never pull `@milkdown/react` → `@milkdown/crepe` → Vue.\n * - Theming: token-driven via markdown-editor.css (every theme, no raw color).\n * - Controlled (`value` + `onChange`) or uncontrolled (`defaultValue`). Mirrors the\n * platform: `isControlled = value !== undefined`; never flips between modes.\n * - StrictMode-safe (see markdown-editor.strictmode.test.tsx).\n * - The brand `:::` directives render as live @brand React components inside the\n * editor (real <Card>/<Alert>/<MetricBlock>, with inline-editable titles +\n * metric label/value) via @prosemirror-adapter/react — see directive-views.tsx.\n * The shared remark pipeline keeps the editor and the Streamdown preview on one\n * markdown dialect.\n */\nimport \"@milkdown/kit/prose/view/style/prosemirror.css\";\nimport \"./markdown-editor.css\";\n\nimport {\n Editor,\n defaultValueCtx,\n editorViewCtx,\n editorViewOptionsCtx,\n parserCtx,\n rootCtx,\n serializerCtx,\n} from \"@milkdown/kit/core\";\nimport { commonmark } from \"@milkdown/kit/preset/commonmark\";\nimport { gfm } from \"@milkdown/kit/preset/gfm\";\nimport { history } from \"@milkdown/kit/plugin/history\";\nimport { listener, listenerCtx } from \"@milkdown/kit/plugin/listener\";\nimport type { Node as ProseNode } from \"@milkdown/kit/prose/model\";\nimport { TextSelection } from \"@milkdown/kit/prose/state\";\nimport type { EditorView } from \"@milkdown/kit/prose/view\";\nimport { getMarkdown, replaceAll } from \"@milkdown/kit/utils\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport {\n ProsemirrorAdapterProvider,\n useNodeViewFactory,\n usePluginViewFactory,\n useWidgetViewFactory,\n} from \"@prosemirror-adapter/react\";\nimport {\n forwardRef,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n type HTMLAttributes,\n} from \"react\";\n\nimport { calcProsePlugins } from \"../calc-block/calc-editor-prose\";\nimport type { CalcEditorHooks } from \"../calc-block/types\";\nimport { completionsViewPlugins } from \"./completions\";\nimport type { EditorCompletionProvider } from \"../lib/editor-completions\";\nimport type { EditorContentAccess, EditorSelection } from \"../lib/editor-content-access\";\nimport { proseMirrorContentAccess, selectionWatchPlugin } from \"../lib/editor-content-access-prose\";\nimport { markdownScaleVars } from \"../lib/markdown/markdown-scale\";\nimport { plainText, slugifyHeading, uniqueSlug } from \"../lib/markdown/slugify\";\nimport {\n IterationEditContext,\n type IterationEditHandler,\n} from \"../markdown-iteration/edit-context\";\nimport { parseMarkdownOutline } from \"../markdown-outline\";\nimport { directivePlugins } from \"./directive-nodes\";\nimport { directiveViewPlugins } from \"./directive-views\";\nimport { exitKeymapPlugins } from \"./exit-keymap\";\nimport { Milkdown, MilkdownProvider, useEditor, useInstance } from \"./milkdown-react\";\nimport { pasteEmbedPlugin, type EmbedAssetFn } from \"./paste-embed\";\nimport { brandSlashViewPlugins, type SlashCommand } from \"./slash\";\nimport { tableViewPlugins } from \"./table-view\";\n\n/**\n * Imperative handle exposed via `ref`.\n *\n * Extends {@link EditorContentAccess} so any consumer of a `MarkdownEditor`\n * ref can drive AI editing operations (insert, replace, subscribe to selection)\n * through the engine-agnostic interface, alongside the markdown-specific helpers.\n *\n * `getText()` is an alias for `getMarkdown()` (same output; both return the full\n * document serialized to markdown). The duplication is intentional: `getText`\n * satisfies the `EditorContentAccess` interface contract; `getMarkdown` is the\n * historically-named markdown-specific method. JSDoc notes the equivalence.\n */\nexport interface MarkdownEditorHandle extends EditorContentAccess {\n /**\n * Serialize the current document to a markdown string.\n * Equivalent to `getText()` — both return the full document as markdown.\n */\n getMarkdown: () => string;\n /**\n * Serialize the current document, or `null` while the engine is still\n * booting. Used to capture the pre-edit normalization BASELINE for the\n * lossless-edit merge (WI-1) — unlike `getMarkdown` it never falls back to\n * the raw input, so a non-null result is always the editor's own output.\n */\n serialized: () => string | null;\n /**\n * Best-effort: scroll the heading whose outline slug matches into view (#273).\n * Walks the ProseMirror doc for `heading` nodes, slugifies their text with the\n * same algorithm as `parseMarkdownOutline`, and scrolls the first match into\n * view. No-op (never throws) while the engine is booting or if no match.\n */\n scrollToHeading: (slug: string) => void;\n /**\n * Best-effort: scroll toward FULL-SOURCE 1-based `line` by resolving the\n * nearest preceding heading in the ProseMirror doc (no exact source map in\n * WYSIWYG — Milkdown keeps no `data-sourcepos`). No-op when no preceding\n * heading is found or while booting. (#273)\n *\n * For precise navigation, prefer `scrollToHeading(slug)` — the workspace\n * resolves the slug from the line via `parseMarkdownOutline` + `fmOffset`\n * before forwarding here.\n */\n revealLine: (line: number, opts?: { center?: boolean }) => void;\n}\n\nexport interface MarkdownEditorProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Controlled markdown value. Pair with `onChange`. */\n value?: string;\n /** Initial markdown for uncontrolled use. */\n defaultValue?: string;\n /** Fires on every edit with the full markdown document. */\n onChange?: (markdown: string) => void;\n /** Render the editor read-only (still selectable, not editable). */\n readOnly?: boolean;\n /**\n * Accessible name for the editable surface. Milkdown/ProseMirror renders an ARIA\n * `textbox` (`contenteditable`); without a name, screen readers announce an\n * unlabeled field. Set onto the ProseMirror view's `attributes`. Default\n * `\"Markdown editor\"`.\n */\n ariaLabel?: string;\n /**\n * Enable the `/` command menu (insert brand `:::` directives + basic blocks\n * live at the caret). `true` (default) uses the built-in\n * {@link BRAND_SLASH_COMMANDS}; pass a `commands` array to extend/replace them,\n * or `false` to disable. The `trigger` defaults to `\"/\"`.\n *\n * Pass `shortcut` (default `\"Mod-Shift-O\"`) to control the keyboard shortcut that\n * opens the menu at the caret without typing the trigger character — works in\n * BOTH the WYSIWYG pane and the Monaco source pane (#271).\n */\n slashMenu?: boolean | { commands?: SlashCommand[]; trigger?: string; shortcut?: string };\n /**\n * Opt-in calc authoring inside ```calc fences (off by default). Supply the\n * consumer's hooks — `tokenize` (highlight), `evaluate` (result inlays), and/or\n * `complete` (autocomplete; Monaco surface). The library DECORATES, the consumer\n * COMPUTES — no calc engine is bundled. Mirrors `MarkdownPreview`'s `evaluate`.\n */\n calc?: CalcEditorHooks;\n /**\n * Declarative completion providers (#283) — e.g. `[[wikilink]]` autocomplete.\n * Off by default; mirrors the `slashMenu`/`calc` opt-in pattern. Each provider\n * registers a `triggerCharacters` set + a `provide(ctx)` that returns\n * candidates; the library owns detecting the trigger and rendering/inserting.\n *\n * This is a DELIBERATELY MINIMAL, best-effort mirror of the Monaco source-pane\n * behavior (see `markdown-editor/completions/completions-prose.ts` for the\n * exact gaps — no real cross-block line/column, a plain listbox instead of a\n * native suggest widget). Forwarded from `MarkdownWorkspace`'s `completions`\n * prop, which also wires the FULL Monaco/source-pane path.\n */\n completions?: EditorCompletionProvider[];\n /**\n * Host-provided callback for image paste/drop embedding.\n *\n * When set, the editor intercepts paste and drop events that contain image\n * files, shows an inline \"uploading…\" placeholder, calls this function with\n * the `File`, and on resolve inserts `` in the\n * document. On reject the placeholder is removed and an inline error chip\n * (+ toast) is shown — the document never contains a broken image.\n *\n * If not set, the editor's default paste/drop behavior is unchanged.\n *\n * The library NEVER stores assets — all persistence is the host's responsibility.\n */\n onEmbedAsset?: EmbedAssetFn;\n}\n\nexport type { EmbedAssetFn };\n\ninterface ViewProps {\n initialValue: string;\n value?: string;\n onChange?: (markdown: string) => void;\n readOnly: boolean;\n ariaLabel: string;\n slashMenu: boolean | { commands?: SlashCommand[]; trigger?: string; shortcut?: string };\n calc?: CalcEditorHooks;\n completions?: EditorCompletionProvider[];\n onEmbedAsset?: EmbedAssetFn;\n}\n\n/**\n * Walk the ProseMirror doc for a `heading` node whose slug (slugified with the\n * SAME algorithm as `parseMarkdownOutline`) matches `slug`, and scroll it into\n * view. Best-effort: a no-op (never throws) when no match / out of range. Shared\n * by `scrollToHeading` and the best-effort `revealLine` (#273).\n */\nfunction scrollHeadingBySlug(editor: Editor, slug: string): void {\n editor.action((ctx) => {\n const view = ctx.get(editorViewCtx);\n const { doc } = view.state;\n const used = new Map<string, number>();\n let targetPos: number | null = null;\n doc.forEach((node, offset) => {\n if (targetPos !== null) return;\n if (node.type.name === \"heading\") {\n const id = uniqueSlug(slugifyHeading(plainText(node.textContent)), used);\n if (id === slug) targetPos = offset + 1; // +1 steps inside the heading node\n }\n });\n if (targetPos === null) return;\n try {\n const resolved = doc.resolve(targetPos);\n view.dispatch(view.state.tr.setSelection(TextSelection.near(resolved)).scrollIntoView());\n } catch {\n // Guard against out-of-range positions — never throw.\n }\n });\n}\n\nconst MarkdownEditorView = forwardRef<MarkdownEditorHandle, ViewProps>(function MarkdownEditorView(\n {\n initialValue,\n value,\n onChange,\n readOnly,\n ariaLabel,\n slashMenu,\n calc,\n completions,\n onEmbedAsset,\n },\n ref,\n) {\n // Latest onChange via a ref so the create effect can run once per `readOnly`.\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n // Tracks the editor's current markdown — both the \"preserve across recreate\"\n // value and the echo-guard for controlled `value` syncing.\n const lastMarkdown = useRef(initialValue);\n\n // Renders the brand `:::` directives as live @brand React components inside the\n // editor (must run under <ProsemirrorAdapterProvider>). The factory is stable.\n const nodeViewFactory = useNodeViewFactory();\n // The widget factory backs the `/` command menu (rendered as a ProseMirror\n // widget at the caret). Same adapter, also stable.\n const widgetViewFactory = useWidgetViewFactory();\n // The plugin-view factory mounts the table controls toolbar as a ProseMirror\n // plugin view (outside contentEditable, no focus conflicts).\n const pluginViewFactory = usePluginViewFactory();\n\n // Resolve the slash config through a ref so a fresh `commands` array identity\n // each render never rebuilds the editor — only enabling/disabling does.\n const slashEnabled = slashMenu !== false;\n const slashConfigRef = useRef<{ commands?: SlashCommand[]; trigger?: string; shortcut?: string }>(\n {},\n );\n slashConfigRef.current = typeof slashMenu === \"object\" ? slashMenu : {};\n\n // Calc authoring hooks read through a ref — like the slash config, a fresh\n // `calc` object identity each render must NOT rebuild the editor; only\n // toggling the feature on/off does (the plugin reads the latest hooks lazily).\n const calcEnabled = calc != null;\n const calcRef = useRef<CalcEditorHooks | undefined>(calc);\n calcRef.current = calc;\n\n // Completion providers (#283) read through a ref — a fresh `completions`\n // array identity each render must NOT rebuild the editor (or re-register the\n // Monaco-side global provider); only enabling/disabling the feature does. The\n // plugin's async `provide()` fetch reads the LATEST list via this getter.\n const completionsEnabled = completions != null;\n const completionsRef = useRef<EditorCompletionProvider[] | undefined>(completions);\n completionsRef.current = completions;\n\n // Keep onEmbedAsset in a ref so the editor factory captures it by reference —\n // a new function identity each render never causes an editor rebuild.\n const onEmbedAssetRef = useRef(onEmbedAsset);\n onEmbedAssetRef.current = onEmbedAsset;\n\n // #223: the live `IterationEditContext` handler (set by a consumer's\n // `IterationBuilderProvider` / `IterationTemplateProvider` ABOVE this editor),\n // read through a ref like the other slash/calc hooks — a fresh Provider value\n // each render must NOT rebuild the editor; the plugin resolves it lazily via\n // the getter passed to `brandSlashViewPlugins`.\n const iterationEditHandler = useContext(IterationEditContext);\n const iterationEditHandlerRef = useRef<IterationEditHandler | null>(iterationEditHandler);\n iterationEditHandlerRef.current = iterationEditHandler;\n\n // Shared selection-change listener set: the selectionWatchPlugin writes to it;\n // proseMirrorContentAccess reads/writes subscriptions. Stable across re-renders\n // (created once) — the plugin reads it via the thunk each transaction.\n const selectionListeners = useRef(new Set<(sel: EditorSelection) => void>()).current;\n\n // The serializeSlice closure is built lazily in useImperativeHandle (needs\n // getInstance), but the plugin reads the LATEST version via a thunk so it never\n // captures a stale closure. We hold a ref that useImperativeHandle fills in.\n const serializeSliceRef = useRef<(view: EditorView) => string>(() => \"\");\n\n useEditor(\n (root) => {\n // Build the paste-embed plugin with an indirection through the ref so the\n // plugin always calls the latest host callback without rebuilding.\n const embedPlugin = pasteEmbedPlugin(\n onEmbedAssetRef.current ? (file: File) => onEmbedAssetRef.current!(file) : undefined,\n );\n\n let editor = Editor.make()\n .config((ctx) => {\n ctx.set(rootCtx, root);\n ctx.set(defaultValueCtx, lastMarkdown.current);\n ctx.update(editorViewOptionsCtx, (prev) => ({\n ...prev,\n editable: () => !readOnly,\n // Name the ProseMirror `role=\"textbox\"` surface so AT announces it.\n // Spread prev.attributes so we never clobber Milkdown's own view attrs.\n attributes: { ...prev.attributes, \"aria-label\": ariaLabel },\n }));\n ctx.get(listenerCtx).markdownUpdated((_, markdown) => {\n lastMarkdown.current = markdown;\n onChangeRef.current?.(markdown);\n });\n })\n .use(commonmark)\n .use(gfm)\n .use(tableViewPlugins(pluginViewFactory))\n .use(exitKeymapPlugins())\n .use(history)\n .use(listener)\n .use(directivePlugins)\n .use(directiveViewPlugins(nodeViewFactory))\n .use(embedPlugin)\n // Always-present selection-watch plugin — notifies selectionListeners when\n // the ProseMirror selection changes. Listeners subscribe/unsubscribe via the\n // proseMirrorContentAccess adapter; the plugin itself is stateless per-sub.\n .use(\n selectionWatchPlugin(\n () => selectionListeners,\n () => serializeSliceRef.current,\n ),\n );\n if (slashEnabled) {\n editor = editor.use(\n brandSlashViewPlugins(widgetViewFactory, {\n ...slashConfigRef.current,\n getIterationEditHandler: () => iterationEditHandlerRef.current,\n }),\n );\n }\n if (calcEnabled) {\n editor = editor.use(calcProsePlugins(() => calcRef.current));\n }\n if (completionsEnabled) {\n editor = editor.use(\n completionsViewPlugins(widgetViewFactory, () => completionsRef.current),\n );\n }\n return editor;\n },\n [readOnly, ariaLabel, slashEnabled, calcEnabled, completionsEnabled],\n );\n\n const [loading, getInstance] = useInstance();\n\n // Controlled sync: push external `value` into the editor only when it diverges\n // from what the editor last emitted (avoids clobbering on our own echo).\n useEffect(() => {\n if (loading || value === undefined) return;\n if (value === lastMarkdown.current) return;\n const editor = getInstance();\n if (!editor) return;\n lastMarkdown.current = value;\n editor.action(replaceAll(value));\n }, [loading, value, getInstance]);\n\n useImperativeHandle(\n ref,\n () => {\n // Build serialize/parse closures via getInstance().action(ctx => ...) —\n // the exact technique used by directive-views.tsx (readBodyMarkdown /\n // writeBodyMarkdown). These are re-built whenever getInstance changes\n // (i.e. when the editor is recreated).\n\n /** Serialize the current selection slice to markdown (\"\" when collapsed). */\n const serializeSlice = (view: EditorView): string => {\n const editor = getInstance();\n if (!editor) return \"\";\n const { selection } = view.state;\n if (selection.empty) return \"\";\n try {\n return editor\n .action((ctx) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const serialize = (ctx as any).get(serializerCtx) as (node: ProseNode) => string;\n const doc = selection.content().content;\n // Wrap in a top-level doc node so the serializer can emit all block types.\n const wrapper = view.state.doc.type.schema.topNodeType.create(null, doc);\n return serialize(wrapper);\n })\n .trim();\n } catch {\n // Serializer unavailable (e.g. booting) — fall back to plain text.\n return view.state.doc.textBetween(selection.from, selection.to, \"\\n\");\n }\n };\n\n // Update the ref so the selection-watch plugin always reads the latest closure.\n serializeSliceRef.current = serializeSlice;\n\n /** Parse `md` as markdown and replace the current selection; plain-text fallback. */\n const parseAndReplace = (view: EditorView, md: string): void => {\n const editor = getInstance();\n if (!editor) return;\n try {\n editor.action((ctx) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const parse = (ctx as any).get(parserCtx) as (md: string) => ProseNode | null;\n const parsed = parse(md);\n if (!parsed) {\n // Null parse result → plain-text insert.\n view.dispatch(view.state.tr.insertText(md));\n return;\n }\n // Replace the current selection with the parsed content.\n const tr = view.state.tr.replaceSelectionWith(parsed);\n view.dispatch(tr);\n });\n } catch {\n // Parse failure → plain-text fallback (never blanks the doc, never throws).\n try {\n view.dispatch(view.state.tr.insertText(md));\n } catch {\n // Guard: if even insertText fails (e.g. read-only), silently no-op.\n }\n }\n };\n\n const getView = (): EditorView | null => {\n const editor = getInstance();\n if (!editor) return null;\n try {\n return editor.action((ctx) => ctx.get(editorViewCtx));\n } catch {\n return null;\n }\n };\n\n const access = proseMirrorContentAccess({\n getView,\n getText: () => getInstance()?.action(getMarkdown()) ?? lastMarkdown.current,\n serializeSlice,\n parseAndReplace,\n listeners: selectionListeners,\n });\n\n return {\n // EditorContentAccess methods (via proseMirrorContentAccess).\n getText: access.getText,\n getSelection: access.getSelection,\n replaceSelection: access.replaceSelection,\n insertAtCursor: access.insertAtCursor,\n focus: access.focus,\n onSelectionChange: access.onSelectionChange,\n\n // Markdown-specific methods.\n getMarkdown: () => getInstance()?.action(getMarkdown()) ?? lastMarkdown.current,\n serialized: () => getInstance()?.action(getMarkdown()) ?? null,\n\n scrollToHeading: (slug: string) => {\n const editor = getInstance();\n if (editor) scrollHeadingBySlug(editor, slug);\n },\n\n revealLine: (line: number, _opts?: { center?: boolean }) => {\n // WYSIWYG has no source map (Milkdown keeps no data-sourcepos), so this is\n // best-effort: serialize the doc, resolve the nearest preceding heading via\n // the shared outline parser, and scroll to it. No-op when none. (#273)\n const editor = getInstance();\n if (!editor) return;\n const md = editor.action(getMarkdown());\n const preceding = parseMarkdownOutline(md)\n .filter((item) => item.line <= line)\n .at(-1);\n if (preceding) scrollHeadingBySlug(editor, preceding.id);\n },\n };\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [getInstance],\n );\n\n return <Milkdown />;\n});\n\nexport const MarkdownEditor = forwardRef<MarkdownEditorHandle, MarkdownEditorProps>(\n function MarkdownEditor(\n {\n value,\n defaultValue,\n onChange,\n readOnly = false,\n ariaLabel = \"Markdown editor\",\n slashMenu = true,\n calc,\n completions,\n onEmbedAsset,\n className,\n style,\n ...props\n },\n ref,\n ) {\n // Capture the initial value once; later `value` changes flow through the\n // controlled-sync effect, not a remount.\n const initialValue = useRef(value ?? defaultValue ?? \"\").current;\n\n return (\n <div\n data-testid=\"markdown-editor\"\n className={cn(\n // No focus-ring utility here (deliberately, #309): the editable\n // `.ProseMirror` element owns the compound focus indicator itself\n // (`markdown-editor.css`'s `:focus-visible` rule), so it renders\n // even when a consumer `className` overrides this wrapper's\n // classes (`MarkdownWorkspace` does exactly that via\n // `className=\"border-0\"`) and never doubles up with a wrapper ring.\n \"milkdown-host overflow-auto rounded-md border border-border bg-background text-foreground\",\n className,\n )}\n // Publish the shared markdown scale as CSS vars the editor CSS reads, so the\n // WYSIWYG headings/measure match the preview (single source of truth, #18).\n style={{ ...markdownScaleVars(), ...style }}\n {...props}\n >\n <MilkdownProvider>\n <ProsemirrorAdapterProvider>\n <MarkdownEditorView\n ref={ref}\n initialValue={initialValue}\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n ariaLabel={ariaLabel}\n slashMenu={slashMenu}\n calc={calc}\n completions={completions}\n onEmbedAsset={onEmbedAsset}\n />\n </ProsemirrorAdapterProvider>\n </MilkdownProvider>\n </div>\n );\n },\n);\n","\"use client\";\n\n/**\n * The WYSIWYG (Milkdown/ProseMirror) mirror of the completion-provider API\n * (#283) — a self-owned raw `$prose` plugin (the `brand-slash-plugin.ts`\n * precedent), Monaco-free.\n *\n * ## What this covers, and what it doesn't (read before reaching for this file)\n *\n * The Monaco / source-pane path (`../../lib/editor-completions-monaco.ts`) gets\n * REAL document line/column coordinates and Monaco's own themed suggest widget\n * for free. Milkdown's ProseMirror document is a NODE TREE, not line-oriented\n * text, so there is no exact equivalent — this is a deliberately MINIMAL,\n * best-effort mirror (per #283: \"if full WYSIWYG parity is too large to land\n * safely, ship... a minimal WYSIWYG trigger→insert\"):\n *\n * - `EditorCompletionContext.source` / `.lineText` are the CURRENT TEXTBLOCK's\n * plain text only (not the whole document, and not markdown-serialized) —\n * good enough for a provider whose logic looks at the text around the caret\n * (the `[[wikilink]]` case this issue targets), but NOT a byte-exact mirror\n * of the Monaco context.\n * - `.line` is always `1` (there is no cross-block \"line number\" concept).\n * - The popup is a plain, unstyled-beyond-tokens listbox (no groups/icons —\n * `EditorCompletionItem` doesn't carry them), NOT Monaco's native widget.\n * - Keyboard support covers ↑/↓/Enter/Tab/Esc (feature parity with the Monaco\n * path's expectations) but not mouse-hover-to-preview or the scroll-into-view\n * polish `MonacoSlashMenu`/`slash-widget.tsx` add.\n *\n * Candidates/filtering/insertText stay 100% consumer-owned either way — this\n * file only detects the trigger, asks the provider, and renders/inserts.\n */\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport type { Node as ProseNode } from \"@milkdown/kit/prose/model\";\nimport { Plugin, PluginKey, type Transaction } from \"@milkdown/kit/prose/state\";\nimport type { EditorView } from \"@milkdown/kit/prose/view\";\nimport { DecorationSet } from \"@milkdown/kit/prose/view\";\nimport { $prose } from \"@milkdown/kit/utils\";\nimport type { ReactWidgetViewComponent, useWidgetViewFactory } from \"@prosemirror-adapter/react\";\n\nimport {\n collectCompletions,\n resolveReplaceRange,\n type EditorCompletionContext,\n type EditorCompletionItem,\n type EditorCompletionProvider,\n} from \"../../lib/editor-completions\";\n\n/** The factory `useWidgetViewFactory()` returns (borrowed, see `brand-slash-plugin.ts`). */\nexport type CompletionWidgetFactory = ReturnType<typeof useWidgetViewFactory>;\n\n/** Plugin state. `active === false` means no popup is showing. */\nexport interface CompletionPluginState {\n active: boolean;\n /** Document position of the trigger character that opened/re-opened tracking. */\n from: number;\n /** The trigger character itself (matched against each provider's `triggerCharacters`). */\n triggerChar: string;\n /** Text typed since the trigger (closes on whitespace / caret leaving the block). */\n query: string;\n /** The latest resolved candidates (empty while a fetch is in flight). */\n items: EditorCompletionItem[];\n /** Index into `items` of the highlighted candidate. */\n index: number;\n /** Bumped on every trigger/query change; guards a stale async `provide()` reply. */\n requestId: number;\n}\n\nexport const CLOSED: CompletionPluginState = {\n active: false,\n from: 0,\n triggerChar: \"\",\n query: \"\",\n items: [],\n index: 0,\n requestId: 0,\n};\n\nexport const completionsPluginKey = new PluginKey<CompletionPluginState>(\"brand-completions\");\n\ntype CompletionMeta =\n | \"close\"\n | { type: \"items\"; items: EditorCompletionItem[]; requestId: number }\n | { type: \"nav\"; index: number };\n\n/**\n * Recompute state from a transaction. Exported for unit tests (the\n * `brand-slash-plugin.ts` / `nextState` precedent) — a pure function over\n * `(prev, transaction, triggerCharacters)`, no rendering needed to exercise it.\n *\n * A freshly typed trigger character ALWAYS (re)opens tracking at its position —\n * even while already active — so `[[note` tracks the LAST `[` (matching the\n * Monaco path's `triggerQueryStart`, which also resolves to the last\n * occurrence): the first `[` opens, the second `[` re-anchors with an empty\n * query, and `note` extends it from there.\n */\nexport function nextCompletionState(\n prev: CompletionPluginState,\n tr: Transaction,\n triggerCharacters: string[],\n): CompletionPluginState {\n const meta = tr.getMeta(completionsPluginKey) as CompletionMeta | undefined;\n if (meta === \"close\") return CLOSED;\n if (meta && typeof meta === \"object\") {\n if (meta.type === \"items\") {\n return prev.active && meta.requestId === prev.requestId\n ? { ...prev, items: meta.items }\n : prev;\n }\n if (meta.type === \"nav\") {\n return prev.active ? { ...prev, index: meta.index } : prev;\n }\n }\n\n const sel = tr.selection;\n if (!sel.empty) return prev.active ? CLOSED : prev;\n const pos = sel.from;\n\n // A pure single-character INSERT nets the doc +1 char with a single step —\n // this excludes a backspace/delete (net -1) from ever being misread as\n // \"just typed a trigger character\" just because deleting happened to expose\n // one immediately before the new caret (e.g. backspacing \"n\" out of \"[[n\"\n // lands the caret right after the trigger \"[\"; textBetween alone can't tell\n // an insert from a delete apart, so it needs this net-growth guard too).\n const isPureSingleCharInsert =\n tr.docChanged && tr.steps.length === 1 && tr.doc.content.size === tr.before.content.size + 1;\n\n if (isPureSingleCharInsert) {\n const justTyped = tr.doc.textBetween(Math.max(0, pos - 1), pos);\n if (triggerCharacters.includes(justTyped)) {\n return {\n active: true,\n from: pos - 1,\n triggerChar: justTyped,\n query: \"\",\n items: [],\n index: 0,\n requestId: prev.requestId + 1,\n };\n }\n }\n\n if (!prev.active) return prev;\n if (pos <= prev.from) return CLOSED;\n\n const start = tr.doc.resolve(prev.from);\n const $pos = tr.doc.resolve(pos);\n if (start.parent !== $pos.parent) return CLOSED;\n\n const query = $pos.parent.textBetween(start.parentOffset + 1, $pos.parentOffset);\n if (/\\s/.test(query)) return CLOSED;\n if (query === prev.query) return prev;\n return { ...prev, query, index: 0, requestId: prev.requestId + 1 };\n}\n\n/** Build the (best-effort — see file doc) context handed to `provider.provide()`. */\nexport function buildCompletionContext(\n doc: ProseNode,\n state: CompletionPluginState,\n): EditorCompletionContext {\n const resolved = doc.resolve(state.from);\n const blockStart = resolved.pos - resolved.parentOffset;\n const lineText = resolved.parent.textContent;\n const caretPos = state.from + 1 + state.query.length;\n const column = caretPos - blockStart + 1;\n return { source: lineText, line: 1, column, lineText };\n}\n\n/**\n * Resolve the `[from, to)` doc-position range an item's `insertText` replaces,\n * reusing the SAME pure range math the Monaco path uses (`resolveReplaceRange`)\n * against this textblock's plain text treated as \"line 1\".\n */\nexport function completionReplaceRange(\n doc: ProseNode,\n state: CompletionPluginState,\n item: EditorCompletionItem,\n): { from: number; to: number } {\n const resolved = doc.resolve(state.from);\n const blockStart = resolved.pos - resolved.parentOffset;\n const lineText = resolved.parent.textContent;\n const caretPos = state.from + 1 + state.query.length;\n const column = caretPos - blockStart + 1;\n const range = resolveReplaceRange(item, { lineNumber: 1, column }, lineText, [state.triggerChar]);\n return {\n from: blockStart + (range.startColumn - 1),\n to: blockStart + (range.endColumn - 1),\n };\n}\n\n/** Insert `item` (replacing the resolved range), close the popup, refocus. */\nexport function insertCompletionItem(\n view: EditorView,\n state: CompletionPluginState,\n item: EditorCompletionItem,\n): void {\n const { from, to } = completionReplaceRange(view.state.doc, state, item);\n view.dispatch(\n view.state.tr.insertText(item.insertText, from, to).setMeta(completionsPluginKey, \"close\"),\n );\n view.focus();\n}\n\n/** Options for {@link completionsProsePlugin}. */\nexport interface CompletionsProsePluginOptions {\n /** The widget factory from `useWidgetViewFactory()` (must run under the adapter provider). */\n widgetFactory: CompletionWidgetFactory;\n /** The React component rendered as the widget (built by `createCompletionWidget`). */\n widgetComponent: ReactWidgetViewComponent;\n /** Read live on every transaction / fetch — a rebuilt array is picked up without a rebuild. */\n getProviders: () => EditorCompletionProvider[] | undefined;\n}\n\n/** Build the completions `$prose` plugin. Returns a Milkdown plugin to `.use()`. */\nexport function completionsProsePlugin(options: CompletionsProsePluginOptions): MilkdownPlugin {\n const { widgetFactory, widgetComponent, getProviders } = options;\n\n return $prose(() => {\n return new Plugin<CompletionPluginState>({\n key: completionsPluginKey,\n state: {\n init: () => CLOSED,\n apply: (tr, value) => {\n const providers = getProviders() ?? [];\n const triggerCharacters = Array.from(\n new Set(providers.flatMap((p) => p.triggerCharacters ?? [])),\n );\n return nextCompletionState(value, tr, triggerCharacters);\n },\n },\n props: {\n decorations: (state) => {\n const s = completionsPluginKey.getState(state);\n if (!s?.active) return DecorationSet.empty;\n const factory = widgetFactory({ component: widgetComponent, as: \"span\" });\n const anchor = s.from + 1 + s.query.length; // right at the caret\n const decoration = factory(anchor, {\n side: 1,\n ignoreSelection: true,\n key: `brand-completions:${String(s.from)}:${s.query}:${String(s.items.length)}:${String(s.index)}`,\n });\n return DecorationSet.create(state.doc, [decoration]);\n },\n handleKeyDown: (view, event) => {\n const s = completionsPluginKey.getState(view.state);\n if (!s?.active) return false;\n\n if (event.key === \"Escape\") {\n view.dispatch(view.state.tr.setMeta(completionsPluginKey, \"close\"));\n event.preventDefault();\n return true;\n }\n if (s.items.length === 0) return false;\n\n if (event.key === \"ArrowDown\") {\n const index = (s.index + 1) % s.items.length;\n view.dispatch(view.state.tr.setMeta(completionsPluginKey, { type: \"nav\", index }));\n event.preventDefault();\n return true;\n }\n if (event.key === \"ArrowUp\") {\n const index = (s.index - 1 + s.items.length) % s.items.length;\n view.dispatch(view.state.tr.setMeta(completionsPluginKey, { type: \"nav\", index }));\n event.preventDefault();\n return true;\n }\n if (event.key === \"Enter\" || event.key === \"Tab\") {\n const item = s.items[Math.min(s.index, s.items.length - 1)];\n if (item) {\n insertCompletionItem(view, s, item);\n event.preventDefault();\n return true;\n }\n }\n return false;\n },\n },\n view: () => ({\n update: (view, prevEditorState) => {\n const state = completionsPluginKey.getState(view.state);\n const prev = completionsPluginKey.getState(prevEditorState);\n if (!state?.active) return;\n if (prev?.active && prev.requestId === state.requestId) return;\n\n const providers = (getProviders() ?? []).filter(\n (p) => !p.triggerCharacters || p.triggerCharacters.includes(state.triggerChar),\n );\n if (providers.length === 0) return;\n\n const ctx = buildCompletionContext(view.state.doc, state);\n const requestId = state.requestId;\n collectCompletions(providers, ctx)\n .then((matches) => {\n if (view.isDestroyed) return;\n const current = completionsPluginKey.getState(view.state);\n if (!current?.active || current.requestId !== requestId) return; // stale — superseded\n view.dispatch(\n view.state.tr.setMeta(completionsPluginKey, {\n type: \"items\",\n items: matches.map((m) => m.item),\n requestId,\n }),\n );\n })\n .catch(() => {\n // collectCompletions already degrades a throwing/rejecting provider to\n // \"no items\" internally; this catch only guards an unexpected failure\n // in the .then() itself (e.g. a destroyed view) — never surface it.\n });\n },\n }),\n });\n }) as unknown as MilkdownPlugin;\n}\n","\"use client\";\n\n/**\n * CompletionWidget — the React widget the completions plugin mounts at the\n * caret (the `slash-widget.tsx` precedent). Reads the live plugin state,\n * renders the branded `<CompletionMenu>` floating below the caret, and inserts\n * the chosen candidate through the SAME path as the plugin's own Enter/Tab\n * handler (`insertCompletionItem`), so mouse and keyboard behave identically.\n */\nimport type { ReactWidgetViewComponent } from \"@prosemirror-adapter/react\";\nimport { useWidgetViewContext } from \"@prosemirror-adapter/react\";\nimport { useLayoutEffect, useRef } from \"react\";\n\nimport { CompletionMenu, completionOptionId } from \"./completions-menu\";\nimport {\n completionsPluginKey,\n insertCompletionItem,\n type CompletionPluginState,\n} from \"./completions-prose\";\n\nconst ID_PREFIX = \"brand-completions\";\n\n/** Build the widget component (no external state — everything reads off the view). */\nexport function createCompletionWidget(): ReactWidgetViewComponent {\n function CompletionWidget() {\n const { view } = useWidgetViewContext();\n const wrapperRef = useRef<HTMLSpanElement>(null);\n\n const state = completionsPluginKey.getState(view.state) as CompletionPluginState | undefined;\n const items = state?.items ?? [];\n const activeIndex = state ? Math.min(state.index, Math.max(0, items.length - 1)) : 0;\n const activeId = items.length > 0 ? completionOptionId(ID_PREFIX, activeIndex) : undefined;\n\n // Mirror the active option onto the editor's `textbox` for AT, same wiring\n // as the slash widget.\n useLayoutEffect(() => {\n const dom = view.dom as HTMLElement;\n const listEl = wrapperRef.current?.querySelector<HTMLElement>('[role=\"listbox\"]');\n if (listEl && !listEl.id) listEl.id = `${ID_PREFIX}-listbox`;\n dom.setAttribute(\"aria-expanded\", \"true\");\n if (listEl) dom.setAttribute(\"aria-controls\", listEl.id);\n if (activeId) dom.setAttribute(\"aria-activedescendant\", activeId);\n else dom.removeAttribute(\"aria-activedescendant\");\n return () => {\n dom.removeAttribute(\"aria-expanded\");\n dom.removeAttribute(\"aria-controls\");\n dom.removeAttribute(\"aria-activedescendant\");\n };\n }, [view, activeId]);\n\n const onSelect = (index: number) => {\n if (!state) return;\n const item = items[index];\n if (item) insertCompletionItem(view, state, item);\n };\n\n return (\n <span\n ref={wrapperRef}\n contentEditable={false}\n className=\"brand-completions-anchor relative inline-block h-0 w-0 align-baseline\"\n >\n <span className=\"absolute left-0 top-1 z-50 block\">\n <CompletionMenu\n items={items}\n activeIndex={activeIndex}\n onSelect={onSelect}\n idPrefix={ID_PREFIX}\n />\n </span>\n </span>\n );\n }\n return CompletionWidget;\n}\n","\"use client\";\n\n/**\n * CompletionMenu — the token-styled listbox the WYSIWYG completions widget\n * renders at the caret (`completions-widget.tsx`). Deliberately simpler than\n * `../slash/slash-menu.tsx`'s `SlashMenu`: `EditorCompletionItem` carries only\n * `label`/`detail`/`insertText` (no groups/icons), so there is nothing to group.\n * Same visual grammar (bg-popover, accent selection) for consistency with the\n * slash popup and Monaco's own themed suggest widget.\n */\nimport { useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { forwardRef, type HTMLAttributes } from \"react\";\n\nimport type { EditorCompletionItem } from \"../../lib/editor-completions\";\n\nexport interface CompletionMenuProps extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n /** The candidates to show (already resolved by the provider(s)). */\n items: EditorCompletionItem[];\n /** Index of the highlighted candidate. */\n activeIndex: number;\n /** Called when a candidate is chosen (click or the plugin's Enter/Tab). */\n onSelect: (index: number) => void;\n /** DOM id prefix so each option id is stable + unique. */\n idPrefix?: string;\n /** Shown while no candidates have resolved yet (or none matched). */\n emptyLabel?: string;\n}\n\n/** Build the stable DOM id for an option element. */\nexport function completionOptionId(idPrefix: string, index: number): string {\n return `${idPrefix}-${index}`;\n}\n\nexport const CompletionMenu = forwardRef<HTMLDivElement, CompletionMenuProps>(\n function CompletionMenu(\n {\n items,\n activeIndex,\n onSelect,\n idPrefix = \"brand-completions\",\n emptyLabel: emptyLabelProp,\n className,\n ...props\n },\n ref,\n ) {\n const { t } = useLocale();\n const emptyLabel = emptyLabelProp ?? t(\"editor.completions.noSuggestions\");\n return (\n <div\n ref={ref}\n role=\"listbox\"\n aria-label={t(\"editor.completions.suggestions\")}\n className={cn(\n \"max-h-[min(280px,50vh)] w-64 overflow-y-auto overflow-x-hidden rounded-md bg-popover p-1 text-popover-foreground shadow-ring-md\",\n className,\n )}\n {...props}\n >\n {items.length === 0 ? (\n <div className=\"px-2 py-3 text-center text-caption text-muted-foreground\">\n {emptyLabel}\n </div>\n ) : (\n items.map((item, index) => {\n const selected = index === activeIndex;\n return (\n <div\n key={`${item.label}-${String(index)}`}\n id={completionOptionId(idPrefix, index)}\n role=\"option\"\n aria-selected={selected}\n data-selected={selected ? \"true\" : undefined}\n // The editor keeps focus — select on mousedown (before the editor\n // would steal focus back), mirroring `slash-menu.tsx`.\n onMouseDown={(e) => {\n e.preventDefault();\n onSelect(index);\n }}\n className={cn(\n \"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-body outline-none transition-colors duration-fast\",\n \"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground\",\n )}\n >\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{item.label}</span>\n {item.detail ? (\n <span className=\"truncate text-meta text-muted-foreground\">{item.detail}</span>\n ) : null}\n </span>\n </div>\n );\n })\n )}\n </div>\n );\n },\n);\n","\"use client\";\n\n/**\n * WYSIWYG completions mirror — public surface for the Milkdown `MarkdownEditor`\n * (the `../slash/index.ts` precedent). See `completions-prose.ts` for what this\n * mirror does and does NOT cover relative to the Monaco path.\n */\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\n\nimport type { EditorCompletionProvider } from \"../../lib/editor-completions\";\nimport { completionsProsePlugin, type CompletionWidgetFactory } from \"./completions-prose\";\nimport { createCompletionWidget } from \"./completions-widget\";\n\n/**\n * Build the `.use()`-ready plugin array for the WYSIWYG completions mirror.\n * Call with the widget factory from `useWidgetViewFactory()` (must run inside a\n * `<ProsemirrorAdapterProvider>`), mirroring `brandSlashViewPlugins`.\n */\nexport function completionsViewPlugins(\n widgetFactory: CompletionWidgetFactory,\n getProviders: () => EditorCompletionProvider[] | undefined,\n): MilkdownPlugin[] {\n const widgetComponent = createCompletionWidget();\n return [completionsProsePlugin({ widgetFactory, widgetComponent, getProviders })];\n}\n\nexport {\n CLOSED,\n completionsPluginKey,\n nextCompletionState,\n buildCompletionContext,\n completionReplaceRange,\n insertCompletionItem,\n type CompletionPluginState,\n type CompletionWidgetFactory,\n} from \"./completions-prose\";\nexport { CompletionMenu, completionOptionId, type CompletionMenuProps } from \"./completions-menu\";\n","\"use client\";\n\n/**\n * React node-views for the brand `:::` directives.\n *\n * Upgrades the Milkdown (ProseMirror) WYSIWYG surface from token-styled `toDOM`\n * chrome (see directive-nodes.ts) to the ACTUAL @brand React components, rendered\n * live INSIDE the editor via @prosemirror-adapter/react:\n *\n * :::card → real <Card> (title editable inline; body = editable content)\n * :::callout → real <Alert> (title editable inline; body = editable content)\n * ::metric → real <MetricBlock> (label + value editable inline; atomic)\n * :::timeline → branded frame around the editable list (kept editable, not a\n * derived <Timeline>, so steps round-trip as real markdown list items)\n *\n * Inline edits write through `setAttrs` to the directive node's `attributes`, which\n * the existing `toMarkdown` runner serializes back to `:::name{key=\"value\"}` — so the\n * round-trip stays lossless (the editor and the Streamdown preview share one dialect).\n *\n * The `toDOM` definitions in directive-nodes.ts remain as the schema's serialization\n * fallback (clipboard / no-adapter); when these node-views are registered, ProseMirror\n * renders them instead.\n */\nimport {\n Alert,\n AlertDescription,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n ContextMenu,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n useLocale,\n} from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { editorViewCtx, parserCtx, serializerCtx } from \"@milkdown/kit/core\";\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport type { Node as ProseNode } from \"@milkdown/kit/prose/model\";\nimport { $view } from \"@milkdown/kit/utils\";\nimport { useNodeViewContext, type useNodeViewFactory } from \"@prosemirror-adapter/react\";\nimport {\n ArrowLeftRight,\n FileText,\n Grid3x3,\n LayoutGrid,\n MoreHorizontal,\n Pencil,\n Repeat2,\n} from \"lucide-react\";\nimport { useContext, useEffect, useRef, type KeyboardEvent, type ReactNode } from \"react\";\n\nimport { IterationEditContext } from \"../markdown-iteration/edit-context\";\nimport type { IterationLayout } from \"../markdown-iteration/iteration\";\nimport {\n builderValueFromParts,\n directivePartsFromValue,\n evaluateEmbedded,\n ITERATION_LAYOUTS,\n staticMarkdownFromValue,\n transposeIterationValue,\n} from \"../markdown-iteration/iteration-builder\";\nimport { MetricBlock } from \"../metric-block\";\nimport { containerDirectiveSchema, leafDirectiveSchema } from \"./directive-nodes\";\nimport { useInstance } from \"./milkdown-react\";\n\ntype Attrs = Record<string, string>;\n\n/** Map a callout `type` to an @elabs-ai/components-ui Alert variant (mirrors the preview). */\nconst CALLOUT_VARIANT: Record<string, \"default\" | \"info\" | \"success\" | \"warning\" | \"destructive\"> =\n {\n info: \"info\",\n note: \"info\",\n tip: \"success\",\n success: \"success\",\n warning: \"warning\",\n caution: \"warning\",\n danger: \"destructive\",\n error: \"destructive\",\n destructive: \"destructive\",\n };\n\nfunction capitalize(s: string): string {\n return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;\n}\n\n/** Read the directive node's `name` + `attributes`, with a writer that round-trips. */\nfunction useDirectiveAttrs() {\n const { node, setAttrs } = useNodeViewContext();\n const name = String(node.attrs.name);\n const attributes = (node.attrs.attributes ?? {}) as Attrs;\n const update = (key: string, value: string) => {\n const next: Attrs = { ...attributes };\n if (value === \"\") delete next[key];\n else next[key] = value;\n setAttrs({ attributes: next });\n };\n return { name, attributes, update };\n}\n\ninterface InlineEditProps {\n value: string;\n onCommit: (value: string) => void;\n ariaLabel: string;\n placeholder?: string;\n className?: string;\n}\n\n/**\n * A seamless inline editor for a single directive attribute.\n *\n * Uncontrolled `contentEditable` (so the caret never jumps mid-type), syncing the\n * DOM text from `value` only while NOT focused, and committing on blur / Enter\n * (Escape reverts). Marked `data-directive-chrome` so the node-view's `stopEvent`\n * routes its keystrokes to the browser, not ProseMirror.\n */\nfunction InlineEdit({ value, onCommit, ariaLabel, placeholder, className }: InlineEditProps) {\n const ref = useRef<HTMLSpanElement>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n // Don't clobber the user's text while they're editing this field.\n if (el === el.ownerDocument.activeElement) return;\n if (el.textContent !== value) el.textContent = value;\n }, [value]);\n\n const commit = () => {\n const next = (ref.current?.textContent ?? \"\").trim();\n if (next !== value) onCommit(next);\n };\n\n const onKeyDown = (e: KeyboardEvent<HTMLSpanElement>) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n e.currentTarget.blur();\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n if (ref.current) ref.current.textContent = value;\n e.currentTarget.blur();\n }\n };\n\n return (\n <span\n ref={ref}\n role=\"textbox\"\n aria-label={ariaLabel}\n // Single-line field: Enter commits (it never inserts a newline).\n aria-multiline={false}\n data-directive-chrome=\"\"\n data-placeholder={placeholder}\n contentEditable\n suppressContentEditableWarning\n tabIndex={0}\n spellCheck={false}\n onBlur={commit}\n onKeyDown={onKeyDown}\n className={cn(\"brand-inline-edit rounded-sm focus-ring\", className)}\n />\n );\n}\n\n/** `:::name` block directives → live @brand component with an editable body. */\nfunction ContainerDirectiveView() {\n const { t } = useLocale();\n const { contentRef } = useNodeViewContext();\n const { name, attributes, update } = useDirectiveAttrs();\n\n // The editable ProseMirror content (the directive body) mounts here.\n const body = <div className=\"brand-directive__body\" ref={contentRef} />;\n\n if (name === \"card\") {\n return (\n <Card className=\"brand-directive brand-directive--card\" data-brand-directive=\"card\">\n <CardHeader className=\"pb-3\">\n <CardTitle>\n <InlineEdit\n ariaLabel={t(\"editor.directiveViews.cardTitle\")}\n placeholder={t(\"editor.directiveViews.cardTitle\")}\n value={attributes.title ?? \"\"}\n onCommit={(v) => update(\"title\", v)}\n />\n </CardTitle>\n </CardHeader>\n <CardContent>{body}</CardContent>\n </Card>\n );\n }\n\n if (name === \"callout\") {\n const variant = CALLOUT_VARIANT[attributes.type ?? \"\"] ?? \"default\";\n return (\n <Alert\n variant={variant}\n className=\"brand-directive brand-directive--callout\"\n data-brand-directive=\"callout\"\n >\n {/* Non-heading title (matches the preview): a callout sits inside content\n flow, so its label must not join the document heading outline (see #21). */}\n <div className=\"mb-1 font-medium leading-none tracking-tight\">\n <InlineEdit\n ariaLabel={t(\"editor.directiveViews.calloutTitle\")}\n placeholder={capitalize(attributes.type ?? \"note\")}\n value={attributes.title ?? \"\"}\n onCommit={(v) => update(\"title\", v)}\n />\n </div>\n <AlertDescription>{body}</AlertDescription>\n </Alert>\n );\n }\n\n if (name === \"timeline\") {\n // Kept as an editable list inside a branded frame — a derived <Timeline> can't\n // be edited in place, and the steps must serialize back as markdown list items.\n return (\n <div className=\"brand-directive brand-directive--timeline\" data-brand-directive=\"timeline\">\n {body}\n </div>\n );\n }\n\n if (name === \"iterate\" || name === \"pivot\") {\n // Owns hooks (dialog state + editor instance) → its own component.\n return <IterationDirectiveView />;\n }\n\n // Unknown container directive — surface it, but never drop the body content.\n // `role=\"note\"` (not Alert's default assertive `role=\"alert\"`): this is a\n // PERSISTENT block that re-renders on every re-parse, so an assertive live region\n // would re-announce on each keystroke (#37). The title is a label, not a heading.\n return (\n <Alert\n role=\"note\"\n variant=\"destructive\"\n className=\"brand-directive brand-directive--unknown\"\n data-brand-directive={name}\n >\n <div className=\"mb-1 font-medium leading-none tracking-tight\">\n {t(\"editor.directiveViews.unknownBlock\", { name })}\n </div>\n <AlertDescription>{body}</AlertDescription>\n </Alert>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Iteration node-view (`:::iterate` / `:::pivot`) + the ⋯ re-edit modal */\n/* ------------------------------------------------------------------ */\n\ntype GetEditor = () =>\n | { ctx: { get: (token: unknown) => unknown }; action: <T>(fn: (ctx: unknown) => T) => T }\n | undefined;\n\n/** Serialize a directive node's BODY content back to markdown (the template). */\nfunction readBodyMarkdown(getInstance: GetEditor, node: ProseNode): string {\n try {\n const editor = getInstance();\n if (!editor) return node.textContent;\n return editor\n .action((ctx) => {\n const serialize = (ctx as { get: (t: unknown) => (n: ProseNode) => string }).get(\n serializerCtx,\n );\n const doc = node.type.schema.topNodeType.create(null, node.content);\n return serialize(doc);\n })\n .trim();\n } catch {\n // Serializer unavailable (e.g. SSR/edge) — fall back to the plain text body.\n return node.textContent;\n }\n}\n\n/** Replace a directive node's BODY with markdown parsed back into PM content. */\nfunction writeBodyMarkdown(\n getInstance: GetEditor,\n getPos: () => number | undefined,\n template: string,\n): void {\n try {\n const editor = getInstance();\n const pos = getPos();\n if (!editor || pos == null) return;\n editor.action((ctx) => {\n const parse = (ctx as { get: (t: unknown) => (md: string) => ProseNode | null }).get(\n parserCtx,\n );\n const parsed = parse(template);\n if (!parsed) return;\n const view = (\n ctx as {\n get: (t: unknown) => {\n state: { doc: ProseNode; tr: unknown };\n dispatch: (tr: unknown) => void;\n };\n }\n ).get(editorViewCtx);\n const node = view.state.doc.nodeAt(pos);\n if (!node) return;\n const start = pos + 1;\n const end = start + node.content.size;\n const tr = (\n view.state.tr as { replaceWith: (from: number, to: number, content: unknown) => unknown }\n ).replaceWith(start, end, parsed.content);\n view.dispatch(tr);\n });\n } catch {\n // Parser unavailable / position stale — leave the inline body as the source.\n }\n}\n\n/**\n * Merge `next` into `attrs`, OMITTING any key whose value is an empty string\n * rather than writing it. `directivePartsFromValue` always computes `rows`/\n * `cols`/`values` as a joined string — `\"\"` when the list is empty — and an\n * empty-string attribute value serializes as a BARE flag (`rows` with no\n * `=\"…\"`) via mdast-util-directive, which a consumer's `evaluate` then sees as\n * `attributes.rows === \"\"` instead of `undefined` (a real behaviour-flip risk\n * for any consumer that branches on attribute presence). Every writer that\n * rewrites the builder-known keys (`as`/`layout`/`values`/`rows`/`cols`) with a\n * possibly-empty computed value routes through this instead of a raw spread —\n * that's also what keeps a consumer's OWN attributes (e.g. `source`/`region`,\n * unknown to the builder model) intact: only the keys `next` actually names\n * are touched, everything else in `attrs` passes through untouched.\n */\nfunction mergeAttrsOmittingEmpty(\n attrs: Record<string, string>,\n next: Record<string, string>,\n): Record<string, string> {\n const merged: Record<string, string> = { ...attrs };\n for (const [key, value] of Object.entries(next)) {\n if (value === \"\") delete merged[key];\n else merged[key] = value;\n }\n return merged;\n}\n\n/**\n * Replace the ENTIRE directive node (not just its body) with parsed markdown —\n * the \"Convert to static\" node-menu action's writer. A no-op on blank markdown\n * (nothing resolved yet — e.g. no values entered) so the block is never\n * clobbered with empty content.\n */\nfunction replaceNodeWithMarkdown(\n getInstance: GetEditor,\n getPos: () => number | undefined,\n markdown: string,\n): void {\n if (!markdown.trim()) return;\n try {\n const editor = getInstance();\n const pos = getPos();\n if (!editor || pos == null) return;\n editor.action((ctx) => {\n const parse = (ctx as { get: (t: unknown) => (md: string) => ProseNode | null }).get(\n parserCtx,\n );\n const parsed = parse(markdown);\n if (!parsed) return;\n const view = (\n ctx as {\n get: (t: unknown) => {\n state: { doc: ProseNode; tr: unknown };\n dispatch: (tr: unknown) => void;\n };\n }\n ).get(editorViewCtx);\n const node = view.state.doc.nodeAt(pos);\n if (!node) return;\n const tr = (\n view.state.tr as { replaceWith: (from: number, to: number, content: unknown) => unknown }\n ).replaceWith(pos, pos + node.nodeSize, parsed.content);\n view.dispatch(tr);\n });\n } catch {\n // Parser/position unavailable — leave the node as a live directive.\n }\n}\n\n/* -------------------------------------------------------------------- */\n/* The iteration node MENU (⋯ dropdown AND right-click context menu) */\n/* -------------------------------------------------------------------- */\n\ninterface IterationMenuAction {\n type: \"item\";\n id: string;\n label: string;\n icon: ReactNode;\n onSelect: () => void;\n /**\n * Disable the item (e.g. \"Transpose\"/\"Convert to static\" with no resolvable\n * data yet). The WHY must be folded into `label` itself — a disabled Radix\n * menu item is `pointer-events-none` (a `title` tooltip can never fire) and\n * skipped by keyboard roving-focus, so `title` is not a viable explanation\n * channel for any input modality.\n */\n disabled?: boolean;\n}\n\ninterface IterationMenuLayoutGroup {\n type: \"layout\";\n id: \"layout\";\n label: string;\n icon: ReactNode;\n value: IterationLayout;\n options: IterationLayout[];\n onChange: (layout: IterationLayout) => void;\n}\n\ntype IterationMenuEntry = IterationMenuAction | IterationMenuLayoutGroup;\n\n/**\n * Render one shared item list into EITHER the `⋯` dropdown or the right-click\n * context menu — the two surfaces the AC requires to expose the SAME actions\n * (#223). Taking `entries` as data (rather than duplicating JSX per menu type)\n * means the two menus can never diverge: a new action is added once, here.\n */\nfunction IterationMenuItems({\n kind,\n entries,\n}: {\n kind: \"dropdown\" | \"context\";\n entries: IterationMenuEntry[];\n}) {\n const isDropdown = kind === \"dropdown\";\n const Item = isDropdown ? DropdownMenuItem : ContextMenuItem;\n const Sub = isDropdown ? DropdownMenuSub : ContextMenuSub;\n const SubTrigger = isDropdown ? DropdownMenuSubTrigger : ContextMenuSubTrigger;\n const SubContent = isDropdown ? DropdownMenuSubContent : ContextMenuSubContent;\n const RadioGroup = isDropdown ? DropdownMenuRadioGroup : ContextMenuRadioGroup;\n const RadioItem = isDropdown ? DropdownMenuRadioItem : ContextMenuRadioItem;\n\n return (\n <>\n {entries.map((entry) => {\n if (entry.type === \"layout\") {\n return (\n <Sub key={entry.id}>\n <SubTrigger className=\"gap-2\">\n {entry.icon}\n {entry.label}\n </SubTrigger>\n <SubContent>\n <RadioGroup\n value={entry.value}\n onValueChange={(next) => entry.onChange(next as IterationLayout)}\n >\n {entry.options.map((option) => (\n <RadioItem key={option} value={option} className=\"capitalize\">\n {option}\n </RadioItem>\n ))}\n </RadioGroup>\n </SubContent>\n </Sub>\n );\n }\n return (\n <Item key={entry.id} onSelect={entry.onSelect} disabled={entry.disabled}>\n {entry.icon}\n {entry.label}\n </Item>\n );\n })}\n </>\n );\n}\n\n/**\n * `:::iterate` / `:::pivot` node-view: the body IS the per-cell TEMPLATE (with\n * `{{tokens}}`), kept editable inline in a quiet labelled frame (accent rail, no\n * fill). When the consumer provides an {@link IterationEditContext} handler, a\n * `⋯` button AND a right-click both open the SAME node menu (#223):\n * - \"Edit iteration…\" — the existing guided re-edit (`requestEdit`).\n * - \"Change layout\" — rewrites the `layout` attribute directly (no dialog).\n * - \"Transpose\" (pivot only) — swaps the rows/cols value lists.\n * - \"Convert to static\" — replaces the directive with its populated markdown.\n * With no handler wired, neither menu renders and the body stays editable\n * inline (today's behaviour, unchanged).\n */\nfunction IterationDirectiveView() {\n const { t } = useLocale();\n const { contentRef, node, getPos, setAttrs } = useNodeViewContext();\n const { name, attributes } = useDirectiveAttrs();\n const [, getInstance] = useInstance();\n const onEdit = useContext(IterationEditContext);\n\n const isPivot = name === \"pivot\";\n const kind: \"iterate\" | \"pivot\" = isPivot ? \"pivot\" : \"iterate\";\n const Icon = isPivot ? Grid3x3 : Repeat2;\n\n const requestEdit = () => {\n onEdit?.({\n kind,\n template: readBodyMarkdown(getInstance as GetEditor, node),\n // A5: hand the current attributes (value lists, bind name, layout) to the\n // handler so the GUIDED builder can reopen with its data — and a writer that\n // round-trips BOTH the attributes and the body, not just the template.\n attributes: { ...(attributes as Record<string, string>) },\n onSave: (template) => writeBodyMarkdown(getInstance as GetEditor, getPos, template),\n // MERGE the guided builder's write-back into the EXISTING attributes rather\n // than replacing the whole record — `directivePartsFromValue` only knows\n // about `as`/`layout`/`values`/`rows`/`cols`, so a naive\n // `setAttrs({ attributes: nextAttrs })` would silently drop every other\n // attribute the directive carries (e.g. a consumer's `source`/`region`\n // reference — `containerDirectiveSchema.attrs.attributes` is a free-form\n // record, and those keys are load-bearing for the consumer's `evaluate`).\n // Mirrors the `transpose()` fix below.\n onSaveData: ({ attributes: nextAttrs, template }) => {\n setAttrs({\n attributes: mergeAttrsOmittingEmpty(attributes as Record<string, string>, nextAttrs),\n });\n writeBodyMarkdown(getInstance as GetEditor, getPos, template);\n },\n onSetAttributes: (nextAttrs) => setAttrs({ attributes: nextAttrs }),\n onReplaceWithMarkdown: (markdown) =>\n replaceNodeWithMarkdown(getInstance as GetEditor, getPos, markdown),\n });\n };\n\n const setLayout = (layout: IterationLayout) => {\n setAttrs({ attributes: { ...(attributes as Record<string, string>), layout } });\n };\n\n /**\n * Swap a pivot's rows/cols in place — a no-op template read (transpose never\n * touches the body), so it doesn't need the (async-ish) `readBodyMarkdown`.\n *\n * MERGES the transposed `rows`/`cols` into the EXISTING attributes rather than\n * replacing the whole record (via `mergeAttrsOmittingEmpty`) — so a consumer's\n * OTHER attributes (e.g. `source`/`region`) survive, AND an empty transposed\n * axis is OMITTED rather than written as `\"\"` (which mdast-util-directive would\n * serialize as a bare `rows`/`cols` FLAG, not a genuinely-absent attribute). The\n * menu gates this action on `hasEmbeddedData` below, so in practice both axes\n * are always non-empty here — this stays defensive rather than load-bearing.\n */\n const transpose = () => {\n const seed = builderValueFromParts(kind, attributes as Record<string, string>, \"\");\n const { attributes: transposed } = directivePartsFromValue(transposeIterationValue(seed));\n setAttrs({\n attributes: mergeAttrsOmittingEmpty(attributes as Record<string, string>, {\n rows: transposed.rows ?? \"\",\n cols: transposed.cols ?? \"\",\n }),\n });\n };\n\n // \"Transpose\" (pivot) and \"Convert to static\" only have anything to act on\n // when the block's data is EMBEDDED in its own attributes (`values=` /\n // `rows=`×`cols=`) — the built-in `evaluateEmbedded` resolver. A block whose\n // cells come from the consumer's `evaluate` instead (e.g. `source=\"repos\"`)\n // resolves to zero cells here — Transpose would be a pure visual no-op that\n // still DIRTIES the document (writing bare `rows`/`cols` flags), and Convert\n // to static has nothing to flatten — so both are disabled, with the reason\n // folded into the visible LABEL (not `title`): a disabled Radix menu item is\n // both `pointer-events-none` (no hover tooltip can ever fire) and skipped by\n // keyboard roving-focus, so `title` is unreachable by any input modality —\n // the rendered text is the only place a reason can actually be read.\n const hasEmbeddedData =\n evaluateEmbedded({\n kind,\n layout: (attributes.layout as IterationLayout) || ITERATION_LAYOUTS[kind][0]!,\n template: \"\",\n as: attributes.as || \"item\",\n attributes: attributes as Record<string, string>,\n }).cells.length > 0;\n\n const disabledHint = t(\"editor.directiveViews.needsEmbeddedValues\");\n\n const convertToStatic = () => {\n const template = readBodyMarkdown(getInstance as GetEditor, node);\n const value = builderValueFromParts(kind, attributes as Record<string, string>, template);\n replaceNodeWithMarkdown(getInstance as GetEditor, getPos, staticMarkdownFromValue(value));\n };\n\n const menuEntries: IterationMenuEntry[] = [\n {\n type: \"item\",\n id: \"edit\",\n label: t(\"editor.directiveViews.editIteration\"),\n icon: <Pencil className=\"size-4\" aria-hidden=\"true\" />,\n onSelect: requestEdit,\n },\n {\n type: \"layout\",\n id: \"layout\",\n label: t(\"editor.directiveViews.changeLayout\"),\n icon: <LayoutGrid className=\"size-4\" aria-hidden=\"true\" />,\n value: (attributes.layout as IterationLayout) || ITERATION_LAYOUTS[kind][0]!,\n options: ITERATION_LAYOUTS[kind],\n onChange: setLayout,\n },\n ...(isPivot\n ? [\n {\n type: \"item\",\n id: \"transpose\",\n label: hasEmbeddedData\n ? t(\"editor.directiveViews.transpose\")\n : `${t(\"editor.directiveViews.transpose\")} ${disabledHint}`,\n icon: <ArrowLeftRight className=\"size-4\" aria-hidden=\"true\" />,\n onSelect: transpose,\n disabled: !hasEmbeddedData,\n } satisfies IterationMenuAction,\n ]\n : []),\n {\n type: \"item\",\n id: \"convert-to-static\",\n label: hasEmbeddedData\n ? t(\"editor.directiveViews.convertToStatic\")\n : `${t(\"editor.directiveViews.convertToStatic\")} ${disabledHint}`,\n icon: <FileText className=\"size-4\" aria-hidden=\"true\" />,\n onSelect: convertToStatic,\n disabled: !hasEmbeddedData,\n },\n ];\n\n const header = (\n <div className=\"mb-1.5 flex items-center gap-1.5 text-meta font-medium text-info-text\">\n <Icon className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span>{isPivot ? t(\"editor.directiveViews.pivot\") : t(\"editor.directiveViews.iterate\")}</span>\n {!isPivot && attributes.as ? (\n <span className=\"font-normal text-muted-foreground\">\n {t(\"editor.directiveViews.perItem\", { as: attributes.as })}\n </span>\n ) : null}\n <span className=\"font-normal text-muted-foreground\">\n {t(\"editor.directiveViews.templateSuffix\")}\n </span>\n {onEdit ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n // `data-directive-chrome` routes the click to the browser, not ProseMirror.\n data-directive-chrome=\"\"\n aria-label={t(\"editor.directiveViews.iterationActions\")}\n title={t(\"editor.directiveViews.iterationActionsTitle\")}\n className=\"ms-auto inline-flex size-5 items-center justify-center rounded-sm text-muted-foreground hover:bg-accent hover:text-foreground focus-ring\"\n >\n <MoreHorizontal className=\"size-4\" aria-hidden=\"true\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <IterationMenuItems kind=\"dropdown\" entries={menuEntries} />\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n );\n\n const body = (\n // The editable template body (inline ProseMirror content).\n <div className=\"brand-directive__body\" ref={contentRef} />\n );\n\n if (!onEdit) {\n // No consumer handler wired — no re-edit / node-menu affordance (today's\n // behaviour); the template stays editable inline only.\n return (\n <div\n className=\"brand-directive brand-directive--iterate border-s-2 border-s-info ps-3\"\n data-brand-directive={name}\n >\n {header}\n {body}\n </div>\n );\n }\n\n return (\n <ContextMenu>\n <div\n className=\"brand-directive brand-directive--iterate border-s-2 border-s-info ps-3\"\n data-brand-directive={name}\n >\n {/*\n * Scoped to the HEADER chrome only — NOT the editable template body.\n * Radix's ContextMenuTrigger calls `event.preventDefault()` on every\n * `contextmenu` inside its child, so wrapping the whole frame (header +\n * body) hijacked the browser's native context menu (spellcheck\n * suggestions, Paste, Look Up, Emoji) for right-clicks inside the\n * editable template text. Right-click still opens this SAME menu from\n * the header row; the body keeps its native menu.\n */}\n <ContextMenuTrigger asChild>{header}</ContextMenuTrigger>\n {body}\n </div>\n <ContextMenuContent>\n <IterationMenuItems kind=\"context\" entries={menuEntries} />\n </ContextMenuContent>\n </ContextMenu>\n );\n}\n\n/** `::name` leaf directives (e.g. `::metric`) → live, atomic @brand component. */\nfunction LeafDirectiveView() {\n const { t } = useLocale();\n const { name, attributes, update } = useDirectiveAttrs();\n\n if (name !== \"metric\") {\n return (\n <div\n className=\"brand-directive brand-directive--leaf brand-directive--unknown rounded-md border border-destructive/40 bg-surface-muted p-3 text-sm text-muted-foreground\"\n data-brand-leaf={name}\n >\n {t(\"editor.directiveViews.unknownInlineBlock\")} <code>::{name}</code>\n </div>\n );\n }\n\n const delta = attributes.delta;\n return (\n <MetricBlock\n className=\"brand-directive brand-directive--leaf brand-directive--metric\"\n data-brand-leaf=\"metric\"\n label={\n <InlineEdit\n ariaLabel={t(\"editor.directiveViews.metricLabel\")}\n placeholder={t(\"editor.directiveViews.metricLabelPlaceholder\")}\n value={attributes.label ?? \"\"}\n onCommit={(v) => update(\"label\", v)}\n />\n }\n value={\n <InlineEdit\n ariaLabel={t(\"editor.directiveViews.metricValue\")}\n placeholder={t(\"editor.directiveViews.metricValuePlaceholder\")}\n value={attributes.value ?? \"\"}\n onCommit={(v) => update(\"value\", v)}\n className=\"min-w-[1ch]\"\n />\n }\n description={attributes.description}\n delta={delta}\n deltaDirection={delta?.startsWith(\"+\") ? \"up\" : delta?.startsWith(\"-\") ? \"down\" : \"neutral\"}\n />\n );\n}\n\n/**\n * Keep keystrokes inside an editable attribute field out of ProseMirror's hands —\n * but ONLY for chrome that belongs to THIS node-view, not an ancestor's. ProseMirror\n * asks the node-view whose `dom` contains the event, and `@prosemirror-adapter` marks\n * every node-view root with `data-node-view-root`. So we walk up from the event target\n * and stop ONLY if we reach a `[data-directive-chrome]` element WITHOUT first crossing\n * a node-view-root boundary. This bounds the capture to the current node-view — a\n * nested directive can't swallow events meant for its parent's chrome, and vice versa\n * (#37). A shared, unbounded `closest()` could reach an ancestor's chrome.\n */\nfunction directiveStopEvent(event: Event): boolean {\n let node = event.target;\n while (node instanceof HTMLElement) {\n if (node.hasAttribute(\"data-directive-chrome\")) return true;\n // Reached this node-view's own root without finding chrome → any match above\n // belongs to an ancestor node-view; don't capture for it.\n if (node.hasAttribute(\"data-node-view-root\")) return false;\n node = node.parentElement;\n }\n return false;\n}\n\n/**\n * Build the `$view` plugins that bind the directive schemas to their React\n * node-views. Call with the factory from `useNodeViewFactory()` (so it must run\n * inside a `<ProsemirrorAdapterProvider>`), then `.use()` the result on the editor.\n */\nexport function directiveViewPlugins(\n nodeViewFactory: ReturnType<typeof useNodeViewFactory>,\n): MilkdownPlugin[] {\n return [\n $view(containerDirectiveSchema.node, () =>\n nodeViewFactory({\n component: ContainerDirectiveView,\n as: \"div\",\n contentAs: \"div\",\n stopEvent: directiveStopEvent,\n }),\n ),\n $view(leafDirectiveSchema.node, () =>\n nodeViewFactory({\n component: LeafDirectiveView,\n as: \"div\",\n stopEvent: directiveStopEvent,\n }),\n ),\n ].flat() as MilkdownPlugin[];\n}\n","\"use client\";\n\n/**\n * Vendored + adapted from `@milkdown/react` (MIT — © 2020-present Mirone).\n *\n * `<MilkdownProvider>` holds the editor instance/loading state in context;\n * `<Milkdown />` renders the root node the ProseMirror view mounts into.\n */\nimport type { Editor } from \"@milkdown/kit/core\";\nimport { type FC, type ReactNode, useMemo, useRef, useState } from \"react\";\n\nimport type { EditorInfoCtx, GetEditor } from \"./types\";\nimport { editorInfoContext, useGetEditor } from \"./use-get-editor\";\n\nexport const Milkdown: FC = () => {\n const domRef = useGetEditor();\n\n return <div data-milkdown-root ref={domRef} />;\n};\n\nexport const MilkdownProvider: FC<{ children: ReactNode }> = ({ children }) => {\n const dom = useRef<HTMLDivElement | undefined>(undefined);\n const [editorFactory, setEditorFactory] = useState<GetEditor | undefined>(undefined);\n const editor = useRef<Editor | undefined>(undefined);\n const [loading, setLoading] = useState(true);\n\n const editorInfoCtx = useMemo<EditorInfoCtx>(\n () => ({ loading, dom, editor, setLoading, editorFactory, setEditorFactory }),\n [loading, editorFactory],\n );\n\n return <editorInfoContext.Provider value={editorInfoCtx}>{children}</editorInfoContext.Provider>;\n};\n","/**\n * Vendored + adapted from `@milkdown/react` (MIT — © 2020-present Mirone).\n *\n * The actual ProseMirror mount/destroy lifecycle. This is the file that matters\n * for React 19 StrictMode: the effect kicks off an async `editor.create()` on\n * mount and `editor.destroy()` on cleanup. StrictMode runs mount → cleanup →\n * mount in dev, so a destroy can fire while the first create is still pending —\n * which `@milkdown/core`'s Editor guards (it defers destroy until OnCreate\n * settles), so the double-mount converges to a single editor. The co-located\n * `markdown-editor.strictmode.test.tsx` is the gate that proves that convergence\n * for our vendored copy; harden here only if that test ever shows >1 surface.\n *\n * DESTROY gets the same rigor (issue #65). Effect cleanup fires\n * `editor.destroy()` but nothing awaited it, and `@milkdown/ctx` schedules its\n * own internal async cleanup (a timer) inside that promise — so an unmount that\n * doesn't wait can let the timer fire after Vitest has already recycled the\n * file's jsdom environment (`ReferenceError: removeEventListener is not\n * defined`). `pendingDestroys` tracks every in-flight `destroy()`, and\n * `waitForPendingMilkdownTeardown()` lets a caller — a test's `afterEach`, or any\n * consumer that cares — await \"every Milkdown teardown has settled\" the same way\n * `create()` already converges before this effect resolves.\n */\nimport { createContext, useContext, useEffect, useRef } from \"react\";\n\nimport type { EditorInfoCtx } from \"./types\";\n\nexport const editorInfoContext = createContext<EditorInfoCtx>({} as EditorInfoCtx);\n\n/** Module-scoped registry of in-flight Milkdown `destroy()` promises. Not part\n * of the package's public API (not re-exported from `./index`) — reached by\n * relative import from tests/consumers that specifically need to await\n * teardown, mirroring how `editorInfoContext` itself stays internal. */\nconst pendingDestroys = new Set<Promise<unknown>>();\n\n/** True while at least one Milkdown `destroy()` is still in flight. */\nexport function hasPendingMilkdownTeardown(): boolean {\n return pendingDestroys.size > 0;\n}\n\n/** Await every Milkdown `destroy()` currently in flight. Resolves immediately\n * when none are pending. Never rejects — `destroy()` already routes its own\n * failure through `console.error` (see the cleanup below), so a caller awaiting\n * teardown only needs \"settled\", not \"succeeded\". */\nexport function waitForPendingMilkdownTeardown(): Promise<void> {\n return Promise.all(pendingDestroys).then(() => undefined);\n}\n\nexport function useGetEditor() {\n const {\n dom,\n editor: editorRef,\n setLoading,\n editorFactory: getEditor,\n } = useContext(editorInfoContext);\n const domRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const div = domRef.current;\n\n if (!getEditor) return;\n if (!div) return;\n\n dom.current = div;\n\n const editor = getEditor(div);\n if (!editor) return;\n\n setLoading(true);\n editor\n .create()\n .then((editor) => {\n editorRef.current = editor;\n })\n .finally(() => {\n setLoading(false);\n })\n .catch(console.error);\n\n return () => {\n // `.catch(console.error)` means this promise itself never rejects, so\n // it's safe to await via `Promise.all` in `waitForPendingMilkdownTeardown`\n // without a stray unhandled rejection.\n const destroyPromise = editor.destroy().catch(console.error);\n pendingDestroys.add(destroyPromise);\n void destroyPromise.finally(() => {\n pendingDestroys.delete(destroyPromise);\n });\n };\n }, [dom, editorRef, getEditor, setLoading]);\n\n return domRef;\n}\n","/**\n * Vendored + adapted from `@milkdown/react` (MIT — © 2020-present Mirone).\n *\n * `useEditor(getEditor, deps)` registers the editor factory with the provider.\n * `getEditor` is memoized on `deps`, so the mount effect runs exactly once per\n * dependency change (re-creating the editor when `deps` change).\n */\nimport { type DependencyList, useCallback, useContext, useLayoutEffect } from \"react\";\n\nimport type { GetEditor, UseEditorReturn } from \"./types\";\nimport { editorInfoContext } from \"./use-get-editor\";\n\nexport function useEditor(getEditor: GetEditor, deps: DependencyList = []): UseEditorReturn {\n const editorInfo = useContext(editorInfoContext);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const factory = useCallback(getEditor, deps);\n\n useLayoutEffect(() => {\n editorInfo.setEditorFactory(() => factory);\n }, [editorInfo, factory]);\n\n return {\n loading: editorInfo.loading,\n get: () => editorInfo.editor.current,\n };\n}\n","/**\n * Vendored + adapted from `@milkdown/react` (MIT — © 2020-present Mirone).\n *\n * `useInstance()` reads the live editor instance from context. Returns\n * `[loading, getInstance]`; only call `getInstance()` once `loading` is false.\n */\nimport type { Editor } from \"@milkdown/kit/core\";\n\nimport { useCallback, useContext } from \"react\";\n\nimport { editorInfoContext } from \"./use-get-editor\";\n\nexport type Instance = [true, () => undefined] | [false, () => Editor];\n\nexport function useInstance() {\n const editorInfo = useContext(editorInfoContext);\n\n const getInstance = useCallback(() => {\n return editorInfo.editor.current;\n }, [editorInfo.editor]);\n\n return [editorInfo.loading, getInstance] as Instance;\n}\n","\"use client\";\n\n/**\n * Exit-block keymap — let the caret escape a fenced code block (incl. the\n * ```calc fence) to the block that follows it.\n *\n * Inside a `code_block` there is otherwise no keyboard way out: Tab is swallowed\n * (indent) or blurs the editor, and a code block that is the document's LAST\n * child traps the caret entirely — there is no following line to arrow into.\n * This binds Tab and Mod-Enter (collapsed selection, inside a code block) to move\n * the caret to the block AFTER the code block, inserting a trailing empty\n * paragraph first when the code block is the document's last child.\n *\n * Why `props.handleKeyDown` and not a `keymap` plugin: `handleKeyDown` props run\n * BEFORE keymap plugins, so this reliably beats any commonmark Tab→indent\n * binding. It only acts inside a code block, so Tab in a GFM table (`tableKeymap`\n * → next cell) and Tab elsewhere are untouched, and the slash menu (which never\n * opens inside a code block) is unaffected.\n */\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport { $prose } from \"@milkdown/kit/utils\";\nimport type { Command, Transaction } from \"@milkdown/kit/prose/state\";\nimport { Plugin, TextSelection } from \"@milkdown/kit/prose/state\";\n\n/**\n * ProseMirror command: if the collapsed selection sits inside a `code_block`,\n * move the caret to the block after it — creating a trailing empty paragraph when\n * the code block is the document's last child. Returns `false` otherwise so other\n * handlers still run. Exported standalone so it can be unit-tested against the\n * real editor schema without a live keyboard event.\n */\nexport const exitCodeBlock: Command = (state, dispatch) => {\n const { selection } = state;\n if (!selection.empty) return false;\n const { $head } = selection;\n // `spec.code` is set on code-block node types (commonmark's `code_block`,\n // including the ```calc fence) — the same check the slash plugin uses.\n if (!$head.parent.type.spec.code) return false;\n\n const paragraph = state.schema.nodes.paragraph;\n if (!paragraph) return false;\n\n // Position directly after the code-block node (its sibling boundary).\n const after = $head.after($head.depth);\n\n if (dispatch) {\n const nodeAfter = state.doc.resolve(after).nodeAfter;\n let tr: Transaction = state.tr;\n if (nodeAfter && nodeAfter.isTextblock) {\n // A textblock already follows — step the caret into its content start.\n tr = tr.setSelection(TextSelection.create(tr.doc, after + 1));\n } else {\n // Nothing usable follows (the code block is the last child, or a leaf\n // follows) — insert a fresh empty paragraph and land the caret inside it.\n const para = paragraph.createAndFill();\n if (!para) return false;\n tr = tr.insert(after, para);\n tr = tr.setSelection(TextSelection.create(tr.doc, after + 1));\n }\n dispatch(tr.scrollIntoView());\n }\n return true;\n};\n\n/**\n * The Milkdown plugin array to `.use()` on the editor chain. Binds Tab and\n * Mod-Enter to {@link exitCodeBlock} via `handleKeyDown`.\n */\nexport function exitKeymapPlugins(): MilkdownPlugin[] {\n return [\n $prose(\n () =>\n new Plugin({\n props: {\n handleKeyDown: (view, event) => {\n const isTab = event.key === \"Tab\" && !event.shiftKey;\n const isModEnter = event.key === \"Enter\" && (event.metaKey || event.ctrlKey);\n if (!isTab && !isModEnter) return false;\n const handled = exitCodeBlock(view.state, view.dispatch);\n if (handled) event.preventDefault();\n return handled;\n },\n },\n }),\n ) as unknown as MilkdownPlugin,\n ];\n}\n","/**\n * paste-embed — ProseMirror plugin + helper for image paste/drop embedding.\n *\n * When `onEmbedAsset` is provided by the host:\n * 1. Intercepts paste (from clipboard) or drop events carrying image files.\n * 2. Inserts an inline \"uploading…\" widget Decoration at the caret/drop position.\n * 3. Calls `onEmbedAsset(file)` and on resolve inserts a real image node, removing\n * the placeholder.\n * 4. On reject removes the placeholder and renders a transient inline error\n * decoration (role=\"alert\", auto-dismissed after 4 s) + toast.error().\n *\n * The plugin is a raw `$prose` ProseMirror plugin (same pattern as the slash menu),\n * so it pulls zero new dependencies beyond what Milkdown already provides.\n *\n * A11y:\n * - Upload placeholder: role=\"status\" aria-live=\"polite\"\n * - Error decoration: role=\"alert\" (assertive; fires once and auto-dismisses)\n *\n * Motion: gated via `duration-normal ease-standard motion-reduce:transition-none`\n * token utilities (defined in @elabs-ai/components-tokens).\n */\n\nimport type { MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport { $prose } from \"@milkdown/kit/utils\";\nimport { Plugin, PluginKey, type Transaction } from \"@milkdown/kit/prose/state\";\nimport { Decoration, DecorationSet } from \"@milkdown/kit/prose/view\";\nimport type { EditorView } from \"@milkdown/kit/prose/view\";\nimport { toast } from \"@elabs-ai/components-ui\";\n\n/** The host-provided hook: receives a File, resolves to a URL/path string. */\nexport type EmbedAssetFn = (file: File) => Promise<string>;\n\n/** A unique key for each in-flight upload. */\ntype UploadId = string;\n\nfunction uniqueId(): UploadId {\n return `embed-${Math.random().toString(36).slice(2)}`;\n}\n\n/** Meta tags for the plugin's own transactions. */\ninterface EmbedMetaAdd {\n type: \"add\";\n id: UploadId;\n pos: number;\n filename: string;\n}\ninterface EmbedMetaRemove {\n type: \"remove\";\n id: UploadId;\n}\ninterface EmbedMetaError {\n type: \"error\";\n id: UploadId;\n /** Position at which the error chip should appear. */\n pos: number;\n message: string;\n}\ninterface EmbedMetaClearError {\n type: \"clear-error\";\n id: UploadId;\n}\ntype EmbedMeta = EmbedMetaAdd | EmbedMetaRemove | EmbedMetaError | EmbedMetaClearError;\n\n/** Runtime state: active upload placeholders + transient error chips. */\ninterface EmbedState {\n /** Decoration set that maps through doc changes. */\n decos: DecorationSet;\n /** Map from upload id → decoration for fast lookup / removal. */\n byId: Map<UploadId, Decoration>;\n /** Set of error ids currently showing. */\n errorIds: Set<UploadId>;\n}\n\nconst embedPluginKey = new PluginKey<EmbedState>(\"brand-embed\");\n\n/** DOM element for the uploading placeholder chip. */\nfunction makePlaceholderChip(filename: string): HTMLElement {\n const chip = document.createElement(\"span\");\n chip.setAttribute(\"role\", \"status\");\n chip.setAttribute(\"aria-live\", \"polite\");\n chip.setAttribute(\"aria-label\", `Uploading ${filename}`);\n chip.setAttribute(\"title\", `Uploading ${filename}…`);\n chip.className =\n \"inline-flex items-center gap-1 rounded px-2 py-0.5 text-caption bg-muted text-muted-foreground \" +\n \"border border-border select-none transition-opacity duration-normal ease-standard motion-reduce:transition-none\";\n\n // SVG spinner (token-colored, no Spinner component needed here — widget is a raw DOM node)\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n svg.style.cssText = \"width:0.85em;height:0.85em;animation:spin 1s linear infinite;flex-shrink:0;\";\n const circle = document.createElementNS(\"http://www.w3.org/2000/svg\", \"circle\");\n circle.setAttribute(\"cx\", \"12\");\n circle.setAttribute(\"cy\", \"12\");\n circle.setAttribute(\"r\", \"9\");\n circle.setAttribute(\"stroke\", \"currentColor\");\n circle.setAttribute(\"stroke-width\", \"2.5\");\n circle.setAttribute(\"stroke-dasharray\", \"56.5\");\n circle.setAttribute(\"stroke-dashoffset\", \"42\");\n circle.setAttribute(\"stroke-linecap\", \"round\");\n svg.appendChild(circle);\n chip.appendChild(svg);\n\n // Ensure the keyframe is injected once per document.\n if (!chip.ownerDocument.head.querySelector(\"#embed-spin-keyframe\")) {\n const style = chip.ownerDocument.createElement(\"style\");\n style.id = \"embed-spin-keyframe\";\n style.textContent = \"@keyframes spin{to{transform:rotate(360deg)}}\";\n chip.ownerDocument.head.appendChild(style);\n }\n\n const label = document.createElement(\"span\");\n label.textContent = `${filename.length > 24 ? filename.slice(0, 22) + \"…\" : filename} Uploading…`;\n chip.appendChild(label);\n\n return chip;\n}\n\n/** DOM element for the error chip (transient, auto-dismissing). */\nfunction makeErrorChip(message: string): HTMLElement {\n const chip = document.createElement(\"span\");\n chip.setAttribute(\"role\", \"alert\");\n chip.setAttribute(\"aria-live\", \"assertive\");\n chip.setAttribute(\"title\", message);\n // #124: the chip's own \"Upload failed\" label is running text — ink rung.\n chip.className =\n \"inline-flex items-center gap-1 rounded px-2 py-0.5 text-caption bg-destructive/10 text-destructive-text \" +\n \"border border-destructive/30 select-none\";\n\n const label = document.createElement(\"span\");\n label.textContent = `⚠ Upload failed`;\n chip.appendChild(label);\n return chip;\n}\n\n/**\n * Core embed helper — exported so tests can call it directly against a live\n * ProseMirror view without synthesizing ClipboardEvent/DragEvent.\n */\nexport function embedAsset(\n view: EditorView,\n file: File,\n pos: number,\n onEmbedAsset: EmbedAssetFn,\n onError?: (msg: string) => void,\n): void {\n const id = uniqueId();\n\n // 1. Insert the uploading placeholder widget.\n const addMeta: EmbedMeta = { type: \"add\", id, pos, filename: file.name };\n view.dispatch(view.state.tr.setMeta(embedPluginKey, addMeta));\n\n // 2. Call the host hook.\n onEmbedAsset(file).then(\n (path) => {\n // 3a. Resolve: find the placeholder's current mapped position.\n const pluginState = embedPluginKey.getState(view.state);\n const deco = pluginState?.byId.get(id);\n\n // Remove the placeholder decoration.\n const removeMeta: EmbedMeta = { type: \"remove\", id };\n const tr: Transaction = view.state.tr.setMeta(embedPluginKey, removeMeta);\n\n if (deco) {\n // Insert the image node at the placeholder's current mapped position.\n const decoPos = (deco as unknown as { from: number }).from;\n const imageNode = view.state.schema.nodes.image?.create({\n src: path,\n alt: file.name.replace(/\\.[^.]+$/, \"\"),\n title: null,\n });\n if (imageNode) {\n // We need to insert the image: insert then dispatch remove separately.\n // Insert first so the position is relative to the current doc.\n const insertTr = view.state.tr.insert(decoPos, imageNode);\n view.dispatch(insertTr);\n // Now remove the deco (after the doc has shifted).\n const removeTr = view.state.tr.setMeta(embedPluginKey, removeMeta);\n view.dispatch(removeTr);\n return;\n }\n }\n // Fallback: just remove the placeholder.\n view.dispatch(tr);\n },\n (err) => {\n // 3b. Reject: find the placeholder position, show error.\n const pluginState = embedPluginKey.getState(view.state);\n const deco = pluginState?.byId.get(id);\n const errPos = deco ? (deco as unknown as { from: number }).from : pos;\n const message = err instanceof Error ? err.message : \"Upload failed\";\n\n // Remove placeholder + add error chip.\n const errorMeta: EmbedMeta = { type: \"error\", id, pos: errPos, message };\n view.dispatch(view.state.tr.setMeta(embedPluginKey, errorMeta));\n\n // Surface the error via toast (additional signal; not the only cue).\n toast.error(`Upload failed: ${message}`);\n onError?.(message);\n\n // Auto-dismiss the error chip after 4 s.\n setTimeout(() => {\n if (view.isDestroyed) return;\n const clearMeta: EmbedMeta = { type: \"clear-error\", id };\n view.dispatch(view.state.tr.setMeta(embedPluginKey, clearMeta));\n }, 4000);\n },\n );\n}\n\n/** Read image Files from a FileList (or null). */\nfunction imageFiles(list: FileList | null | undefined): File[] {\n if (!list) return [];\n const files: File[] = [];\n for (let i = 0; i < list.length; i++) {\n const f = list[i];\n if (f && f.type.startsWith(\"image/\")) files.push(f);\n }\n return files;\n}\n\n/**\n * Build the paste-embed Milkdown plugin. Pass the `onEmbedAsset` hook from the\n * host. When `onEmbedAsset` is undefined the plugin is still registered but never\n * intercepts events — normal ProseMirror behavior is preserved.\n */\nexport function pasteEmbedPlugin(onEmbedAsset?: EmbedAssetFn): MilkdownPlugin {\n return $prose(() => {\n return new Plugin<EmbedState>({\n key: embedPluginKey,\n state: {\n init: () => ({\n decos: DecorationSet.empty,\n byId: new Map(),\n errorIds: new Set(),\n }),\n apply: (tr, prev, _oldState, newState) => {\n // Map existing decorations through the transaction.\n let decos = prev.decos.map(tr.mapping, tr.doc);\n const byId = new Map(prev.byId);\n const errorIds = new Set(prev.errorIds);\n\n // Also map the positions stored on each deco (they're widget decorations\n // whose .from tracks through the mapping automatically via DecorationSet.map).\n // We just need to keep byId in sync: after mapping, find each id's deco.\n for (const [id, oldDeco] of prev.byId.entries()) {\n // Find the mapped deco in the new set (same key).\n const key = (oldDeco.spec as { key?: string }).key;\n if (key) {\n // Re-fetch from the mapped set so .from is up to date.\n const found = decos.find(undefined, undefined, (spec) => spec.key === key);\n if (found.length > 0 && found[0]) {\n byId.set(id, found[0]);\n } else {\n byId.delete(id);\n }\n }\n }\n\n const meta = tr.getMeta(embedPluginKey) as EmbedMeta | undefined;\n if (!meta) return { decos, byId, errorIds };\n\n switch (meta.type) {\n case \"add\": {\n const chip = makePlaceholderChip(meta.filename);\n const deco = Decoration.widget(meta.pos, chip, {\n key: `embed-placeholder:${meta.id}`,\n side: -1,\n });\n decos = decos.add(newState.doc, [deco]);\n byId.set(meta.id, deco);\n break;\n }\n case \"remove\": {\n const key = `embed-placeholder:${meta.id}`;\n const toRemove = decos.find(undefined, undefined, (spec) => spec.key === key);\n if (toRemove.length > 0) {\n decos = decos.remove(toRemove);\n }\n byId.delete(meta.id);\n errorIds.delete(meta.id);\n break;\n }\n case \"error\": {\n // Remove the placeholder chip.\n const placeholderKey = `embed-placeholder:${meta.id}`;\n const placeholders = decos.find(\n undefined,\n undefined,\n (spec) => spec.key === placeholderKey,\n );\n if (placeholders.length > 0) {\n decos = decos.remove(placeholders);\n }\n byId.delete(meta.id);\n\n // Add the error chip.\n const chip = makeErrorChip(meta.message);\n const errorKey = `embed-error:${meta.id}`;\n const deco = Decoration.widget(meta.pos, chip, { key: errorKey, side: 1 });\n decos = decos.add(newState.doc, [deco]);\n errorIds.add(meta.id);\n break;\n }\n case \"clear-error\": {\n const errorKey = `embed-error:${meta.id}`;\n const toRemove = decos.find(undefined, undefined, (spec) => spec.key === errorKey);\n if (toRemove.length > 0) {\n decos = decos.remove(toRemove);\n }\n errorIds.delete(meta.id);\n break;\n }\n }\n\n return { decos, byId, errorIds };\n },\n },\n props: {\n decorations: (state) => embedPluginKey.getState(state)?.decos ?? DecorationSet.empty,\n\n handlePaste: (view, event) => {\n if (!onEmbedAsset) return false;\n const files = imageFiles(event.clipboardData?.files);\n if (files.length === 0) return false;\n\n event.preventDefault();\n const pos = view.state.selection.from;\n for (const file of files) {\n embedAsset(view, file, pos, onEmbedAsset);\n }\n return true;\n },\n\n handleDrop: (view, event) => {\n if (!onEmbedAsset) return false;\n const files = imageFiles((event as DragEvent).dataTransfer?.files);\n if (files.length === 0) return false;\n\n event.preventDefault();\n // Compute drop position from coordinates.\n const coords = view.posAtCoords({\n left: (event as DragEvent).clientX,\n top: (event as DragEvent).clientY,\n });\n const pos = coords?.pos ?? view.state.selection.from;\n for (const file of files) {\n embedAsset(view, file, pos, onEmbedAsset);\n }\n return true;\n },\n },\n });\n }) as unknown as MilkdownPlugin;\n}\n","\"use client\";\n\n/**\n * Table control overlay for the Milkdown WYSIWYG editor.\n *\n * Mechanism: a **contextual toolbar** rendered as a ProseMirror plugin view\n * (via `@prosemirror-adapter/react`'s `usePluginViewFactory`). When the\n * selection is inside a GFM table, a token-styled button bar appears at the\n * bottom of the editor's wrapper offering add/remove row and column ops.\n *\n * Design choice — why NOT a `$view` node-view:\n * A content-replacing `$view` for the GFM table conflicts with gfm's\n * `columnResizingPlugin` and `tableEditingPlugin` decorations (both rewrite\n * the DOM directly), breaking cell selection highlighting and column handles.\n * The contextual toolbar avoids all decoration conflicts while delivering the\n * full feature surface (add/remove row+col, Tab nav, lossless round-trip,\n * token look, keyboard a11y). GFM's `tableKeymap` already binds Tab →\n * NextCell and Shift+Tab → PrevCell so no extra keymap plugin is needed.\n *\n * Focus safety:\n * Plugin views mount OUTSIDE `contentEditable` (ProseMirror appends them to\n * the editor wrapper). Buttons use `onMouseDown={preventDefault}` so clicks\n * never steal focus from the editing surface.\n *\n * Command wiring:\n * All ops call the existing gfm commands (addRowAfterCommand, etc.) via\n * `commandsCtx.call()`. The GFM schema + serializer are never replaced, so\n * GFM round-trip fidelity is structurally guaranteed.\n */\n\nimport type { CmdKey } from \"@milkdown/kit/core\";\nimport type { Ctx, MilkdownPlugin } from \"@milkdown/kit/ctx\";\nimport { commandsCtx } from \"@milkdown/kit/core\";\nimport {\n addColAfterCommand,\n addColBeforeCommand,\n addRowAfterCommand,\n addRowBeforeCommand,\n deleteSelectedCellsCommand,\n} from \"@milkdown/kit/preset/gfm\";\nimport { Plugin, PluginKey } from \"@milkdown/kit/prose/state\";\nimport type { EditorView } from \"@milkdown/kit/prose/view\";\nimport { $prose } from \"@milkdown/kit/utils\";\nimport { isInTable } from \"@milkdown/kit/prose/tables\";\nimport { useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { usePluginViewContext } from \"@prosemirror-adapter/react\";\nimport type { usePluginViewFactory } from \"@prosemirror-adapter/react\";\n\n// ---------------------------------------------------------------------------\n// Ctx bridge\n// ---------------------------------------------------------------------------\n\n/**\n * Per-instance Ctx registry. The `$prose` plugin's `view(editorView)` records\n * THIS editor's live Milkdown `Ctx` keyed by its ProseMirror view; the React\n * toolbar reads it back via the view it gets from `usePluginViewContext`. Keyed\n * by the view (NOT a module singleton) so multiple `MarkdownEditor`s on one page\n * — a Storybook autodocs page, a split workspace — each dispatch to the correct\n * instance. Entries are released with their view (WeakMap GC).\n */\nconst ctxByView = new WeakMap<EditorView, Ctx>();\n\n/** Call a gfm command key on the editor that owns `view`. */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction dispatchCommand(view: EditorView, key: CmdKey<any>): void {\n const ctx = ctxByView.get(view);\n if (!ctx) return;\n try {\n ctx.get(commandsCtx).call(key);\n } catch {\n // Editor may be mid-destroy; swallow gracefully.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Plugin key\n// ---------------------------------------------------------------------------\n\nconst tableControlsKey = new PluginKey<boolean>(\"brand-table-controls\");\n\n// ---------------------------------------------------------------------------\n// Toolbar React component\n// ---------------------------------------------------------------------------\n\ninterface ToolbarButtonProps {\n onClick: () => void;\n ariaLabel: string;\n title: string;\n children: React.ReactNode;\n variant?: \"default\" | \"destructive\";\n}\n\nfunction ToolbarButton({\n onClick,\n ariaLabel,\n title,\n children,\n variant = \"default\",\n}: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel}\n title={title}\n // Keep focus in the editor — a mousedown without preventDefault would\n // blur the ProseMirror view before the click fires.\n onMouseDown={(e) => e.preventDefault()}\n onClick={onClick}\n className={cn(\n \"inline-flex h-7 items-center gap-1 rounded px-2 text-caption font-medium\",\n \"border border-border-strong\",\n \"focus-ring\",\n \"transition-colors duration-fast ease-standard motion-reduce:transition-none\",\n // #124: the button's LABEL text, not a mark — ink rung.\n variant === \"destructive\"\n ? \"bg-background text-destructive-text hover:bg-destructive/10\"\n : \"bg-background text-foreground hover:bg-surface-muted\",\n )}\n >\n {children}\n </button>\n );\n}\n\n/** Thin visual separator between button groups. */\nfunction ToolbarDivider() {\n return <span aria-hidden=\"true\" className=\"mx-0.5 h-4 w-px bg-border-strong\" />;\n}\n\n/**\n * The table controls toolbar.\n *\n * Rendered as a ProseMirror plugin view: ProseMirror mounts it into the\n * editor's wrapper DOM (`view.dom.parentElement`) and calls `update()` on\n * every state change — `usePluginViewContext` re-renders the React tree.\n * Hidden when the cursor is not inside a table.\n */\nfunction TableControlsView() {\n const { view } = usePluginViewContext();\n const { t } = useLocale();\n const inTable = isInTable(view.state);\n const run = (key: Parameters<typeof dispatchCommand>[1]) => () => dispatchCommand(view, key);\n\n if (!inTable) return null;\n\n const addRowAbove = t(\"editor.tableView.addRowAbove\");\n const addRowBelow = t(\"editor.tableView.addRowBelow\");\n const deleteRow = t(\"editor.tableView.deleteRow\");\n const addColLeft = t(\"editor.tableView.addColumnLeft\");\n const addColRight = t(\"editor.tableView.addColumnRight\");\n const deleteCol = t(\"editor.tableView.deleteColumn\");\n\n return (\n <div\n role=\"toolbar\"\n aria-label={t(\"editor.tableView.tableControls\")}\n className={cn(\n \"flex flex-wrap items-center gap-1 px-2 py-1.5\",\n \"border-t border-border-strong bg-surface-muted\",\n )}\n >\n {/* Row group */}\n <span className=\"mr-1 select-none text-caption text-muted-foreground\">\n {t(\"editor.tableView.row\")}\n </span>\n <ToolbarButton\n ariaLabel={addRowAbove}\n title={addRowAbove}\n onClick={run(addRowBeforeCommand.key)}\n >\n ↑+\n </ToolbarButton>\n <ToolbarButton\n ariaLabel={addRowBelow}\n title={addRowBelow}\n onClick={run(addRowAfterCommand.key)}\n >\n ↓+\n </ToolbarButton>\n <ToolbarButton\n ariaLabel={deleteRow}\n title={deleteRow}\n variant=\"destructive\"\n onClick={run(deleteSelectedCellsCommand.key)}\n >\n ×row\n </ToolbarButton>\n\n <ToolbarDivider />\n\n {/* Column group */}\n <span className=\"mr-1 select-none text-caption text-muted-foreground\">\n {t(\"editor.tableView.col\")}\n </span>\n <ToolbarButton\n ariaLabel={addColLeft}\n title={addColLeft}\n onClick={run(addColBeforeCommand.key)}\n >\n ←+\n </ToolbarButton>\n <ToolbarButton\n ariaLabel={addColRight}\n title={addColRight}\n onClick={run(addColAfterCommand.key)}\n >\n →+\n </ToolbarButton>\n <ToolbarButton\n ariaLabel={deleteCol}\n title={deleteCol}\n variant=\"destructive\"\n onClick={run(deleteSelectedCellsCommand.key)}\n >\n ×col\n </ToolbarButton>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Exported factory\n// ---------------------------------------------------------------------------\n\n/**\n * Build the Milkdown plugin array for table controls.\n *\n * Call with `usePluginViewFactory()` from inside `<ProsemirrorAdapterProvider>`,\n * then `.use(tableViewPlugins(pluginViewFactory))` on the editor chain.\n *\n * The signature matches the `directiveViewPlugins` convention: factory\n * captured from the React adapter hook, plugin array returned for `.use()`.\n */\nexport function tableViewPlugins(\n pluginViewFactory: ReturnType<typeof usePluginViewFactory>,\n): MilkdownPlugin[] {\n return [\n $prose((ctx) => {\n // Build the ProseMirror PluginViewSpec from the adapter factory. This\n // returns a `view(editorView) => PluginView` that ProseMirror calls to\n // mount/update/destroy the React tree.\n const makePluginView = pluginViewFactory({ component: TableControlsView });\n\n return new Plugin({\n key: tableControlsKey,\n state: {\n // Track whether the cursor is in a table (bool) as plugin state so\n // ProseMirror knows when to trigger an `update()` on the plugin view.\n init: (_cfg, state) => isInTable(state),\n apply: (_tr, _prev, _old, state) => isInTable(state),\n },\n // Bind THIS editor's Ctx to THIS editor's view, so the toolbar's\n // commands dispatch to the right instance when several editors share\n // this module (autodocs page, split workspace).\n view: (editorView) => {\n ctxByView.set(editorView, ctx);\n return makePluginView(editorView);\n },\n });\n }) as unknown as MilkdownPlugin,\n ];\n}\n","\"use client\";\n\nimport { Button, useCopyToClipboard, useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { CheckIcon, CopyIcon } from \"lucide-react\";\nimport { useCallback, type ComponentProps } from \"react\";\n\nexport interface CopyButtonProps extends Omit<ComponentProps<typeof Button>, \"value\"> {\n /** Text written to the clipboard on click. */\n value: string;\n /** Show the \"Copy\" / \"Copied\" label next to the icon. Defaults to true. */\n label?: boolean;\n}\n\n/**\n * Brand-ui copy-to-clipboard button with a transient \"Copied\" state. Shared by\n * the editor toolbar and workspace so the copy affordance is defined once.\n */\nexport function CopyButton({ value, label = true, className, ...props }: CopyButtonProps) {\n // Clipboard write + transient flag come from the shared `@elabs-ai/components-ui`\n // hook, so this button and `CopyableValue` cannot drift on timing or on what\n // happens where there is no clipboard.\n const { copied, copy } = useCopyToClipboard();\n const { t } = useLocale();\n\n const onClick = useCallback(() => {\n void copy(value);\n }, [copy, value]);\n\n const text = copied ? t(\"editor.copyButton.copied\") : t(\"copy\");\n\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n {...props}\n type=\"button\"\n className={cn(\"h-7 gap-1.5\", className)}\n onClick={onClick}\n aria-label={text}\n >\n {copied ? (\n <CheckIcon\n key={String(copied)}\n className=\"size-4 text-success animate-in fade-in zoom-in-95 duration-fast ease-entrance\"\n aria-hidden=\"true\"\n />\n ) : (\n <CopyIcon className=\"size-4\" aria-hidden=\"true\" />\n )}\n {label ? <span className=\"text-xs\">{text}</span> : null}\n </Button>\n );\n}\n","/**\n * Engine-agnostic editor content-access interface + Monaco adapter.\n *\n * ISOLATION INVARIANT: this file imports ONLY `monaco-editor` and the\n * `MonacoCodeEditor` type from `../code-editor`. It MUST NOT import\n * `editor-content-access-prose.ts` or anything from `@milkdown`, or the\n * `@milkdown` graph leaks into the `.` barrel (#271-inverse).\n */\nimport type { MonacoCodeEditor } from \"../code-editor\";\n\n/** A snapshot of the editor's current selection. Text, not engine positions —\n * that is what an AI assistant needs and it keeps the interface engine-agnostic. */\nexport interface EditorSelection {\n /**\n * The selected text. Plain text for Monaco. For the Milkdown WYSIWYG it is the\n * selection SERIALIZED TO MARKDOWN by default (so `**bold**`, links, lists round-trip);\n * pass `{ fidelity: \"plainText\" }` to the adapter to get `doc.textBetween` instead.\n */\n text: string;\n /** `true` when the selection is collapsed (a bare caret, nothing highlighted). */\n empty: boolean;\n}\n\n/**\n * Engine-agnostic, text-oriented access to an editor's content + selection — the\n * uniform surface an external AI assistant drives across the Monaco code editors\n * AND the Milkdown WYSIWYG markdown editor.\n *\n * D5: this manipulates EDITOR CONTENT only. It performs no model calls, no transport,\n * no fetch. The app owns the AI call and APPLIES the result via these methods.\n *\n * `replaceSelection` and `insertAtCursor` are equivalent primitives (both replace the\n * active range; an empty range is the caret) — two names for two reader intents.\n */\nexport interface EditorContentAccess {\n /** The full document text (Monaco: source; Milkdown: serialized markdown). */\n getText(): string;\n /** A snapshot of the current selection (selected text + whether collapsed). */\n getSelection(): EditorSelection;\n /** Replace the current selection; a collapsed selection inserts at the caret. */\n replaceSelection(text: string): void;\n /**\n * Insert at the caret; a non-empty selection is replaced (platform \"typing\" semantics).\n * Equivalent to `replaceSelection` — two names for two reader intents (agent-legibility).\n */\n insertAtCursor(text: string): void;\n /** Move focus to the editing surface (so the user can keep typing after an apply). */\n focus(): void;\n /**\n * Subscribe to selection changes. Fires with a fresh `EditorSelection` whenever the\n * selection or caret moves. Returns an unsubscribe function — the caller MUST call it\n * (e.g. in a React effect cleanup) to dispose the underlying engine listener.\n */\n onSelectionChange(listener: (selection: EditorSelection) => void): () => void;\n}\n\n/**\n * Wrap a live Monaco `IStandaloneCodeEditor` as the engine-agnostic\n * {@link EditorContentAccess}. The `CodeEditor` / `DiffEditor` / `CodeWorkspace`\n * `ref` already exposes the raw Monaco instance — pass it here to get the\n * uniform shape used by the markdown surfaces.\n *\n * For a `DiffEditor`, pass the modified (editable) side:\n * `monacoContentAccess(diffRef.current!.getModifiedEditor())`.\n *\n * @example\n * const editorRef = useRef<MonacoCodeEditor>(null);\n * // ...later\n * const access = monacoContentAccess(editorRef.current!);\n * access.insertAtCursor(aiText);\n */\nexport function monacoContentAccess(editor: MonacoCodeEditor): EditorContentAccess {\n const readSelection = (): EditorSelection => {\n const model = editor.getModel();\n const selection = editor.getSelection();\n if (!model || !selection) return { text: \"\", empty: true };\n const text = model.getValueInRange(selection);\n return { text, empty: selection.isEmpty() };\n };\n\n /**\n * Replace the active selection range. An empty selection (caret) inserts at\n * the caret — one path covers both insert & replace (executeEdits over an empty\n * range == insert at that position, exactly the markdown-commands.ts pattern).\n */\n const applyAtSelection = (text: string): void => {\n const selection = editor.getSelection();\n if (!selection) return;\n editor.executeEdits(\"editor-content-access\", [\n { range: selection, text, forceMoveMarkers: true },\n ]);\n // An explicit undo stop makes this AI apply independently undoable. The\n // toolbar helpers omit it because they immediately re-select (their own stop);\n // for the AI path an explicit stop is the right call.\n editor.pushUndoStop();\n };\n\n return {\n getText: () => editor.getValue(),\n getSelection: readSelection,\n replaceSelection: applyAtSelection,\n insertAtCursor: applyAtSelection,\n focus: () => editor.focus(),\n onSelectionChange: (listener) => {\n const sub = editor.onDidChangeCursorSelection(() => listener(readSelection()));\n return () => sub.dispose();\n },\n };\n}\n"],"mappings":"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","names":["ctx","useEffect","useState","jsx","CodeEditor","useState","useEffect","ctx","clamp","ctx","Plugin","PluginKey","$prose","plainText","listener","cn","forwardRef","jsx","jsxs","forwardRef","DocumentOutline","cn","cn","forwardRef","jsx","jsxs","IterationBlock","attrString","cellScope","ctx","Repeat2","ctx","Repeat2","useLocale","cn","forwardRef","jsx","jsxs","forwardRef","SlashMenu","useLocale","cn","$prose","Plugin","PluginKey","DecorationSet","PluginKey","query","index","$prose","ctx","Plugin","DecorationSet","useRef","jsx","useRef","editorViewCtx","parserCtx","serializerCtx","TextSelection","cn","forwardRef","useContext","useEffect","useImperativeHandle","useRef","Plugin","PluginKey","DecorationSet","$prose","CLOSED","PluginKey","$prose","Plugin","DecorationSet","ctx","useWidgetViewContext","useLayoutEffect","useRef","useLocale","cn","forwardRef","jsx","jsxs","CompletionMenu","jsx","ID_PREFIX","useWidgetViewContext","useRef","useLayoutEffect","ContextMenu","ContextMenuContent","ContextMenuItem","ContextMenuTrigger","useLocale","cn","editorViewCtx","parserCtx","Grid3x3","Repeat2","useContext","useEffect","useRef","useMemo","useRef","useState","createContext","useEffect","useRef","useRef","useEffect","editor","jsx","useRef","useState","useMemo","useContext","useLayoutEffect","useContext","useLayoutEffect","useCallback","useContext","useContext","useCallback","Fragment","jsx","jsxs","useRef","useEffect","cn","useLocale","ctx","parserCtx","editorViewCtx","ContextMenuItem","useContext","Grid3x3","Repeat2","ContextMenu","ContextMenuTrigger","ContextMenuContent","$prose","Plugin","TextSelection","$prose","Plugin","PluginKey","Decoration","DecorationSet","Plugin","PluginKey","$prose","useLocale","cn","jsx","jsxs","ctx","jsx","ctx","editorViewCtx","TextSelection","forwardRef","MarkdownEditorView","useRef","useContext","useEffect","useImperativeHandle","serializerCtx","parserCtx","MarkdownEditor","cn","useLocale","cn","useCallback","jsx","jsxs","listener"]}
|