@elabs-ai/components-editor 4.0.0 → 4.1.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.
Files changed (54) hide show
  1. package/README.md +6 -7
  2. package/dist/{chunk-LBC5VJBD.js → chunk-C62O7IOQ.js} +110 -21
  3. package/dist/chunk-C62O7IOQ.js.map +1 -0
  4. package/dist/index.css +4 -2
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js +57 -20
  8. package/dist/index.js.map +1 -1
  9. package/dist/markdown/index.css +4 -2
  10. package/dist/markdown/index.css.map +1 -1
  11. package/dist/markdown/index.d.ts +2 -2
  12. package/dist/markdown/index.js +9 -9
  13. package/dist/markdown/index.js.map +1 -1
  14. package/dist/{markdown-editor-DfBZibAn.d.ts → markdown-editor-CBp_4eDv.d.ts} +1 -1
  15. package/package.json +6 -6
  16. package/src/ai-objects/entity.tsx +1 -1
  17. package/src/ai-objects/knowledge-card.tsx +1 -1
  18. package/src/calc-block/calc-editor.css +1 -1
  19. package/src/code-editor/code-editor.stories.tsx +76 -1
  20. package/src/code-editor/code-editor.tsx +1 -1
  21. package/src/code-workspace/code-workspace.test.tsx +83 -0
  22. package/src/code-workspace/code-workspace.tsx +73 -20
  23. package/src/diff-editor/diff-editor.stories.tsx +9 -1
  24. package/src/lib/monaco-deep-imports.d.ts +59 -0
  25. package/src/lib/monaco-theme-bridge.test.ts +307 -0
  26. package/src/lib/monaco-theme-bridge.ts +154 -10
  27. package/src/markdown-academic/citations.tsx +2 -2
  28. package/src/markdown-academic/footnotes.tsx +2 -2
  29. package/src/markdown-academic/toc.tsx +1 -1
  30. package/src/markdown-editor/directive-views.tsx +2 -5
  31. package/src/markdown-editor/markdown-editor.css +26 -3
  32. package/src/markdown-editor/markdown-editor.focus.test.ts +49 -0
  33. package/src/markdown-editor/markdown-editor.stories.tsx +128 -7
  34. package/src/markdown-editor/markdown-editor.tsx +8 -5
  35. package/src/markdown-editor/milkdown-react/use-get-editor.timer-leak.test.ts +80 -0
  36. package/src/markdown-editor/milkdown-react/use-get-editor.ts +37 -1
  37. package/src/markdown-editor/paste-embed.ts +2 -1
  38. package/src/markdown-editor/slash/slash-menu.stories.tsx +8 -3
  39. package/src/markdown-editor/slash/slash-menu.test.tsx +27 -0
  40. package/src/markdown-editor/slash/slash-menu.tsx +13 -1
  41. package/src/markdown-editor/table-view.tsx +3 -2
  42. package/src/markdown-iteration/iteration-builder-dialog.stories.tsx +57 -19
  43. package/src/markdown-outline/document-outline.stories.tsx +1 -1
  44. package/src/markdown-outline/document-outline.tsx +1 -1
  45. package/src/markdown-preview/markdown-preview-transclusion.test.tsx +1 -1
  46. package/src/markdown-preview/markdown-preview.stories.tsx +24 -2
  47. package/src/markdown-preview/markdown-preview.test.tsx +20 -3
  48. package/src/markdown-toolbar/markdown-toolbar.stories.tsx +3 -0
  49. package/src/markdown-workspace/markdown-workspace.test.tsx +31 -1
  50. package/src/mermaid-diagram/mermaid-viewer.tsx +1 -1
  51. package/src/prose/prose.stories.tsx +3 -0
  52. package/src/prose/prose.test.ts +54 -0
  53. package/src/prose/prose.tsx +7 -0
  54. package/dist/chunk-LBC5VJBD.js.map +0 -1
package/dist/index.css CHANGED
@@ -11,8 +11,10 @@
11
11
  max-width: var(--md-measure, 48rem);
12
12
  margin-inline: auto;
13
13
  }
14
- .milkdown-host .ProseMirror:focus {
15
- outline: none;
14
+ .milkdown-host .ProseMirror:focus-visible {
15
+ outline: 1px solid var(--ring-contour);
16
+ outline-offset: -3px;
17
+ box-shadow: inset 0 0 0 2px var(--ring);
16
18
  }
17
19
  .milkdown-host {
18
20
  display: flex;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/markdown-editor/markdown-editor.css","../src/calc-block/calc-editor.css"],"sourcesContent":["/**\n * Token-driven theme for the Milkdown (ProseMirror) WYSIWYG surface.\n *\n * Milkdown core ships ZERO styles (headless), so this is where brand-ui owns the\n * look. Every value is a semantic design token (var(--foreground), --border,\n * --radius, …) — NO raw colors — so the editor matches all three themes\n * (light/dark) for free. Every rule\n * is scoped under `.milkdown-host` so nothing leaks globally.\n */\n\n.milkdown-host .ProseMirror {\n outline: none;\n padding: 1rem 1.1rem;\n min-height: 9rem;\n color: var(--foreground);\n font-size: 0.875rem;\n line-height: 1.65;\n caret-color: var(--primary);\n word-break: break-word;\n /* Same reading measure as the preview (max-w-3xl). Driven by the shared scale\n (--md-measure, set via markdownScaleVars) so editor + preview match (#18). */\n max-width: var(--md-measure, 48rem);\n margin-inline: auto;\n}\n\n.milkdown-host .ProseMirror:focus {\n outline: none;\n}\n\n/* ----------------------------------------------------------------------------\n * Fill height — the editable grows to fill a definite-height parent.\n *\n * Milkdown's host box is otherwise content-sized (the `.ProseMirror` floor below\n * is `min-height: 9rem`), so inside a tall pane a short document leaves a large\n * dead area below the editable AND the caret-anchored `/` slash menu clips inside\n * the short box near the bottom edge. Making `.milkdown-host` a column flex that\n * fills its parent — and letting the intermediate Milkdown wrapper + the\n * ProseMirror surface grow inside it — means the editable fills the pane\n * (clickable everywhere) and the slash menu has room to open / flip.\n *\n * `min-height: 100%` only resolves against a parent with a DEFINITE height (the\n * workspace's `h-full` wysiwyg pane, or any sized container). In an\n * indefinite-height parent it resolves to `auto`, so the standalone / short\n * editor still sizes to its content above the 9rem floor — no regression for the\n * autodocs / inline cases. (elabs-components: editor-fill-height)\n * ------------------------------------------------------------------------- */\n.milkdown-host {\n display: flex;\n flex-direction: column;\n min-height: 100%;\n}\n.milkdown-host > div,\n.milkdown-host .milkdown {\n display: flex;\n min-height: 0;\n flex: 1 1 auto;\n flex-direction: column;\n}\n.milkdown-host .ProseMirror.editor {\n flex: 1 1 auto;\n}\n\n.milkdown-host .ProseMirror > * + * {\n margin-top: 0.7em;\n}\n\n/* Headings — sizes/weight/tracking come from the SHARED markdown scale\n (markdown-scale.ts → --md-* vars on .milkdown-host), so the editor matches the\n prose `Heading` exactly and can't drift on the next edit to either side (#18).\n Fallbacks equal the canonical scale for use without the vars (e.g. clipboard). */\n.milkdown-host .ProseMirror h1,\n.milkdown-host .ProseMirror h2,\n.milkdown-host .ProseMirror h3,\n.milkdown-host .ProseMirror h4,\n.milkdown-host .ProseMirror h5,\n.milkdown-host .ProseMirror h6 {\n font-weight: var(--md-heading-weight, 600);\n letter-spacing: var(--md-heading-tracking, -0.025em);\n line-height: 1.25;\n color: var(--foreground);\n margin-top: 1.1em;\n}\n.milkdown-host .ProseMirror h1 {\n font-size: var(--md-h1, 1.5rem);\n}\n.milkdown-host .ProseMirror h2 {\n font-size: var(--md-h2, 1.25rem);\n}\n.milkdown-host .ProseMirror h3 {\n font-size: var(--md-h3, 1.125rem);\n}\n.milkdown-host .ProseMirror h4 {\n font-size: var(--md-h4, 1rem);\n}\n.milkdown-host .ProseMirror h5 {\n font-size: var(--md-h5, 0.875rem);\n}\n.milkdown-host .ProseMirror h6 {\n font-size: var(--md-h6, 0.875rem);\n color: var(--muted-foreground);\n}\n\n/* Inline */\n.milkdown-host .ProseMirror a {\n color: var(--primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n.milkdown-host .ProseMirror strong {\n font-weight: 650;\n}\n.milkdown-host .ProseMirror code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.85em;\n background: var(--muted);\n color: var(--foreground);\n padding: 0.12em 0.36em;\n border-radius: calc(var(--radius) - 4px);\n}\n\n/* Blocks */\n.milkdown-host .ProseMirror blockquote {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n color: var(--muted-foreground);\n}\n.milkdown-host .ProseMirror pre {\n background: var(--surface-muted);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.85rem 1rem;\n overflow: auto;\n}\n.milkdown-host .ProseMirror pre code {\n background: transparent;\n padding: 0;\n font-size: 0.82rem;\n}\n.milkdown-host .ProseMirror hr {\n border: none;\n border-top: 1px solid var(--border);\n margin: 1.25em 0;\n}\n\n/* Lists */\n.milkdown-host .ProseMirror ul,\n.milkdown-host .ProseMirror ol {\n padding-left: 1.4rem;\n}\n.milkdown-host .ProseMirror ul {\n list-style: disc;\n}\n.milkdown-host .ProseMirror ol {\n list-style: decimal;\n}\n.milkdown-host .ProseMirror li > * + * {\n margin-top: 0.35em;\n}\n\n/* Tables (GFM) */\n.milkdown-host .ProseMirror table {\n border-collapse: collapse;\n width: 100%;\n font-size: 0.85rem;\n}\n.milkdown-host .ProseMirror th,\n.milkdown-host .ProseMirror td {\n border: 1px solid var(--border);\n padding: 0.4rem 0.6rem;\n text-align: left;\n}\n.milkdown-host .ProseMirror th {\n background: var(--surface-muted);\n font-weight: 600;\n}\n\n/* Task lists (GFM) */\n.milkdown-host .ProseMirror ul[data-item-type=\"task\"] {\n list-style: none;\n padding-left: 0.25rem;\n}\n\n/* Read-only mode dims the caret affordance */\n.milkdown-host .ProseMirror[contenteditable=\"false\"] {\n caret-color: transparent;\n}\n\n/* ---- Brand `:::` directives ----------------------------------------------------\n * These now render as LIVE @brand React components via @prosemirror-adapter/react\n * (see directive-views.tsx), so the box-model chrome (card/callout/metric borders,\n * fills, type accents) comes from the components themselves — NOT from here. This\n * section only owns what the editor adds on top: outer spacing, the editable-body\n * rhythm, the timeline frame (an editable list, not a derived <Timeline>), and the\n * inline-edit affordance for editable attributes (card/callout title, metric\n * label/value). The `toDOM` defs in directive-nodes.ts keep their own marker classes\n * for clipboard serialization but are not visually rendered while node-views are on.\n */\n.milkdown-host .brand-directive {\n margin: 0.85em 0;\n}\n.milkdown-host .brand-directive__body > * + * {\n margin-top: 0.6em;\n}\n/* The first/last editable block inside a card/callout shouldn't add stray margin\n * on top of the component's own padding. */\n.milkdown-host .brand-directive__body > :first-child {\n margin-top: 0;\n}\n\n/* timeline → keep the list editable inside a subtle frame */\n.milkdown-host .brand-directive--timeline {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n}\n\n/* Inline-editable directive attribute (seamless, caret-friendly). */\n.milkdown-host .brand-inline-edit {\n cursor: text;\n min-width: 1ch;\n /* inherit the surrounding component's type (CardTitle, MetricBlock label/value) */\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n}\n.milkdown-host .brand-inline-edit:empty::before {\n content: attr(data-placeholder);\n color: var(--muted-foreground);\n opacity: 0.65;\n pointer-events: none;\n}\n\n/* ------------------------------------------------------------------------- */\n/* Focus writing (Ulysses Phase A): under a `[data-focus-writing]` ancestor */\n/* (the workspace's wysiwyg pane), every top-level block dims except the one */\n/* owning the selection (`.wb-fw-active`, set by the workspace) — paragraph */\n/* focus. Hover un-dims for mouse repositioning. Token-driven, motion-safe. */\n/* ------------------------------------------------------------------------- */\n[data-focus-writing] .milkdown-host .ProseMirror > * {\n opacity: 0.35;\n transition: opacity var(--t-base, 200ms) var(--ease-standard, ease);\n}\n[data-focus-writing] .milkdown-host .ProseMirror > .wb-fw-active,\n[data-focus-writing] .milkdown-host .ProseMirror > *:hover {\n opacity: 1;\n}\n@media (prefers-reduced-motion: reduce) {\n [data-focus-writing] .milkdown-host .ProseMirror > * {\n transition: none;\n }\n}\n","/**\n * Calc authoring decorations (#220) — shared by the Monaco source surface\n * (decoration `inlineClassName`) and the Milkdown WYSIWYG surface (ProseMirror\n * inline decorations + result-inlay widgets).\n *\n * Colors come from the `--calc-*` syntax tokens — the SAME tokens `CalcBlock`\n * paints with (#221), already AA-verified against `--background` (the editor\n * surface) and `--card` in all three themes — so highlighting tracks the theme via\n * the cascade with no hardcoded hex. The compound `.brand-calc-tok.brand-calc-tok--x`\n * selector raises specificity above Monaco's own token color class (`.mtkN`) so\n * the calc color wins regardless of stylesheet load order (no `!important`).\n *\n * Hue is never the SOLE cue (var-def = weight, unresolved = dotted underline), so\n * roles stay distinct in the high-contrast theme where the calc colors collapse\n * toward the foreground — mirroring `CalcBlock.tokenClass`.\n */\n\n.brand-calc-tok.brand-calc-tok--number,\n.brand-calc-tok.brand-calc-tok--constant {\n color: var(--calc-number);\n}\n.brand-calc-tok.brand-calc-tok--unit {\n color: var(--calc-unit);\n}\n.brand-calc-tok.brand-calc-tok--currency {\n color: var(--calc-currency);\n}\n.brand-calc-tok.brand-calc-tok--operator {\n color: var(--calc-operator);\n}\n.brand-calc-tok.brand-calc-tok--function {\n color: var(--calc-function);\n}\n.brand-calc-tok.brand-calc-tok--var-ref {\n color: var(--calc-variable);\n}\n.brand-calc-tok.brand-calc-tok--var-def {\n color: var(--calc-variable);\n font-weight: 500;\n}\n.brand-calc-tok.brand-calc-tok--line-ref {\n color: var(--calc-reference);\n font-variant-numeric: tabular-nums;\n}\n.brand-calc-tok.brand-calc-tok--comment {\n color: var(--calc-comment);\n font-style: italic;\n}\n.brand-calc-tok.brand-calc-tok--unknown {\n color: var(--calc-warning);\n}\n\n/* Non-hue cue for unresolved identifiers / dangling refs — carries the role in\n high-contrast, where hue is flattened. */\n.brand-calc-tok.brand-calc-tok--unresolved {\n text-decoration: underline dotted;\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n}\n\n/**\n * WYSIWYG result inlay — a ProseMirror widget after each calc line showing the\n * computed answer. The number IS the signal (shown as text), color is only an\n * enhancement, and it is non-editable / non-selectable so it never enters the\n * document. (The Monaco surface uses Monaco's own inlay hints, themed via the\n * theme bridge's `editorInlayHint.*` colors — not this class.)\n */\n.brand-calc-inlay {\n /* Full strength (no opacity de-emphasis): the result is the answer, and the\n `--calc-result` token is AA-verified at FULL opacity (#221) — reducing it\n would drop effective contrast below what the contrast gate models. */\n margin-inline-start: 1ch;\n color: var(--calc-result);\n font-variant-numeric: tabular-nums;\n user-select: none;\n white-space: pre;\n}\n.brand-calc-inlay--error {\n color: var(--calc-warning);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/markdown-editor/markdown-editor.css","../src/calc-block/calc-editor.css"],"sourcesContent":["/**\n * Token-driven theme for the Milkdown (ProseMirror) WYSIWYG surface.\n *\n * Milkdown core ships ZERO styles (headless), so this is where brand-ui owns the\n * look. Every value is a semantic design token (var(--foreground), --border,\n * --radius, …) — NO raw colors — so the editor matches every theme\n * (light/dark) for free. Every rule\n * is scoped under `.milkdown-host` so nothing leaks globally.\n */\n\n.milkdown-host .ProseMirror {\n /* Suppresses the platform default outline — NOT a bare removal: the\n `:focus-visible` rule below is the replacement, drawn on this same\n element (#309). It owns the indicator unconditionally, so it can't be\n clipped by an ancestor and can't be deleted by a consumer `className`\n reaching `cn()` on the wrapper (`markdown-editor.tsx`'s root `div`\n intentionally carries no focus-ring utility of its own — see #67/#309). */\n outline: none;\n padding: 1rem 1.1rem;\n min-height: 9rem;\n color: var(--foreground);\n font-size: 0.875rem;\n line-height: 1.65;\n caret-color: var(--primary);\n word-break: break-word;\n /* Same reading measure as the preview (max-w-3xl). Driven by the shared scale\n (--md-measure, set via markdownScaleVars) so editor + preview match (#18). */\n max-width: var(--md-measure, 48rem);\n margin-inline: auto;\n}\n\n/* Focus indicator (#309) — a compound two-layer shape mirroring the\n `focus-ring-inset` Tailwind utility (`packages/tokens/src/themes.css`,\n ADR 0027), hand-written because this stylesheet is a plain side-effect CSS\n import resolved by the CONSUMER's bundler — not guaranteed to run through\n Tailwind, so `@apply`/utility classes aren't reachable here (verified:\n `@apply` appears zero times in any .css file in this repo). Drawn INSIDE\n the editable's own box (`outline-offset: -3px`, an inset `box-shadow`), so\n it can't be clipped by an ancestor and survives a consumer `className`\n deleting the wrapper's classes (`MarkdownWorkspace` already does this via\n `className=\"border-0\"`). Two layers of opposite value so at least one edge\n clears 3:1 against the ground whatever `--ring` resolves to in a given\n theme (`--ring-contour` is the dedicated contour token — `themes-contrast.\n test.ts`'s `INDICATOR_SURFACES` locks `max(ring, contour) >= 3:1` at the\n token level; `markdown-editor.stories.tsx`'s `FocusIndicator` play\n re-measures it on this rendered surface, both themes). */\n.milkdown-host .ProseMirror:focus-visible {\n outline: 1px solid var(--ring-contour);\n outline-offset: -3px;\n box-shadow: inset 0 0 0 2px var(--ring);\n}\n\n/* ----------------------------------------------------------------------------\n * Fill height — the editable grows to fill a definite-height parent.\n *\n * Milkdown's host box is otherwise content-sized (the `.ProseMirror` floor below\n * is `min-height: 9rem`), so inside a tall pane a short document leaves a large\n * dead area below the editable AND the caret-anchored `/` slash menu clips inside\n * the short box near the bottom edge. Making `.milkdown-host` a column flex that\n * fills its parent — and letting the intermediate Milkdown wrapper + the\n * ProseMirror surface grow inside it — means the editable fills the pane\n * (clickable everywhere) and the slash menu has room to open / flip.\n *\n * `min-height: 100%` only resolves against a parent with a DEFINITE height (the\n * workspace's `h-full` wysiwyg pane, or any sized container). In an\n * indefinite-height parent it resolves to `auto`, so the standalone / short\n * editor still sizes to its content above the 9rem floor — no regression for the\n * autodocs / inline cases. (elabs-components: editor-fill-height)\n * ------------------------------------------------------------------------- */\n.milkdown-host {\n display: flex;\n flex-direction: column;\n min-height: 100%;\n}\n.milkdown-host > div,\n.milkdown-host .milkdown {\n display: flex;\n min-height: 0;\n flex: 1 1 auto;\n flex-direction: column;\n}\n.milkdown-host .ProseMirror.editor {\n flex: 1 1 auto;\n}\n\n.milkdown-host .ProseMirror > * + * {\n margin-top: 0.7em;\n}\n\n/* Headings — sizes/weight/tracking come from the SHARED markdown scale\n (markdown-scale.ts → --md-* vars on .milkdown-host), so the editor matches the\n prose `Heading` exactly and can't drift on the next edit to either side (#18).\n Fallbacks equal the canonical scale for use without the vars (e.g. clipboard). */\n.milkdown-host .ProseMirror h1,\n.milkdown-host .ProseMirror h2,\n.milkdown-host .ProseMirror h3,\n.milkdown-host .ProseMirror h4,\n.milkdown-host .ProseMirror h5,\n.milkdown-host .ProseMirror h6 {\n font-weight: var(--md-heading-weight, 600);\n letter-spacing: var(--md-heading-tracking, -0.025em);\n line-height: 1.25;\n color: var(--foreground);\n margin-top: 1.1em;\n}\n.milkdown-host .ProseMirror h1 {\n font-size: var(--md-h1, 1.5rem);\n}\n.milkdown-host .ProseMirror h2 {\n font-size: var(--md-h2, 1.25rem);\n}\n.milkdown-host .ProseMirror h3 {\n font-size: var(--md-h3, 1.125rem);\n}\n.milkdown-host .ProseMirror h4 {\n font-size: var(--md-h4, 1rem);\n}\n.milkdown-host .ProseMirror h5 {\n font-size: var(--md-h5, 0.875rem);\n}\n.milkdown-host .ProseMirror h6 {\n font-size: var(--md-h6, 0.875rem);\n color: var(--muted-foreground);\n}\n\n/* Inline */\n.milkdown-host .ProseMirror a {\n color: var(--primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n.milkdown-host .ProseMirror strong {\n font-weight: 650;\n}\n.milkdown-host .ProseMirror code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.85em;\n background: var(--muted);\n color: var(--foreground);\n padding: 0.12em 0.36em;\n border-radius: calc(var(--radius) - 4px);\n}\n\n/* Blocks */\n.milkdown-host .ProseMirror blockquote {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n color: var(--muted-foreground);\n}\n.milkdown-host .ProseMirror pre {\n background: var(--surface-muted);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.85rem 1rem;\n overflow: auto;\n}\n.milkdown-host .ProseMirror pre code {\n background: transparent;\n padding: 0;\n font-size: 0.82rem;\n}\n.milkdown-host .ProseMirror hr {\n border: none;\n border-top: 1px solid var(--border);\n margin: 1.25em 0;\n}\n\n/* Lists */\n.milkdown-host .ProseMirror ul,\n.milkdown-host .ProseMirror ol {\n padding-left: 1.4rem;\n}\n.milkdown-host .ProseMirror ul {\n list-style: disc;\n}\n.milkdown-host .ProseMirror ol {\n list-style: decimal;\n}\n.milkdown-host .ProseMirror li > * + * {\n margin-top: 0.35em;\n}\n\n/* Tables (GFM) */\n.milkdown-host .ProseMirror table {\n border-collapse: collapse;\n width: 100%;\n font-size: 0.85rem;\n}\n.milkdown-host .ProseMirror th,\n.milkdown-host .ProseMirror td {\n border: 1px solid var(--border);\n padding: 0.4rem 0.6rem;\n text-align: left;\n}\n.milkdown-host .ProseMirror th {\n background: var(--surface-muted);\n font-weight: 600;\n}\n\n/* Task lists (GFM) */\n.milkdown-host .ProseMirror ul[data-item-type=\"task\"] {\n list-style: none;\n padding-left: 0.25rem;\n}\n\n/* Read-only mode dims the caret affordance */\n.milkdown-host .ProseMirror[contenteditable=\"false\"] {\n caret-color: transparent;\n}\n\n/* ---- Brand `:::` directives ----------------------------------------------------\n * These now render as LIVE @brand React components via @prosemirror-adapter/react\n * (see directive-views.tsx), so the box-model chrome (card/callout/metric borders,\n * fills, type accents) comes from the components themselves — NOT from here. This\n * section only owns what the editor adds on top: outer spacing, the editable-body\n * rhythm, the timeline frame (an editable list, not a derived <Timeline>), and the\n * inline-edit affordance for editable attributes (card/callout title, metric\n * label/value). The `toDOM` defs in directive-nodes.ts keep their own marker classes\n * for clipboard serialization but are not visually rendered while node-views are on.\n */\n.milkdown-host .brand-directive {\n margin: 0.85em 0;\n}\n.milkdown-host .brand-directive__body > * + * {\n margin-top: 0.6em;\n}\n/* The first/last editable block inside a card/callout shouldn't add stray margin\n * on top of the component's own padding. */\n.milkdown-host .brand-directive__body > :first-child {\n margin-top: 0;\n}\n\n/* timeline → keep the list editable inside a subtle frame */\n.milkdown-host .brand-directive--timeline {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n}\n\n/* Inline-editable directive attribute (seamless, caret-friendly). */\n.milkdown-host .brand-inline-edit {\n cursor: text;\n min-width: 1ch;\n /* inherit the surrounding component's type (CardTitle, MetricBlock label/value) */\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n}\n.milkdown-host .brand-inline-edit:empty::before {\n content: attr(data-placeholder);\n color: var(--muted-foreground);\n opacity: 0.65;\n pointer-events: none;\n}\n\n/* ------------------------------------------------------------------------- */\n/* Focus writing (Ulysses Phase A): under a `[data-focus-writing]` ancestor */\n/* (the workspace's wysiwyg pane), every top-level block dims except the one */\n/* owning the selection (`.wb-fw-active`, set by the workspace) — paragraph */\n/* focus. Hover un-dims for mouse repositioning. Token-driven, motion-safe. */\n/* ------------------------------------------------------------------------- */\n[data-focus-writing] .milkdown-host .ProseMirror > * {\n opacity: 0.35;\n transition: opacity var(--t-base, 200ms) var(--ease-standard, ease);\n}\n[data-focus-writing] .milkdown-host .ProseMirror > .wb-fw-active,\n[data-focus-writing] .milkdown-host .ProseMirror > *:hover {\n opacity: 1;\n}\n@media (prefers-reduced-motion: reduce) {\n [data-focus-writing] .milkdown-host .ProseMirror > * {\n transition: none;\n }\n}\n","/**\n * Calc authoring decorations (#220) — shared by the Monaco source surface\n * (decoration `inlineClassName`) and the Milkdown WYSIWYG surface (ProseMirror\n * inline decorations + result-inlay widgets).\n *\n * Colors come from the `--calc-*` syntax tokens — the SAME tokens `CalcBlock`\n * paints with (#221), already AA-verified against `--background` (the editor\n * surface) and `--card` in every theme — so highlighting tracks the theme via\n * the cascade with no hardcoded hex. The compound `.brand-calc-tok.brand-calc-tok--x`\n * selector raises specificity above Monaco's own token color class (`.mtkN`) so\n * the calc color wins regardless of stylesheet load order (no `!important`).\n *\n * Hue is never the SOLE cue (var-def = weight, unresolved = dotted underline), so\n * roles stay distinct in the high-contrast theme where the calc colors collapse\n * toward the foreground — mirroring `CalcBlock.tokenClass`.\n */\n\n.brand-calc-tok.brand-calc-tok--number,\n.brand-calc-tok.brand-calc-tok--constant {\n color: var(--calc-number);\n}\n.brand-calc-tok.brand-calc-tok--unit {\n color: var(--calc-unit);\n}\n.brand-calc-tok.brand-calc-tok--currency {\n color: var(--calc-currency);\n}\n.brand-calc-tok.brand-calc-tok--operator {\n color: var(--calc-operator);\n}\n.brand-calc-tok.brand-calc-tok--function {\n color: var(--calc-function);\n}\n.brand-calc-tok.brand-calc-tok--var-ref {\n color: var(--calc-variable);\n}\n.brand-calc-tok.brand-calc-tok--var-def {\n color: var(--calc-variable);\n font-weight: 500;\n}\n.brand-calc-tok.brand-calc-tok--line-ref {\n color: var(--calc-reference);\n font-variant-numeric: tabular-nums;\n}\n.brand-calc-tok.brand-calc-tok--comment {\n color: var(--calc-comment);\n font-style: italic;\n}\n.brand-calc-tok.brand-calc-tok--unknown {\n color: var(--calc-warning);\n}\n\n/* Non-hue cue for unresolved identifiers / dangling refs — carries the role in\n high-contrast, where hue is flattened. */\n.brand-calc-tok.brand-calc-tok--unresolved {\n text-decoration: underline dotted;\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n}\n\n/**\n * WYSIWYG result inlay — a ProseMirror widget after each calc line showing the\n * computed answer. The number IS the signal (shown as text), color is only an\n * enhancement, and it is non-editable / non-selectable so it never enters the\n * document. (The Monaco surface uses Monaco's own inlay hints, themed via the\n * theme bridge's `editorInlayHint.*` colors — not this class.)\n */\n.brand-calc-inlay {\n /* Full strength (no opacity de-emphasis): the result is the answer, and the\n `--calc-result` token is AA-verified at FULL opacity (#221) — reducing it\n would drop effective contrast below what the contrast gate models. */\n margin-inline-start: 1ch;\n color: var(--calc-result);\n font-variant-numeric: tabular-nums;\n user-select: none;\n white-space: pre;\n}\n.brand-calc-inlay--error {\n color: var(--calc-warning);\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as EditorContentAccess, M as MonacoCodeEditor } from './markdown-editor-DfBZibAn.js';
2
- export { C as CodeEditor, a as CodeEditorProps, b as EditorAction, c as EditorSelection, d as EmbedAssetFn, e as MarkdownEditor, f as MarkdownEditorHandle, g as MarkdownEditorProps, m as monacoContentAccess } from './markdown-editor-DfBZibAn.js';
1
+ import { E as EditorContentAccess, M as MonacoCodeEditor } from './markdown-editor-CBp_4eDv.js';
2
+ export { C as CodeEditor, a as CodeEditorProps, b as EditorAction, c as EditorSelection, d as EmbedAssetFn, e as MarkdownEditor, f as MarkdownEditorHandle, g as MarkdownEditorProps, m as monacoContentAccess } from './markdown-editor-CBp_4eDv.js';
3
3
  import * as react from 'react';
4
4
  import { HTMLAttributes, ReactNode, ComponentProps } from 'react';
5
5
  import * as monaco from 'monaco-editor';
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  buildBrandThemeData,
11
11
  monacoContentAccess,
12
12
  useDataTheme
13
- } from "./chunk-LBC5VJBD.js";
13
+ } from "./chunk-C62O7IOQ.js";
14
14
 
15
15
  // src/diff-editor/diff-editor.tsx
16
16
  import * as monaco from "monaco-editor";
@@ -116,7 +116,7 @@ var DiffEditor = forwardRef(function DiffEditor2({
116
116
  });
117
117
 
118
118
  // src/code-workspace/code-workspace.tsx
119
- import { Tabs, TabsList, TabsTrigger } from "@elabs-ai/components-ui";
119
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@elabs-ai/components-ui";
120
120
  import { cn as cn2 } from "@elabs-ai/components-ui/lib/cn";
121
121
  import {
122
122
  forwardRef as forwardRef2,
@@ -150,6 +150,13 @@ var EXT_LANGUAGE = {
150
150
  sh: "shell"
151
151
  };
152
152
  var basename = (path) => path.split("/").pop() || path;
153
+ function tabIdForFile(path) {
154
+ let out = "";
155
+ for (const char of path) {
156
+ out += /[A-Za-z0-9-]/.test(char) ? char : `_${char.codePointAt(0).toString(16)}_`;
157
+ }
158
+ return `tab-${out}`;
159
+ }
153
160
  function inferLanguage(file) {
154
161
  if (file.language) return file.language;
155
162
  const ext = file.path.split(".").pop()?.toLowerCase() ?? "";
@@ -181,6 +188,8 @@ var CodeWorkspace = forwardRef2(
181
188
  const requested = isControlled ? activePath : internalActive;
182
189
  const activeFile = files.find((f) => f.path === requested) ?? files[0];
183
190
  const active = activeFile?.path ?? "";
191
+ const tabs = files.map((file) => ({ file, id: tabIdForFile(file.path) }));
192
+ const activeTabId = tabs.find((t) => t.file.path === active)?.id ?? "";
184
193
  const rootRef = useRef2(null);
185
194
  const [activeEditor, setActiveEditor] = useState2(null);
186
195
  useEffect2(() => {
@@ -236,35 +245,63 @@ var CodeWorkspace = forwardRef2(
236
245
  children: /* @__PURE__ */ jsxs(
237
246
  Tabs,
238
247
  {
239
- value: active,
240
- onValueChange: selectTab,
248
+ value: activeTabId,
249
+ onValueChange: (id) => {
250
+ const found = tabs.find((t) => t.id === id);
251
+ if (found) selectTab(found.file.path);
252
+ },
241
253
  className: "flex min-h-0 flex-1 flex-col gap-0",
242
254
  children: [
243
255
  /* @__PURE__ */ jsxs("div", { className: "flex items-center border-b border-border bg-surface", children: [
244
- /* @__PURE__ */ jsx2(TabsList, { className: "h-9 flex-1 justify-start gap-0 rounded-none bg-transparent p-0", children: files.map((file) => /* @__PURE__ */ jsx2(
256
+ /* @__PURE__ */ jsx2(TabsList, { className: "h-9 flex-1 justify-start gap-0 rounded-none bg-transparent p-0", children: tabs.map(({ file, id }) => /* @__PURE__ */ jsx2(
245
257
  TabsTrigger,
246
258
  {
247
- value: file.path,
259
+ value: id,
248
260
  className: "h-9 rounded-none border-e border-border px-3 text-xs data-[state=active]:bg-background data-[state=active]:shadow-none",
249
261
  children: basename(file.path)
250
262
  },
251
- file.path
263
+ id
252
264
  )) }),
253
265
  activeFile ? /* @__PURE__ */ jsx2(CopyButton, { value: activeFile.value, className: "me-1.5" }) : null
254
266
  ] }),
255
- /* @__PURE__ */ jsx2("div", { className: "min-h-0 flex-1", children: activeFile ? /* @__PURE__ */ jsx2(
256
- CodeEditor,
257
- {
258
- path: activeFile.path,
259
- language: inferLanguage(activeFile),
260
- value: activeFile.value,
261
- readOnly,
262
- onChange: (next) => onFileChange?.(activeFile.path, next),
263
- onMount: (editor2) => setActiveEditor(editor2),
264
- height: "100%"
265
- },
266
- activeFile.path
267
- ) : null })
267
+ tabs.map(({ file, id }) => {
268
+ const isActive = file.path === activeFile?.path;
269
+ return (
270
+ // `forceMount` keeps every tab's content in the DOM so each
271
+ // trigger's `aria-controls` resolves to a real element, not only
272
+ // the active one (#154). Radix derives its own `hidden` from
273
+ // `forceMount || isSelected`, so under `forceMount` it hides
274
+ // NOTHING: without the explicit `hidden` below every inactive
275
+ // panel stays a visible `flex-1` child and the editor height is
276
+ // split across all of them. `tabIndex={-1}` keeps the empty
277
+ // panels out of the tab sequence for the same reason. Only the
278
+ // active file mounts a (single) Monaco instance.
279
+ /* @__PURE__ */ jsx2(
280
+ TabsContent,
281
+ {
282
+ value: id,
283
+ forceMount: true,
284
+ hidden: !isActive,
285
+ tabIndex: isActive ? 0 : -1,
286
+ className: "mt-0 min-h-0 flex-1",
287
+ children: isActive ? /* @__PURE__ */ jsx2(
288
+ CodeEditor,
289
+ {
290
+ path: file.path,
291
+ language: inferLanguage(file),
292
+ value: file.value,
293
+ readOnly,
294
+ onChange: (next) => onFileChange?.(file.path, next),
295
+ onMount: (editor2) => setActiveEditor(editor2),
296
+ height: "100%"
297
+ },
298
+ file.path
299
+ ) : null
300
+ },
301
+ id
302
+ )
303
+ );
304
+ })
268
305
  ]
269
306
  }
270
307
  )
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/diff-editor/diff-editor.tsx","../src/code-workspace/code-workspace.tsx","../src/editor-toolbar/editor-toolbar.tsx","../src/lib/languages.ts","../src/index.ts"],"sourcesContent":["\"use client\";\n\nimport * 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 { applyBrandTheme } from \"../lib/monaco-theme-bridge\";\nimport { useDataTheme } from \"../lib/use-data-theme\";\n\nexport type MonacoDiffEditor = monaco.editor.IStandaloneDiffEditor;\n\nexport interface DiffEditorProps extends Omit<HTMLAttributes<HTMLDivElement>, \"defaultValue\"> {\n /** Left/original document. */\n original: string;\n /** Right/modified document. */\n modified: string;\n /** Monaco language id applied to both sides. Defaults to \"typescript\". */\n language?: string;\n /** Read-only (diffs are read-only by default). */\n readOnly?: boolean;\n /** Side-by-side (true) vs inline (false) diff. Defaults to side-by-side. */\n renderSideBySide?: boolean;\n /** Editor height. Number → px. Defaults to \"100%\". */\n height?: number | string;\n /** Passthrough Monaco diff options (merged over the defaults). */\n options?: monaco.editor.IStandaloneDiffEditorConstructionOptions;\n /** Called once the diff editor + monaco namespace are ready. */\n onMount?: (editor: MonacoDiffEditor, monacoApi: typeof monaco) => void;\n}\n\nconst BASE_OPTIONS: monaco.editor.IStandaloneDiffEditorConstructionOptions = {\n automaticLayout: true,\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n fontSize: 13,\n ignoreTrimWhitespace: false,\n padding: { top: 12, bottom: 12 },\n scrollbar: { verticalScrollbarSize: 10, horizontalScrollbarSize: 10 },\n};\n\n/**\n * A token-themed Monaco diff editor (original ↔ modified). Same theming bridge\n * and chrome story as {@link CodeEditor}; reads as a side-by-side or inline diff.\n */\nexport const DiffEditor = forwardRef<MonacoDiffEditor | null, DiffEditorProps>(function DiffEditor(\n {\n original,\n modified,\n language = \"typescript\",\n readOnly = true,\n renderSideBySide = true,\n height = \"100%\",\n options,\n onMount,\n className,\n style,\n ...props\n },\n ref,\n) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [editor, setEditor] = useState<MonacoDiffEditor | null>(null);\n const { theme, revision } = useDataTheme();\n\n const onMountRef = useRef(onMount);\n onMountRef.current = onMount;\n\n useImperativeHandle<MonacoDiffEditor | null, MonacoDiffEditor | null>(ref, () => editor, [\n editor,\n ]);\n\n // Mount once.\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const instance = monaco.editor.createDiffEditor(container, {\n ...BASE_OPTIONS,\n readOnly,\n renderSideBySide,\n ...options,\n });\n const originalModel = monaco.editor.createModel(original, language);\n const modifiedModel = monaco.editor.createModel(modified, language);\n instance.setModel({ original: originalModel, modified: modifiedModel });\n // Theme is applied by the effect below once `setEditor` runs.\n setEditor(instance);\n onMountRef.current?.(instance, monaco);\n\n return () => {\n instance.dispose();\n originalModel.dispose();\n modifiedModel.dispose();\n setEditor(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const original_ = editor?.getModel()?.original;\n if (original_ && original_.getValue() !== original) original_.setValue(original);\n }, [editor, original]);\n\n useEffect(() => {\n const modified_ = editor?.getModel()?.modified;\n if (modified_ && modified_.getValue() !== modified) modified_.setValue(modified);\n }, [editor, modified]);\n\n useEffect(() => {\n const models = editor?.getModel();\n if (!models) return;\n monaco.editor.setModelLanguage(models.original, language);\n monaco.editor.setModelLanguage(models.modified, language);\n }, [editor, language]);\n\n useEffect(() => {\n editor?.updateOptions({ readOnly, renderSideBySide });\n }, [editor, readOnly, renderSideBySide]);\n\n useEffect(() => {\n if (!editor) return;\n try {\n applyBrandTheme(monaco, theme);\n } catch (err) {\n console.error(\"[@elabs-ai/components-editor] failed to apply brand theme\", err);\n }\n // `revision` re-applies once the data-theme attribute settles. See CodeEditor.\n }, [editor, theme, revision]);\n\n const resolvedStyle: CSSProperties = {\n height: typeof height === \"number\" ? `${height}px` : height,\n ...style,\n };\n\n return (\n <div\n ref={containerRef}\n data-testid=\"diff-editor\"\n className={cn(\"h-full w-full overflow-hidden bg-background text-foreground\", className)}\n style={resolvedStyle}\n {...props}\n />\n );\n});\n","\"use client\";\n\nimport { Tabs, TabsList, TabsTrigger } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n} from \"react\";\nimport { CodeEditor, type MonacoCodeEditor } from \"../code-editor\";\nimport { CopyButton } from \"../copy-button\";\nimport {\n monacoContentAccess,\n type EditorContentAccess,\n type EditorSelection,\n} from \"../lib/editor-content-access\";\n\nexport interface EditorFile {\n /** Unique path/name; shown (basename) on the tab and used as the model path. */\n path: string;\n /** Monaco language id. Inferred from the extension when omitted. */\n language?: string;\n /** File content. */\n value: string;\n}\n\nexport interface CodeWorkspaceProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Open files, one tab each. */\n files: EditorFile[];\n /** Controlled active file path. */\n activePath?: string;\n /** Initial active path for uncontrolled use. Defaults to the first file. */\n defaultActivePath?: string;\n /** Called when the active tab changes. */\n onActivePathChange?: (path: string) => void;\n /** Called on every edit with the file path + new content. */\n onFileChange?: (path: string, value: string) => void;\n /** Render all files read-only. */\n readOnly?: boolean;\n /** Workspace height. Number → px. Defaults to \"100%\". */\n height?: number | string;\n}\n\nconst EXT_LANGUAGE: Record<string, string> = {\n ts: \"typescript\",\n tsx: \"typescript\",\n mts: \"typescript\",\n cts: \"typescript\",\n js: \"javascript\",\n jsx: \"javascript\",\n mjs: \"javascript\",\n cjs: \"javascript\",\n json: \"json\",\n css: \"css\",\n scss: \"scss\",\n less: \"less\",\n html: \"html\",\n md: \"markdown\",\n py: \"python\",\n sql: \"sql\",\n yml: \"yaml\",\n yaml: \"yaml\",\n xml: \"xml\",\n sh: \"shell\",\n};\n\nconst basename = (path: string) => path.split(\"/\").pop() || path;\n\nfunction inferLanguage(file: EditorFile): string {\n if (file.language) return file.language;\n const ext = file.path.split(\".\").pop()?.toLowerCase() ?? \"\";\n return EXT_LANGUAGE[ext] ?? \"plaintext\";\n}\n\n/**\n * Imperative handle exposed via `CodeWorkspace`'s `ref`.\n *\n * **Migration note:** the forwarded `ref` type changed from `HTMLDivElement` to\n * `CodeWorkspaceHandle` (mirrors the #273 `MarkdownWorkspaceHandle` precedent).\n * Replace any `ref.current` / `ref.current.scrollIntoView()` DOM access with\n * `ref.current?.getElement()`. This is the only breaking change in this work.\n */\nexport interface CodeWorkspaceHandle extends EditorContentAccess {\n /**\n * The live Monaco editor for the currently active tab, or `null` while booting\n * or when no files are open. Consumers needing the full Monaco API can use this\n * and pass it to `monacoContentAccess(handle.getActiveEditor()!)`.\n */\n getActiveEditor(): MonacoCodeEditor | null;\n /**\n * The workspace root DOM element. Preserves the old `HTMLDivElement` ref access\n * that existed before the ref-type change (#273 `MarkdownWorkspace` playbook).\n */\n getElement(): HTMLDivElement | null;\n}\n\n/** No-op EditorContentAccess returned when no Monaco editor is mounted. */\nconst NULL_ACCESS: EditorContentAccess = {\n getText: () => \"\",\n getSelection: () => ({ text: \"\", empty: true }),\n replaceSelection: () => undefined,\n insertAtCursor: () => undefined,\n focus: () => undefined,\n onSelectionChange: () => () => undefined,\n};\n\n/**\n * Multi-file editor shell: brand-ui `Tabs` for the file bar above a single\n * {@link CodeEditor} that swaps to the active file. This is the \"reuse our\n * components for the chrome\" surface — Tabs/Button are ours; the editing surface\n * is Monaco. Edits bubble through `onFileChange`.\n *\n * The forwarded `ref` exposes a {@link CodeWorkspaceHandle} (not `HTMLDivElement`).\n * Use `ref.current?.getElement()` to access the root DOM node.\n */\nexport const CodeWorkspace = forwardRef<CodeWorkspaceHandle, CodeWorkspaceProps>(\n function CodeWorkspace(\n {\n files,\n activePath,\n defaultActivePath,\n onActivePathChange,\n onFileChange,\n readOnly = false,\n height = \"100%\",\n className,\n style,\n ...props\n },\n ref,\n ) {\n const [internalActive, setInternalActive] = useState(defaultActivePath ?? files[0]?.path ?? \"\");\n const isControlled = activePath !== undefined;\n const requested = isControlled ? activePath : internalActive;\n const activeFile = files.find((f) => f.path === requested) ?? files[0];\n const active = activeFile?.path ?? \"\";\n\n // The root div ref (getElement() preserves old HTMLDivElement access).\n const rootRef = useRef<HTMLDivElement | null>(null);\n\n // The live Monaco editor for the active tab. Nulled when there is no active file.\n const [activeEditor, setActiveEditor] = useState<MonacoCodeEditor | null>(null);\n\n // Defensive: if the active file is removed (no `activeFile`), null the held instance\n // so getActiveEditor() and content-access methods don't act on a disposed editor.\n useEffect(() => {\n if (!activeFile) setActiveEditor(null);\n }, [activeFile]);\n\n // A STABLE listener set for onSelectionChange. The handle adds/removes here;\n // the binding effect below forwards the ACTIVE editor's selection changes into\n // it. This is what makes a subscribe-in-mount-effect survive the editor's async\n // mount (and tab swaps): the consumer subscribes once; binding re-attaches as\n // `activeEditor` changes. (Delegating onSelectionChange to a call-time snapshot\n // would bind to the not-yet-mounted editor and silently never fire.)\n const [selectionListeners] = useState(() => new Set<(sel: EditorSelection) => void>());\n useEffect(() => {\n if (!activeEditor) return;\n const unsub = monacoContentAccess(activeEditor).onSelectionChange((sel) =>\n selectionListeners.forEach((l) => l(sel)),\n );\n return unsub;\n }, [activeEditor, selectionListeners]);\n\n const selectTab = useCallback(\n (path: string) => {\n if (!isControlled) setInternalActive(path);\n onActivePathChange?.(path);\n },\n [isControlled, onActivePathChange],\n );\n\n useImperativeHandle(ref, () => {\n const access = activeEditor ? monacoContentAccess(activeEditor) : NULL_ACCESS;\n return {\n getActiveEditor: () => activeEditor,\n getElement: () => rootRef.current,\n getText: () => access.getText(),\n getSelection: () => access.getSelection(),\n replaceSelection: (text: string) => access.replaceSelection(text),\n insertAtCursor: (text: string) => access.insertAtCursor(text),\n focus: () => access.focus(),\n // Stable set (not the call-time `access`) so a subscription survives the\n // editor's async mount + tab swaps; the binding effect forwards events.\n onSelectionChange: (listener) => {\n selectionListeners.add(listener);\n return () => selectionListeners.delete(listener);\n },\n };\n }, [activeEditor, selectionListeners]);\n\n const resolvedStyle: CSSProperties = {\n height: typeof height === \"number\" ? `${height}px` : height,\n ...style,\n };\n\n return (\n <div\n ref={rootRef}\n className={cn(\n \"flex flex-col overflow-hidden rounded-lg border border-border bg-surface\",\n className,\n )}\n style={resolvedStyle}\n {...props}\n >\n <Tabs\n value={active}\n onValueChange={selectTab}\n className=\"flex min-h-0 flex-1 flex-col gap-0\"\n >\n <div className=\"flex items-center border-b border-border bg-surface\">\n <TabsList className=\"h-9 flex-1 justify-start gap-0 rounded-none bg-transparent p-0\">\n {files.map((file) => (\n <TabsTrigger\n key={file.path}\n value={file.path}\n className=\"h-9 rounded-none border-e border-border px-3 text-xs data-[state=active]:bg-background data-[state=active]:shadow-none\"\n >\n {basename(file.path)}\n </TabsTrigger>\n ))}\n </TabsList>\n {activeFile ? <CopyButton value={activeFile.value} className=\"me-1.5\" /> : null}\n </div>\n\n <div className=\"min-h-0 flex-1\">\n {activeFile ? (\n <CodeEditor\n key={activeFile.path}\n path={activeFile.path}\n language={inferLanguage(activeFile)}\n value={activeFile.value}\n readOnly={readOnly}\n onChange={(next) => onFileChange?.(activeFile.path, next)}\n onMount={(editor) => setActiveEditor(editor)}\n height=\"100%\"\n />\n ) : null}\n </div>\n </Tabs>\n </div>\n );\n },\n);\n","\"use client\";\n\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport { CopyButton } from \"../copy-button\";\nimport { EDITOR_LANGUAGES, type EditorLanguage } from \"../lib/languages\";\n\nexport interface EditorToolbarProps extends HTMLAttributes<HTMLDivElement> {\n /** Filename shown on the left (e.g. \"index.ts\"). */\n filename?: ReactNode;\n /** Active language id; shows a language picker when `onLanguageChange` is set. */\n language?: string;\n /** Called when the user picks a language. Omit to hide the picker. */\n onLanguageChange?: (language: string) => void;\n /** Languages offered in the picker. Defaults to {@link EDITOR_LANGUAGES}. */\n languages?: EditorLanguage[];\n /** Text copied by the copy button. Omit to hide the button. */\n copyValue?: string;\n /** Extra actions rendered before the copy button (brand-ui buttons, etc.). */\n actions?: ReactNode;\n}\n\n/**\n * Brand-ui chrome for the editors — the part that genuinely reuses our\n * components. Renders a filename label, an optional language `Select` and a copy\n * `Button`. The editing surface itself is Monaco; this is the frame around it.\n */\nexport const EditorToolbar = forwardRef<HTMLDivElement, EditorToolbarProps>(function EditorToolbar(\n {\n filename,\n language,\n onLanguageChange,\n languages = EDITOR_LANGUAGES,\n copyValue,\n actions,\n className,\n ...props\n },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(\n \"flex h-10 shrink-0 items-center gap-2 border-b border-border bg-surface px-3\",\n className,\n )}\n {...props}\n >\n {filename != null ? (\n <span className=\"truncate text-sm font-medium text-foreground\">{filename}</span>\n ) : null}\n\n <div className=\"ml-auto flex items-center gap-1.5\">\n {actions}\n\n {onLanguageChange ? (\n <Select value={language} onValueChange={onLanguageChange}>\n <SelectTrigger size=\"sm\" className=\"h-7 w-[140px]\" aria-label=\"Language\">\n <SelectValue placeholder=\"Language\" />\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l.id} value={l.id}>\n {l.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ) : null}\n\n {copyValue != null ? <CopyButton value={copyValue} /> : null}\n </div>\n </div>\n );\n});\n","/**\n * Curated language list for the editor toolbar's language picker. Ids are\n * Monaco's built-in language ids (`monaco.languages.getLanguages()`), so they\n * map straight onto `setModelLanguage` / model creation.\n */\nexport interface EditorLanguage {\n /** Monaco language id (e.g. \"typescript\"). */\n id: string;\n /** Human-readable label for the picker. */\n label: string;\n}\n\nexport const EDITOR_LANGUAGES: EditorLanguage[] = [\n { id: \"typescript\", label: \"TypeScript\" },\n { id: \"javascript\", label: \"JavaScript\" },\n { id: \"json\", label: \"JSON\" },\n { id: \"html\", label: \"HTML\" },\n { id: \"css\", label: \"CSS\" },\n { id: \"scss\", label: \"SCSS\" },\n { id: \"markdown\", label: \"Markdown\" },\n { id: \"python\", label: \"Python\" },\n { id: \"sql\", label: \"SQL\" },\n { id: \"yaml\", label: \"YAML\" },\n { id: \"xml\", label: \"XML\" },\n { id: \"shell\", label: \"Shell\" },\n { id: \"plaintext\", label: \"Plain text\" },\n];\n\n/** Look up a language's label, falling back to the raw id. */\nexport function languageLabel(id: string): string {\n return EDITOR_LANGUAGES.find((l) => l.id === id)?.label ?? id;\n}\n","/**\n * @elabs-ai/components-editor — a token-themed Monaco (VS Code) editor wrapped as brand-ui\n * components.\n *\n * Monaco is a self-rendering editor engine; this package wraps it the way\n * @elabs-ai/components-flow wraps React Flow and @elabs-ai/components-data wraps TanStack. The editing\n * surface + widgets are Monaco's, recolored from the active brand theme via the\n * theming bridge; the chrome (toolbar, file tabs, copy button) is brand-ui.\n *\n * Worker setup (for completions/diagnostics) — import ONCE at the app entry:\n * import \"@elabs-ai/components-editor/monaco-environment\"; // Vite apps\n */\nexport {\n CodeEditor,\n type CodeEditorProps,\n type MonacoCodeEditor,\n type EditorAction,\n} from \"./code-editor\";\nexport { DiffEditor, type DiffEditorProps, type MonacoDiffEditor } from \"./diff-editor\";\nexport {\n MarkdownEditor,\n type MarkdownEditorProps,\n type MarkdownEditorHandle,\n type EmbedAssetFn,\n} from \"./markdown-editor\";\nexport {\n CodeWorkspace,\n type CodeWorkspaceHandle,\n type CodeWorkspaceProps,\n type EditorFile,\n} from \"./code-workspace\";\nexport {\n monacoContentAccess,\n type EditorContentAccess,\n type EditorSelection,\n} from \"./lib/editor-content-access\";\nexport { EditorToolbar, type EditorToolbarProps } from \"./editor-toolbar\";\nexport { EditorContextMenu, type EditorContextMenuProps } from \"./editor-context-menu\";\nexport { CopyButton, type CopyButtonProps } from \"./copy-button\";\n\n// Theming bridge + language helpers (advanced consumers / custom chrome).\nexport { applyBrandTheme, buildBrandThemeData, brandThemeId } from \"./lib/monaco-theme-bridge\";\nexport { useDataTheme, type DataThemeState } from \"./lib/use-data-theme\";\nexport { EDITOR_LANGUAGES, languageLabel, type EditorLanguage } from \"./lib/languages\";\n\n// Re-export the monaco namespace so consumers can build custom editors / wire\n// commands without adding a direct dependency.\nexport * as monaco from \"monaco-editor\";\n"],"mappings":"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","names":["DiffEditor","editor","cn","forwardRef","useEffect","useImperativeHandle","useRef","useState","jsx","forwardRef","CodeWorkspace","useState","useRef","useEffect","useImperativeHandle","cn","editor","cn","forwardRef","jsx","jsxs","forwardRef","EditorToolbar","cn","monaco"]}
1
+ {"version":3,"sources":["../src/diff-editor/diff-editor.tsx","../src/code-workspace/code-workspace.tsx","../src/editor-toolbar/editor-toolbar.tsx","../src/lib/languages.ts","../src/index.ts"],"sourcesContent":["\"use client\";\n\nimport * 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 { applyBrandTheme } from \"../lib/monaco-theme-bridge\";\nimport { useDataTheme } from \"../lib/use-data-theme\";\n\nexport type MonacoDiffEditor = monaco.editor.IStandaloneDiffEditor;\n\nexport interface DiffEditorProps extends Omit<HTMLAttributes<HTMLDivElement>, \"defaultValue\"> {\n /** Left/original document. */\n original: string;\n /** Right/modified document. */\n modified: string;\n /** Monaco language id applied to both sides. Defaults to \"typescript\". */\n language?: string;\n /** Read-only (diffs are read-only by default). */\n readOnly?: boolean;\n /** Side-by-side (true) vs inline (false) diff. Defaults to side-by-side. */\n renderSideBySide?: boolean;\n /** Editor height. Number → px. Defaults to \"100%\". */\n height?: number | string;\n /** Passthrough Monaco diff options (merged over the defaults). */\n options?: monaco.editor.IStandaloneDiffEditorConstructionOptions;\n /** Called once the diff editor + monaco namespace are ready. */\n onMount?: (editor: MonacoDiffEditor, monacoApi: typeof monaco) => void;\n}\n\nconst BASE_OPTIONS: monaco.editor.IStandaloneDiffEditorConstructionOptions = {\n automaticLayout: true,\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n fontSize: 13,\n ignoreTrimWhitespace: false,\n padding: { top: 12, bottom: 12 },\n scrollbar: { verticalScrollbarSize: 10, horizontalScrollbarSize: 10 },\n};\n\n/**\n * A token-themed Monaco diff editor (original ↔ modified). Same theming bridge\n * and chrome story as {@link CodeEditor}; reads as a side-by-side or inline diff.\n */\nexport const DiffEditor = forwardRef<MonacoDiffEditor | null, DiffEditorProps>(function DiffEditor(\n {\n original,\n modified,\n language = \"typescript\",\n readOnly = true,\n renderSideBySide = true,\n height = \"100%\",\n options,\n onMount,\n className,\n style,\n ...props\n },\n ref,\n) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [editor, setEditor] = useState<MonacoDiffEditor | null>(null);\n const { theme, revision } = useDataTheme();\n\n const onMountRef = useRef(onMount);\n onMountRef.current = onMount;\n\n useImperativeHandle<MonacoDiffEditor | null, MonacoDiffEditor | null>(ref, () => editor, [\n editor,\n ]);\n\n // Mount once.\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const instance = monaco.editor.createDiffEditor(container, {\n ...BASE_OPTIONS,\n readOnly,\n renderSideBySide,\n ...options,\n });\n const originalModel = monaco.editor.createModel(original, language);\n const modifiedModel = monaco.editor.createModel(modified, language);\n instance.setModel({ original: originalModel, modified: modifiedModel });\n // Theme is applied by the effect below once `setEditor` runs.\n setEditor(instance);\n onMountRef.current?.(instance, monaco);\n\n return () => {\n instance.dispose();\n originalModel.dispose();\n modifiedModel.dispose();\n setEditor(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const original_ = editor?.getModel()?.original;\n if (original_ && original_.getValue() !== original) original_.setValue(original);\n }, [editor, original]);\n\n useEffect(() => {\n const modified_ = editor?.getModel()?.modified;\n if (modified_ && modified_.getValue() !== modified) modified_.setValue(modified);\n }, [editor, modified]);\n\n useEffect(() => {\n const models = editor?.getModel();\n if (!models) return;\n monaco.editor.setModelLanguage(models.original, language);\n monaco.editor.setModelLanguage(models.modified, language);\n }, [editor, language]);\n\n useEffect(() => {\n editor?.updateOptions({ readOnly, renderSideBySide });\n }, [editor, readOnly, renderSideBySide]);\n\n useEffect(() => {\n if (!editor) return;\n try {\n applyBrandTheme(monaco, theme);\n } catch (err) {\n console.error(\"[@elabs-ai/components-editor] failed to apply brand theme\", err);\n }\n // `revision` re-applies once the data-theme attribute settles. See CodeEditor.\n }, [editor, theme, revision]);\n\n const resolvedStyle: CSSProperties = {\n height: typeof height === \"number\" ? `${height}px` : height,\n ...style,\n };\n\n return (\n <div\n ref={containerRef}\n data-testid=\"diff-editor\"\n className={cn(\"h-full w-full overflow-hidden bg-background text-foreground\", className)}\n style={resolvedStyle}\n {...props}\n />\n );\n});\n","\"use client\";\n\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n} from \"react\";\nimport { CodeEditor, type MonacoCodeEditor } from \"../code-editor\";\nimport { CopyButton } from \"../copy-button\";\nimport {\n monacoContentAccess,\n type EditorContentAccess,\n type EditorSelection,\n} from \"../lib/editor-content-access\";\n\nexport interface EditorFile {\n /** Unique path/name; shown (basename) on the tab and used as the model path. */\n path: string;\n /** Monaco language id. Inferred from the extension when omitted. */\n language?: string;\n /** File content. */\n value: string;\n}\n\nexport interface CodeWorkspaceProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Open files, one tab each. */\n files: EditorFile[];\n /** Controlled active file path. */\n activePath?: string;\n /** Initial active path for uncontrolled use. Defaults to the first file. */\n defaultActivePath?: string;\n /** Called when the active tab changes. */\n onActivePathChange?: (path: string) => void;\n /** Called on every edit with the file path + new content. */\n onFileChange?: (path: string, value: string) => void;\n /** Render all files read-only. */\n readOnly?: boolean;\n /** Workspace height. Number → px. Defaults to \"100%\". */\n height?: number | string;\n}\n\nconst EXT_LANGUAGE: Record<string, string> = {\n ts: \"typescript\",\n tsx: \"typescript\",\n mts: \"typescript\",\n cts: \"typescript\",\n js: \"javascript\",\n jsx: \"javascript\",\n mjs: \"javascript\",\n cjs: \"javascript\",\n json: \"json\",\n css: \"css\",\n scss: \"scss\",\n less: \"less\",\n html: \"html\",\n md: \"markdown\",\n py: \"python\",\n sql: \"sql\",\n yml: \"yaml\",\n yaml: \"yaml\",\n xml: \"xml\",\n sh: \"shell\",\n};\n\nconst basename = (path: string) => path.split(\"/\").pop() || path;\n\n/**\n * Radix composes each `TabsContent`'s DOM id from the `Tabs`/`TabsTrigger`\n * `value` (see #154): a raw `file.path` leaks `/`, `.`, spaces and non-ASCII\n * characters straight into that id, which is what a screen reader gets back\n * via `aria-controls`. `path` stays the display label and the lookup key in\n * component state (`active`); this is only ever a DOM-id-safe Tabs value.\n *\n * The encoding is INJECTIVE and depends only on the path — never on the\n * file's position in `files`. Every character outside `[A-Za-z0-9-]` becomes\n * `_<hex code point>_`, and `_` itself is never emitted literally, so the\n * mapping is reversible and two distinct paths can never produce the same\n * value (\"a/b\" → `tab-a_2f_b`, \"a.b\" → `tab-a_2e_b`). An index-derived value\n * would have been collision-safe too, but it changes when the list is\n * reordered or a file is inserted, which re-keys the active `TabsContent` and\n * throws away Monaco's selection, scroll position and undo history on an\n * otherwise harmless `files` update.\n */\nfunction tabIdForFile(path: string): string {\n let out = \"\";\n for (const char of path) {\n out += /[A-Za-z0-9-]/.test(char) ? char : `_${char.codePointAt(0)!.toString(16)}_`;\n }\n return `tab-${out}`;\n}\n\nfunction inferLanguage(file: EditorFile): string {\n if (file.language) return file.language;\n const ext = file.path.split(\".\").pop()?.toLowerCase() ?? \"\";\n return EXT_LANGUAGE[ext] ?? \"plaintext\";\n}\n\n/**\n * Imperative handle exposed via `CodeWorkspace`'s `ref`.\n *\n * **Migration note:** the forwarded `ref` type changed from `HTMLDivElement` to\n * `CodeWorkspaceHandle` (mirrors the #273 `MarkdownWorkspaceHandle` precedent).\n * Replace any `ref.current` / `ref.current.scrollIntoView()` DOM access with\n * `ref.current?.getElement()`. This is the only breaking change in this work.\n */\nexport interface CodeWorkspaceHandle extends EditorContentAccess {\n /**\n * The live Monaco editor for the currently active tab, or `null` while booting\n * or when no files are open. Consumers needing the full Monaco API can use this\n * and pass it to `monacoContentAccess(handle.getActiveEditor()!)`.\n */\n getActiveEditor(): MonacoCodeEditor | null;\n /**\n * The workspace root DOM element. Preserves the old `HTMLDivElement` ref access\n * that existed before the ref-type change (#273 `MarkdownWorkspace` playbook).\n */\n getElement(): HTMLDivElement | null;\n}\n\n/** No-op EditorContentAccess returned when no Monaco editor is mounted. */\nconst NULL_ACCESS: EditorContentAccess = {\n getText: () => \"\",\n getSelection: () => ({ text: \"\", empty: true }),\n replaceSelection: () => undefined,\n insertAtCursor: () => undefined,\n focus: () => undefined,\n onSelectionChange: () => () => undefined,\n};\n\n/**\n * Multi-file editor shell: brand-ui `Tabs` for the file bar above a single\n * {@link CodeEditor} that swaps to the active file. This is the \"reuse our\n * components for the chrome\" surface — Tabs/Button are ours; the editing surface\n * is Monaco. Edits bubble through `onFileChange`.\n *\n * The forwarded `ref` exposes a {@link CodeWorkspaceHandle} (not `HTMLDivElement`).\n * Use `ref.current?.getElement()` to access the root DOM node.\n */\nexport const CodeWorkspace = forwardRef<CodeWorkspaceHandle, CodeWorkspaceProps>(\n function CodeWorkspace(\n {\n files,\n activePath,\n defaultActivePath,\n onActivePathChange,\n onFileChange,\n readOnly = false,\n height = \"100%\",\n className,\n style,\n ...props\n },\n ref,\n ) {\n const [internalActive, setInternalActive] = useState(defaultActivePath ?? files[0]?.path ?? \"\");\n const isControlled = activePath !== undefined;\n const requested = isControlled ? activePath : internalActive;\n const activeFile = files.find((f) => f.path === requested) ?? files[0];\n const active = activeFile?.path ?? \"\";\n\n // DOM-id-safe Tabs value per file (#154); `path` stays the lookup key.\n const tabs = files.map((file) => ({ file, id: tabIdForFile(file.path) }));\n const activeTabId = tabs.find((t) => t.file.path === active)?.id ?? \"\";\n\n // The root div ref (getElement() preserves old HTMLDivElement access).\n const rootRef = useRef<HTMLDivElement | null>(null);\n\n // The live Monaco editor for the active tab. Nulled when there is no active file.\n const [activeEditor, setActiveEditor] = useState<MonacoCodeEditor | null>(null);\n\n // Defensive: if the active file is removed (no `activeFile`), null the held instance\n // so getActiveEditor() and content-access methods don't act on a disposed editor.\n useEffect(() => {\n if (!activeFile) setActiveEditor(null);\n }, [activeFile]);\n\n // A STABLE listener set for onSelectionChange. The handle adds/removes here;\n // the binding effect below forwards the ACTIVE editor's selection changes into\n // it. This is what makes a subscribe-in-mount-effect survive the editor's async\n // mount (and tab swaps): the consumer subscribes once; binding re-attaches as\n // `activeEditor` changes. (Delegating onSelectionChange to a call-time snapshot\n // would bind to the not-yet-mounted editor and silently never fire.)\n const [selectionListeners] = useState(() => new Set<(sel: EditorSelection) => void>());\n useEffect(() => {\n if (!activeEditor) return;\n const unsub = monacoContentAccess(activeEditor).onSelectionChange((sel) =>\n selectionListeners.forEach((l) => l(sel)),\n );\n return unsub;\n }, [activeEditor, selectionListeners]);\n\n const selectTab = useCallback(\n (path: string) => {\n if (!isControlled) setInternalActive(path);\n onActivePathChange?.(path);\n },\n [isControlled, onActivePathChange],\n );\n\n useImperativeHandle(ref, () => {\n const access = activeEditor ? monacoContentAccess(activeEditor) : NULL_ACCESS;\n return {\n getActiveEditor: () => activeEditor,\n getElement: () => rootRef.current,\n getText: () => access.getText(),\n getSelection: () => access.getSelection(),\n replaceSelection: (text: string) => access.replaceSelection(text),\n insertAtCursor: (text: string) => access.insertAtCursor(text),\n focus: () => access.focus(),\n // Stable set (not the call-time `access`) so a subscription survives the\n // editor's async mount + tab swaps; the binding effect forwards events.\n onSelectionChange: (listener) => {\n selectionListeners.add(listener);\n return () => selectionListeners.delete(listener);\n },\n };\n }, [activeEditor, selectionListeners]);\n\n const resolvedStyle: CSSProperties = {\n height: typeof height === \"number\" ? `${height}px` : height,\n ...style,\n };\n\n return (\n <div\n ref={rootRef}\n className={cn(\n \"flex flex-col overflow-hidden rounded-lg border border-border bg-surface\",\n className,\n )}\n style={resolvedStyle}\n {...props}\n >\n <Tabs\n value={activeTabId}\n onValueChange={(id) => {\n const found = tabs.find((t) => t.id === id);\n if (found) selectTab(found.file.path);\n }}\n className=\"flex min-h-0 flex-1 flex-col gap-0\"\n >\n <div className=\"flex items-center border-b border-border bg-surface\">\n <TabsList className=\"h-9 flex-1 justify-start gap-0 rounded-none bg-transparent p-0\">\n {tabs.map(({ file, id }) => (\n <TabsTrigger\n key={id}\n value={id}\n className=\"h-9 rounded-none border-e border-border px-3 text-xs data-[state=active]:bg-background data-[state=active]:shadow-none\"\n >\n {basename(file.path)}\n </TabsTrigger>\n ))}\n </TabsList>\n {activeFile ? <CopyButton value={activeFile.value} className=\"me-1.5\" /> : null}\n </div>\n\n {tabs.map(({ file, id }) => {\n const isActive = file.path === activeFile?.path;\n return (\n // `forceMount` keeps every tab's content in the DOM so each\n // trigger's `aria-controls` resolves to a real element, not only\n // the active one (#154). Radix derives its own `hidden` from\n // `forceMount || isSelected`, so under `forceMount` it hides\n // NOTHING: without the explicit `hidden` below every inactive\n // panel stays a visible `flex-1` child and the editor height is\n // split across all of them. `tabIndex={-1}` keeps the empty\n // panels out of the tab sequence for the same reason. Only the\n // active file mounts a (single) Monaco instance.\n <TabsContent\n key={id}\n value={id}\n forceMount\n hidden={!isActive}\n tabIndex={isActive ? 0 : -1}\n className=\"mt-0 min-h-0 flex-1\"\n >\n {isActive ? (\n <CodeEditor\n key={file.path}\n path={file.path}\n language={inferLanguage(file)}\n value={file.value}\n readOnly={readOnly}\n onChange={(next) => onFileChange?.(file.path, next)}\n onMount={(editor) => setActiveEditor(editor)}\n height=\"100%\"\n />\n ) : null}\n </TabsContent>\n );\n })}\n </Tabs>\n </div>\n );\n },\n);\n","\"use client\";\n\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport { CopyButton } from \"../copy-button\";\nimport { EDITOR_LANGUAGES, type EditorLanguage } from \"../lib/languages\";\n\nexport interface EditorToolbarProps extends HTMLAttributes<HTMLDivElement> {\n /** Filename shown on the left (e.g. \"index.ts\"). */\n filename?: ReactNode;\n /** Active language id; shows a language picker when `onLanguageChange` is set. */\n language?: string;\n /** Called when the user picks a language. Omit to hide the picker. */\n onLanguageChange?: (language: string) => void;\n /** Languages offered in the picker. Defaults to {@link EDITOR_LANGUAGES}. */\n languages?: EditorLanguage[];\n /** Text copied by the copy button. Omit to hide the button. */\n copyValue?: string;\n /** Extra actions rendered before the copy button (brand-ui buttons, etc.). */\n actions?: ReactNode;\n}\n\n/**\n * Brand-ui chrome for the editors — the part that genuinely reuses our\n * components. Renders a filename label, an optional language `Select` and a copy\n * `Button`. The editing surface itself is Monaco; this is the frame around it.\n */\nexport const EditorToolbar = forwardRef<HTMLDivElement, EditorToolbarProps>(function EditorToolbar(\n {\n filename,\n language,\n onLanguageChange,\n languages = EDITOR_LANGUAGES,\n copyValue,\n actions,\n className,\n ...props\n },\n ref,\n) {\n return (\n <div\n ref={ref}\n className={cn(\n \"flex h-10 shrink-0 items-center gap-2 border-b border-border bg-surface px-3\",\n className,\n )}\n {...props}\n >\n {filename != null ? (\n <span className=\"truncate text-sm font-medium text-foreground\">{filename}</span>\n ) : null}\n\n <div className=\"ml-auto flex items-center gap-1.5\">\n {actions}\n\n {onLanguageChange ? (\n <Select value={language} onValueChange={onLanguageChange}>\n <SelectTrigger size=\"sm\" className=\"h-7 w-[140px]\" aria-label=\"Language\">\n <SelectValue placeholder=\"Language\" />\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l.id} value={l.id}>\n {l.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n ) : null}\n\n {copyValue != null ? <CopyButton value={copyValue} /> : null}\n </div>\n </div>\n );\n});\n","/**\n * Curated language list for the editor toolbar's language picker. Ids are\n * Monaco's built-in language ids (`monaco.languages.getLanguages()`), so they\n * map straight onto `setModelLanguage` / model creation.\n */\nexport interface EditorLanguage {\n /** Monaco language id (e.g. \"typescript\"). */\n id: string;\n /** Human-readable label for the picker. */\n label: string;\n}\n\nexport const EDITOR_LANGUAGES: EditorLanguage[] = [\n { id: \"typescript\", label: \"TypeScript\" },\n { id: \"javascript\", label: \"JavaScript\" },\n { id: \"json\", label: \"JSON\" },\n { id: \"html\", label: \"HTML\" },\n { id: \"css\", label: \"CSS\" },\n { id: \"scss\", label: \"SCSS\" },\n { id: \"markdown\", label: \"Markdown\" },\n { id: \"python\", label: \"Python\" },\n { id: \"sql\", label: \"SQL\" },\n { id: \"yaml\", label: \"YAML\" },\n { id: \"xml\", label: \"XML\" },\n { id: \"shell\", label: \"Shell\" },\n { id: \"plaintext\", label: \"Plain text\" },\n];\n\n/** Look up a language's label, falling back to the raw id. */\nexport function languageLabel(id: string): string {\n return EDITOR_LANGUAGES.find((l) => l.id === id)?.label ?? id;\n}\n","/**\n * @elabs-ai/components-editor — a token-themed Monaco (VS Code) editor wrapped as brand-ui\n * components.\n *\n * Monaco is a self-rendering editor engine; this package wraps it the way\n * @elabs-ai/components-flow wraps React Flow and @elabs-ai/components-data wraps TanStack. The editing\n * surface + widgets are Monaco's, recolored from the active brand theme via the\n * theming bridge; the chrome (toolbar, file tabs, copy button) is brand-ui.\n *\n * Worker setup (for completions/diagnostics) — import ONCE at the app entry:\n * import \"@elabs-ai/components-editor/monaco-environment\"; // Vite apps\n */\nexport {\n CodeEditor,\n type CodeEditorProps,\n type MonacoCodeEditor,\n type EditorAction,\n} from \"./code-editor\";\nexport { DiffEditor, type DiffEditorProps, type MonacoDiffEditor } from \"./diff-editor\";\nexport {\n MarkdownEditor,\n type MarkdownEditorProps,\n type MarkdownEditorHandle,\n type EmbedAssetFn,\n} from \"./markdown-editor\";\nexport {\n CodeWorkspace,\n type CodeWorkspaceHandle,\n type CodeWorkspaceProps,\n type EditorFile,\n} from \"./code-workspace\";\nexport {\n monacoContentAccess,\n type EditorContentAccess,\n type EditorSelection,\n} from \"./lib/editor-content-access\";\nexport { EditorToolbar, type EditorToolbarProps } from \"./editor-toolbar\";\nexport { EditorContextMenu, type EditorContextMenuProps } from \"./editor-context-menu\";\nexport { CopyButton, type CopyButtonProps } from \"./copy-button\";\n\n// Theming bridge + language helpers (advanced consumers / custom chrome).\nexport { applyBrandTheme, buildBrandThemeData, brandThemeId } from \"./lib/monaco-theme-bridge\";\nexport { useDataTheme, type DataThemeState } from \"./lib/use-data-theme\";\nexport { EDITOR_LANGUAGES, languageLabel, type EditorLanguage } from \"./lib/languages\";\n\n// Re-export the monaco namespace so consumers can build custom editors / wire\n// commands without adding a direct dependency.\nexport * as monaco from \"monaco-editor\";\n"],"mappings":"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","names":["DiffEditor","editor","cn","forwardRef","useEffect","useImperativeHandle","useRef","useState","jsx","forwardRef","CodeWorkspace","useState","useRef","useEffect","useImperativeHandle","cn","editor","cn","forwardRef","jsx","jsxs","forwardRef","EditorToolbar","cn","monaco"]}
@@ -11,8 +11,10 @@
11
11
  max-width: var(--md-measure, 48rem);
12
12
  margin-inline: auto;
13
13
  }
14
- .milkdown-host .ProseMirror:focus {
15
- outline: none;
14
+ .milkdown-host .ProseMirror:focus-visible {
15
+ outline: 1px solid var(--ring-contour);
16
+ outline-offset: -3px;
17
+ box-shadow: inset 0 0 0 2px var(--ring);
16
18
  }
17
19
  .milkdown-host {
18
20
  display: flex;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/markdown-editor/markdown-editor.css","../../src/calc-block/calc-editor.css"],"sourcesContent":["/**\n * Token-driven theme for the Milkdown (ProseMirror) WYSIWYG surface.\n *\n * Milkdown core ships ZERO styles (headless), so this is where brand-ui owns the\n * look. Every value is a semantic design token (var(--foreground), --border,\n * --radius, …) — NO raw colors — so the editor matches all three themes\n * (light/dark) for free. Every rule\n * is scoped under `.milkdown-host` so nothing leaks globally.\n */\n\n.milkdown-host .ProseMirror {\n outline: none;\n padding: 1rem 1.1rem;\n min-height: 9rem;\n color: var(--foreground);\n font-size: 0.875rem;\n line-height: 1.65;\n caret-color: var(--primary);\n word-break: break-word;\n /* Same reading measure as the preview (max-w-3xl). Driven by the shared scale\n (--md-measure, set via markdownScaleVars) so editor + preview match (#18). */\n max-width: var(--md-measure, 48rem);\n margin-inline: auto;\n}\n\n.milkdown-host .ProseMirror:focus {\n outline: none;\n}\n\n/* ----------------------------------------------------------------------------\n * Fill height — the editable grows to fill a definite-height parent.\n *\n * Milkdown's host box is otherwise content-sized (the `.ProseMirror` floor below\n * is `min-height: 9rem`), so inside a tall pane a short document leaves a large\n * dead area below the editable AND the caret-anchored `/` slash menu clips inside\n * the short box near the bottom edge. Making `.milkdown-host` a column flex that\n * fills its parent — and letting the intermediate Milkdown wrapper + the\n * ProseMirror surface grow inside it — means the editable fills the pane\n * (clickable everywhere) and the slash menu has room to open / flip.\n *\n * `min-height: 100%` only resolves against a parent with a DEFINITE height (the\n * workspace's `h-full` wysiwyg pane, or any sized container). In an\n * indefinite-height parent it resolves to `auto`, so the standalone / short\n * editor still sizes to its content above the 9rem floor — no regression for the\n * autodocs / inline cases. (elabs-components: editor-fill-height)\n * ------------------------------------------------------------------------- */\n.milkdown-host {\n display: flex;\n flex-direction: column;\n min-height: 100%;\n}\n.milkdown-host > div,\n.milkdown-host .milkdown {\n display: flex;\n min-height: 0;\n flex: 1 1 auto;\n flex-direction: column;\n}\n.milkdown-host .ProseMirror.editor {\n flex: 1 1 auto;\n}\n\n.milkdown-host .ProseMirror > * + * {\n margin-top: 0.7em;\n}\n\n/* Headings — sizes/weight/tracking come from the SHARED markdown scale\n (markdown-scale.ts → --md-* vars on .milkdown-host), so the editor matches the\n prose `Heading` exactly and can't drift on the next edit to either side (#18).\n Fallbacks equal the canonical scale for use without the vars (e.g. clipboard). */\n.milkdown-host .ProseMirror h1,\n.milkdown-host .ProseMirror h2,\n.milkdown-host .ProseMirror h3,\n.milkdown-host .ProseMirror h4,\n.milkdown-host .ProseMirror h5,\n.milkdown-host .ProseMirror h6 {\n font-weight: var(--md-heading-weight, 600);\n letter-spacing: var(--md-heading-tracking, -0.025em);\n line-height: 1.25;\n color: var(--foreground);\n margin-top: 1.1em;\n}\n.milkdown-host .ProseMirror h1 {\n font-size: var(--md-h1, 1.5rem);\n}\n.milkdown-host .ProseMirror h2 {\n font-size: var(--md-h2, 1.25rem);\n}\n.milkdown-host .ProseMirror h3 {\n font-size: var(--md-h3, 1.125rem);\n}\n.milkdown-host .ProseMirror h4 {\n font-size: var(--md-h4, 1rem);\n}\n.milkdown-host .ProseMirror h5 {\n font-size: var(--md-h5, 0.875rem);\n}\n.milkdown-host .ProseMirror h6 {\n font-size: var(--md-h6, 0.875rem);\n color: var(--muted-foreground);\n}\n\n/* Inline */\n.milkdown-host .ProseMirror a {\n color: var(--primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n.milkdown-host .ProseMirror strong {\n font-weight: 650;\n}\n.milkdown-host .ProseMirror code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.85em;\n background: var(--muted);\n color: var(--foreground);\n padding: 0.12em 0.36em;\n border-radius: calc(var(--radius) - 4px);\n}\n\n/* Blocks */\n.milkdown-host .ProseMirror blockquote {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n color: var(--muted-foreground);\n}\n.milkdown-host .ProseMirror pre {\n background: var(--surface-muted);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.85rem 1rem;\n overflow: auto;\n}\n.milkdown-host .ProseMirror pre code {\n background: transparent;\n padding: 0;\n font-size: 0.82rem;\n}\n.milkdown-host .ProseMirror hr {\n border: none;\n border-top: 1px solid var(--border);\n margin: 1.25em 0;\n}\n\n/* Lists */\n.milkdown-host .ProseMirror ul,\n.milkdown-host .ProseMirror ol {\n padding-left: 1.4rem;\n}\n.milkdown-host .ProseMirror ul {\n list-style: disc;\n}\n.milkdown-host .ProseMirror ol {\n list-style: decimal;\n}\n.milkdown-host .ProseMirror li > * + * {\n margin-top: 0.35em;\n}\n\n/* Tables (GFM) */\n.milkdown-host .ProseMirror table {\n border-collapse: collapse;\n width: 100%;\n font-size: 0.85rem;\n}\n.milkdown-host .ProseMirror th,\n.milkdown-host .ProseMirror td {\n border: 1px solid var(--border);\n padding: 0.4rem 0.6rem;\n text-align: left;\n}\n.milkdown-host .ProseMirror th {\n background: var(--surface-muted);\n font-weight: 600;\n}\n\n/* Task lists (GFM) */\n.milkdown-host .ProseMirror ul[data-item-type=\"task\"] {\n list-style: none;\n padding-left: 0.25rem;\n}\n\n/* Read-only mode dims the caret affordance */\n.milkdown-host .ProseMirror[contenteditable=\"false\"] {\n caret-color: transparent;\n}\n\n/* ---- Brand `:::` directives ----------------------------------------------------\n * These now render as LIVE @brand React components via @prosemirror-adapter/react\n * (see directive-views.tsx), so the box-model chrome (card/callout/metric borders,\n * fills, type accents) comes from the components themselves — NOT from here. This\n * section only owns what the editor adds on top: outer spacing, the editable-body\n * rhythm, the timeline frame (an editable list, not a derived <Timeline>), and the\n * inline-edit affordance for editable attributes (card/callout title, metric\n * label/value). The `toDOM` defs in directive-nodes.ts keep their own marker classes\n * for clipboard serialization but are not visually rendered while node-views are on.\n */\n.milkdown-host .brand-directive {\n margin: 0.85em 0;\n}\n.milkdown-host .brand-directive__body > * + * {\n margin-top: 0.6em;\n}\n/* The first/last editable block inside a card/callout shouldn't add stray margin\n * on top of the component's own padding. */\n.milkdown-host .brand-directive__body > :first-child {\n margin-top: 0;\n}\n\n/* timeline → keep the list editable inside a subtle frame */\n.milkdown-host .brand-directive--timeline {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n}\n\n/* Inline-editable directive attribute (seamless, caret-friendly). */\n.milkdown-host .brand-inline-edit {\n cursor: text;\n min-width: 1ch;\n /* inherit the surrounding component's type (CardTitle, MetricBlock label/value) */\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n}\n.milkdown-host .brand-inline-edit:empty::before {\n content: attr(data-placeholder);\n color: var(--muted-foreground);\n opacity: 0.65;\n pointer-events: none;\n}\n\n/* ------------------------------------------------------------------------- */\n/* Focus writing (Ulysses Phase A): under a `[data-focus-writing]` ancestor */\n/* (the workspace's wysiwyg pane), every top-level block dims except the one */\n/* owning the selection (`.wb-fw-active`, set by the workspace) — paragraph */\n/* focus. Hover un-dims for mouse repositioning. Token-driven, motion-safe. */\n/* ------------------------------------------------------------------------- */\n[data-focus-writing] .milkdown-host .ProseMirror > * {\n opacity: 0.35;\n transition: opacity var(--t-base, 200ms) var(--ease-standard, ease);\n}\n[data-focus-writing] .milkdown-host .ProseMirror > .wb-fw-active,\n[data-focus-writing] .milkdown-host .ProseMirror > *:hover {\n opacity: 1;\n}\n@media (prefers-reduced-motion: reduce) {\n [data-focus-writing] .milkdown-host .ProseMirror > * {\n transition: none;\n }\n}\n","/**\n * Calc authoring decorations (#220) — shared by the Monaco source surface\n * (decoration `inlineClassName`) and the Milkdown WYSIWYG surface (ProseMirror\n * inline decorations + result-inlay widgets).\n *\n * Colors come from the `--calc-*` syntax tokens — the SAME tokens `CalcBlock`\n * paints with (#221), already AA-verified against `--background` (the editor\n * surface) and `--card` in all three themes — so highlighting tracks the theme via\n * the cascade with no hardcoded hex. The compound `.brand-calc-tok.brand-calc-tok--x`\n * selector raises specificity above Monaco's own token color class (`.mtkN`) so\n * the calc color wins regardless of stylesheet load order (no `!important`).\n *\n * Hue is never the SOLE cue (var-def = weight, unresolved = dotted underline), so\n * roles stay distinct in the high-contrast theme where the calc colors collapse\n * toward the foreground — mirroring `CalcBlock.tokenClass`.\n */\n\n.brand-calc-tok.brand-calc-tok--number,\n.brand-calc-tok.brand-calc-tok--constant {\n color: var(--calc-number);\n}\n.brand-calc-tok.brand-calc-tok--unit {\n color: var(--calc-unit);\n}\n.brand-calc-tok.brand-calc-tok--currency {\n color: var(--calc-currency);\n}\n.brand-calc-tok.brand-calc-tok--operator {\n color: var(--calc-operator);\n}\n.brand-calc-tok.brand-calc-tok--function {\n color: var(--calc-function);\n}\n.brand-calc-tok.brand-calc-tok--var-ref {\n color: var(--calc-variable);\n}\n.brand-calc-tok.brand-calc-tok--var-def {\n color: var(--calc-variable);\n font-weight: 500;\n}\n.brand-calc-tok.brand-calc-tok--line-ref {\n color: var(--calc-reference);\n font-variant-numeric: tabular-nums;\n}\n.brand-calc-tok.brand-calc-tok--comment {\n color: var(--calc-comment);\n font-style: italic;\n}\n.brand-calc-tok.brand-calc-tok--unknown {\n color: var(--calc-warning);\n}\n\n/* Non-hue cue for unresolved identifiers / dangling refs — carries the role in\n high-contrast, where hue is flattened. */\n.brand-calc-tok.brand-calc-tok--unresolved {\n text-decoration: underline dotted;\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n}\n\n/**\n * WYSIWYG result inlay — a ProseMirror widget after each calc line showing the\n * computed answer. The number IS the signal (shown as text), color is only an\n * enhancement, and it is non-editable / non-selectable so it never enters the\n * document. (The Monaco surface uses Monaco's own inlay hints, themed via the\n * theme bridge's `editorInlayHint.*` colors — not this class.)\n */\n.brand-calc-inlay {\n /* Full strength (no opacity de-emphasis): the result is the answer, and the\n `--calc-result` token is AA-verified at FULL opacity (#221) — reducing it\n would drop effective contrast below what the contrast gate models. */\n margin-inline-start: 1ch;\n color: var(--calc-result);\n font-variant-numeric: tabular-nums;\n user-select: none;\n white-space: pre;\n}\n.brand-calc-inlay--error {\n color: var(--calc-warning);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/markdown-editor/markdown-editor.css","../../src/calc-block/calc-editor.css"],"sourcesContent":["/**\n * Token-driven theme for the Milkdown (ProseMirror) WYSIWYG surface.\n *\n * Milkdown core ships ZERO styles (headless), so this is where brand-ui owns the\n * look. Every value is a semantic design token (var(--foreground), --border,\n * --radius, …) — NO raw colors — so the editor matches every theme\n * (light/dark) for free. Every rule\n * is scoped under `.milkdown-host` so nothing leaks globally.\n */\n\n.milkdown-host .ProseMirror {\n /* Suppresses the platform default outline — NOT a bare removal: the\n `:focus-visible` rule below is the replacement, drawn on this same\n element (#309). It owns the indicator unconditionally, so it can't be\n clipped by an ancestor and can't be deleted by a consumer `className`\n reaching `cn()` on the wrapper (`markdown-editor.tsx`'s root `div`\n intentionally carries no focus-ring utility of its own — see #67/#309). */\n outline: none;\n padding: 1rem 1.1rem;\n min-height: 9rem;\n color: var(--foreground);\n font-size: 0.875rem;\n line-height: 1.65;\n caret-color: var(--primary);\n word-break: break-word;\n /* Same reading measure as the preview (max-w-3xl). Driven by the shared scale\n (--md-measure, set via markdownScaleVars) so editor + preview match (#18). */\n max-width: var(--md-measure, 48rem);\n margin-inline: auto;\n}\n\n/* Focus indicator (#309) — a compound two-layer shape mirroring the\n `focus-ring-inset` Tailwind utility (`packages/tokens/src/themes.css`,\n ADR 0027), hand-written because this stylesheet is a plain side-effect CSS\n import resolved by the CONSUMER's bundler — not guaranteed to run through\n Tailwind, so `@apply`/utility classes aren't reachable here (verified:\n `@apply` appears zero times in any .css file in this repo). Drawn INSIDE\n the editable's own box (`outline-offset: -3px`, an inset `box-shadow`), so\n it can't be clipped by an ancestor and survives a consumer `className`\n deleting the wrapper's classes (`MarkdownWorkspace` already does this via\n `className=\"border-0\"`). Two layers of opposite value so at least one edge\n clears 3:1 against the ground whatever `--ring` resolves to in a given\n theme (`--ring-contour` is the dedicated contour token — `themes-contrast.\n test.ts`'s `INDICATOR_SURFACES` locks `max(ring, contour) >= 3:1` at the\n token level; `markdown-editor.stories.tsx`'s `FocusIndicator` play\n re-measures it on this rendered surface, both themes). */\n.milkdown-host .ProseMirror:focus-visible {\n outline: 1px solid var(--ring-contour);\n outline-offset: -3px;\n box-shadow: inset 0 0 0 2px var(--ring);\n}\n\n/* ----------------------------------------------------------------------------\n * Fill height — the editable grows to fill a definite-height parent.\n *\n * Milkdown's host box is otherwise content-sized (the `.ProseMirror` floor below\n * is `min-height: 9rem`), so inside a tall pane a short document leaves a large\n * dead area below the editable AND the caret-anchored `/` slash menu clips inside\n * the short box near the bottom edge. Making `.milkdown-host` a column flex that\n * fills its parent — and letting the intermediate Milkdown wrapper + the\n * ProseMirror surface grow inside it — means the editable fills the pane\n * (clickable everywhere) and the slash menu has room to open / flip.\n *\n * `min-height: 100%` only resolves against a parent with a DEFINITE height (the\n * workspace's `h-full` wysiwyg pane, or any sized container). In an\n * indefinite-height parent it resolves to `auto`, so the standalone / short\n * editor still sizes to its content above the 9rem floor — no regression for the\n * autodocs / inline cases. (elabs-components: editor-fill-height)\n * ------------------------------------------------------------------------- */\n.milkdown-host {\n display: flex;\n flex-direction: column;\n min-height: 100%;\n}\n.milkdown-host > div,\n.milkdown-host .milkdown {\n display: flex;\n min-height: 0;\n flex: 1 1 auto;\n flex-direction: column;\n}\n.milkdown-host .ProseMirror.editor {\n flex: 1 1 auto;\n}\n\n.milkdown-host .ProseMirror > * + * {\n margin-top: 0.7em;\n}\n\n/* Headings — sizes/weight/tracking come from the SHARED markdown scale\n (markdown-scale.ts → --md-* vars on .milkdown-host), so the editor matches the\n prose `Heading` exactly and can't drift on the next edit to either side (#18).\n Fallbacks equal the canonical scale for use without the vars (e.g. clipboard). */\n.milkdown-host .ProseMirror h1,\n.milkdown-host .ProseMirror h2,\n.milkdown-host .ProseMirror h3,\n.milkdown-host .ProseMirror h4,\n.milkdown-host .ProseMirror h5,\n.milkdown-host .ProseMirror h6 {\n font-weight: var(--md-heading-weight, 600);\n letter-spacing: var(--md-heading-tracking, -0.025em);\n line-height: 1.25;\n color: var(--foreground);\n margin-top: 1.1em;\n}\n.milkdown-host .ProseMirror h1 {\n font-size: var(--md-h1, 1.5rem);\n}\n.milkdown-host .ProseMirror h2 {\n font-size: var(--md-h2, 1.25rem);\n}\n.milkdown-host .ProseMirror h3 {\n font-size: var(--md-h3, 1.125rem);\n}\n.milkdown-host .ProseMirror h4 {\n font-size: var(--md-h4, 1rem);\n}\n.milkdown-host .ProseMirror h5 {\n font-size: var(--md-h5, 0.875rem);\n}\n.milkdown-host .ProseMirror h6 {\n font-size: var(--md-h6, 0.875rem);\n color: var(--muted-foreground);\n}\n\n/* Inline */\n.milkdown-host .ProseMirror a {\n color: var(--primary);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n.milkdown-host .ProseMirror strong {\n font-weight: 650;\n}\n.milkdown-host .ProseMirror code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.85em;\n background: var(--muted);\n color: var(--foreground);\n padding: 0.12em 0.36em;\n border-radius: calc(var(--radius) - 4px);\n}\n\n/* Blocks */\n.milkdown-host .ProseMirror blockquote {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n color: var(--muted-foreground);\n}\n.milkdown-host .ProseMirror pre {\n background: var(--surface-muted);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 0.85rem 1rem;\n overflow: auto;\n}\n.milkdown-host .ProseMirror pre code {\n background: transparent;\n padding: 0;\n font-size: 0.82rem;\n}\n.milkdown-host .ProseMirror hr {\n border: none;\n border-top: 1px solid var(--border);\n margin: 1.25em 0;\n}\n\n/* Lists */\n.milkdown-host .ProseMirror ul,\n.milkdown-host .ProseMirror ol {\n padding-left: 1.4rem;\n}\n.milkdown-host .ProseMirror ul {\n list-style: disc;\n}\n.milkdown-host .ProseMirror ol {\n list-style: decimal;\n}\n.milkdown-host .ProseMirror li > * + * {\n margin-top: 0.35em;\n}\n\n/* Tables (GFM) */\n.milkdown-host .ProseMirror table {\n border-collapse: collapse;\n width: 100%;\n font-size: 0.85rem;\n}\n.milkdown-host .ProseMirror th,\n.milkdown-host .ProseMirror td {\n border: 1px solid var(--border);\n padding: 0.4rem 0.6rem;\n text-align: left;\n}\n.milkdown-host .ProseMirror th {\n background: var(--surface-muted);\n font-weight: 600;\n}\n\n/* Task lists (GFM) */\n.milkdown-host .ProseMirror ul[data-item-type=\"task\"] {\n list-style: none;\n padding-left: 0.25rem;\n}\n\n/* Read-only mode dims the caret affordance */\n.milkdown-host .ProseMirror[contenteditable=\"false\"] {\n caret-color: transparent;\n}\n\n/* ---- Brand `:::` directives ----------------------------------------------------\n * These now render as LIVE @brand React components via @prosemirror-adapter/react\n * (see directive-views.tsx), so the box-model chrome (card/callout/metric borders,\n * fills, type accents) comes from the components themselves — NOT from here. This\n * section only owns what the editor adds on top: outer spacing, the editable-body\n * rhythm, the timeline frame (an editable list, not a derived <Timeline>), and the\n * inline-edit affordance for editable attributes (card/callout title, metric\n * label/value). The `toDOM` defs in directive-nodes.ts keep their own marker classes\n * for clipboard serialization but are not visually rendered while node-views are on.\n */\n.milkdown-host .brand-directive {\n margin: 0.85em 0;\n}\n.milkdown-host .brand-directive__body > * + * {\n margin-top: 0.6em;\n}\n/* The first/last editable block inside a card/callout shouldn't add stray margin\n * on top of the component's own padding. */\n.milkdown-host .brand-directive__body > :first-child {\n margin-top: 0;\n}\n\n/* timeline → keep the list editable inside a subtle frame */\n.milkdown-host .brand-directive--timeline {\n border-left: 2px solid var(--border);\n padding-left: 1rem;\n}\n\n/* Inline-editable directive attribute (seamless, caret-friendly). */\n.milkdown-host .brand-inline-edit {\n cursor: text;\n min-width: 1ch;\n /* inherit the surrounding component's type (CardTitle, MetricBlock label/value) */\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n}\n.milkdown-host .brand-inline-edit:empty::before {\n content: attr(data-placeholder);\n color: var(--muted-foreground);\n opacity: 0.65;\n pointer-events: none;\n}\n\n/* ------------------------------------------------------------------------- */\n/* Focus writing (Ulysses Phase A): under a `[data-focus-writing]` ancestor */\n/* (the workspace's wysiwyg pane), every top-level block dims except the one */\n/* owning the selection (`.wb-fw-active`, set by the workspace) — paragraph */\n/* focus. Hover un-dims for mouse repositioning. Token-driven, motion-safe. */\n/* ------------------------------------------------------------------------- */\n[data-focus-writing] .milkdown-host .ProseMirror > * {\n opacity: 0.35;\n transition: opacity var(--t-base, 200ms) var(--ease-standard, ease);\n}\n[data-focus-writing] .milkdown-host .ProseMirror > .wb-fw-active,\n[data-focus-writing] .milkdown-host .ProseMirror > *:hover {\n opacity: 1;\n}\n@media (prefers-reduced-motion: reduce) {\n [data-focus-writing] .milkdown-host .ProseMirror > * {\n transition: none;\n }\n}\n","/**\n * Calc authoring decorations (#220) — shared by the Monaco source surface\n * (decoration `inlineClassName`) and the Milkdown WYSIWYG surface (ProseMirror\n * inline decorations + result-inlay widgets).\n *\n * Colors come from the `--calc-*` syntax tokens — the SAME tokens `CalcBlock`\n * paints with (#221), already AA-verified against `--background` (the editor\n * surface) and `--card` in every theme — so highlighting tracks the theme via\n * the cascade with no hardcoded hex. The compound `.brand-calc-tok.brand-calc-tok--x`\n * selector raises specificity above Monaco's own token color class (`.mtkN`) so\n * the calc color wins regardless of stylesheet load order (no `!important`).\n *\n * Hue is never the SOLE cue (var-def = weight, unresolved = dotted underline), so\n * roles stay distinct in the high-contrast theme where the calc colors collapse\n * toward the foreground — mirroring `CalcBlock.tokenClass`.\n */\n\n.brand-calc-tok.brand-calc-tok--number,\n.brand-calc-tok.brand-calc-tok--constant {\n color: var(--calc-number);\n}\n.brand-calc-tok.brand-calc-tok--unit {\n color: var(--calc-unit);\n}\n.brand-calc-tok.brand-calc-tok--currency {\n color: var(--calc-currency);\n}\n.brand-calc-tok.brand-calc-tok--operator {\n color: var(--calc-operator);\n}\n.brand-calc-tok.brand-calc-tok--function {\n color: var(--calc-function);\n}\n.brand-calc-tok.brand-calc-tok--var-ref {\n color: var(--calc-variable);\n}\n.brand-calc-tok.brand-calc-tok--var-def {\n color: var(--calc-variable);\n font-weight: 500;\n}\n.brand-calc-tok.brand-calc-tok--line-ref {\n color: var(--calc-reference);\n font-variant-numeric: tabular-nums;\n}\n.brand-calc-tok.brand-calc-tok--comment {\n color: var(--calc-comment);\n font-style: italic;\n}\n.brand-calc-tok.brand-calc-tok--unknown {\n color: var(--calc-warning);\n}\n\n/* Non-hue cue for unresolved identifiers / dangling refs — carries the role in\n high-contrast, where hue is flattened. */\n.brand-calc-tok.brand-calc-tok--unresolved {\n text-decoration: underline dotted;\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n}\n\n/**\n * WYSIWYG result inlay — a ProseMirror widget after each calc line showing the\n * computed answer. The number IS the signal (shown as text), color is only an\n * enhancement, and it is non-editable / non-selectable so it never enters the\n * document. (The Monaco surface uses Monaco's own inlay hints, themed via the\n * theme bridge's `editorInlayHint.*` colors — not this class.)\n */\n.brand-calc-inlay {\n /* Full strength (no opacity de-emphasis): the result is the answer, and the\n `--calc-result` token is AA-verified at FULL opacity (#221) — reducing it\n would drop effective contrast below what the contrast gate models. */\n margin-inline-start: 1ch;\n color: var(--calc-result);\n font-variant-numeric: tabular-nums;\n user-select: none;\n white-space: pre;\n}\n.brand-calc-inlay--error {\n color: var(--calc-warning);\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
- import { S as SlashCommand, I as IterationEditHandler, c as EditorSelection, E as EditorContentAccess, h as EditorCompletionProvider, M as MonacoCodeEditor, i as CalcEditorHooks, d as EmbedAssetFn, j as EvaluateCalc, k as CalcValue, l as CalcTokenKind } from '../markdown-editor-DfBZibAn.js';
2
- export { B as BRAND_SLASH_COMMANDS, n as BasicBlockId, o as CALC_FENCE_SEED, p as CalcComplete, q as CalcCompletion, r as CalcCompletionContext, s as CalcCompletionKind, t as CalcContext, u as CalcLineResult, v as CalcRule, w as CalcSheet, x as CalcTint, y as CalcToken, z as CalcTokenize, A as CalcValueKind, D as CompletionMatch, F as CompletionReplaceRange, G as EditorCompletionContext, H as EditorCompletionItem, J as InsertableDirective, K as IterationEditContext, L as IterationEditRequest, e as MarkdownEditor, f as MarkdownEditorHandle, g as MarkdownEditorProps, N as SlashInsertContext, O as SlashRange, P as SourceSlashContext, Q as collectCompletions, R as filterSlashCommands, T as groupSlashCommands, U as insertBasicBlock, V as insertBrandDirective, W as insertCalcFence, m as monacoContentAccess, X as resolveCalcInsert, Y as resolveReplaceRange, Z as triggerQueryStart } from '../markdown-editor-DfBZibAn.js';
1
+ import { S as SlashCommand, I as IterationEditHandler, c as EditorSelection, E as EditorContentAccess, h as EditorCompletionProvider, M as MonacoCodeEditor, i as CalcEditorHooks, d as EmbedAssetFn, j as EvaluateCalc, k as CalcValue, l as CalcTokenKind } from '../markdown-editor-CBp_4eDv.js';
2
+ export { B as BRAND_SLASH_COMMANDS, n as BasicBlockId, o as CALC_FENCE_SEED, p as CalcComplete, q as CalcCompletion, r as CalcCompletionContext, s as CalcCompletionKind, t as CalcContext, u as CalcLineResult, v as CalcRule, w as CalcSheet, x as CalcTint, y as CalcToken, z as CalcTokenize, A as CalcValueKind, D as CompletionMatch, F as CompletionReplaceRange, G as EditorCompletionContext, H as EditorCompletionItem, J as InsertableDirective, K as IterationEditContext, L as IterationEditRequest, e as MarkdownEditor, f as MarkdownEditorHandle, g as MarkdownEditorProps, N as SlashInsertContext, O as SlashRange, P as SourceSlashContext, Q as collectCompletions, R as filterSlashCommands, T as groupSlashCommands, U as insertBasicBlock, V as insertBrandDirective, W as insertCalcFence, m as monacoContentAccess, X as resolveCalcInsert, Y as resolveReplaceRange, Z as triggerQueryStart } from '../markdown-editor-CBp_4eDv.js';
3
3
  import { MilkdownPlugin } from '@milkdown/kit/ctx';
4
4
  import { EditorView } from '@milkdown/kit/prose/view';
5
5
  import * as monaco from 'monaco-editor';
@@ -54,7 +54,7 @@ import {
54
54
  transposeIterationValue,
55
55
  triggerQueryStart,
56
56
  useMarkdownOutline
57
- } from "../chunk-LBC5VJBD.js";
57
+ } from "../chunk-C62O7IOQ.js";
58
58
 
59
59
  // src/lib/editor-completions-monaco.ts
60
60
  import * as monaco from "monaco-editor";
@@ -1530,7 +1530,7 @@ function MermaidViewer({ svg, label }) {
1530
1530
  className: cn4(
1531
1531
  "w-full truncate rounded-md px-2 py-1.5 text-start text-caption transition-colors duration-fast ease-standard motion-reduce:transition-none",
1532
1532
  "hover:bg-accent hover:text-accent-foreground",
1533
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
1533
+ "focus-ring-inset",
1534
1534
  activeHit === hit.id ? "bg-accent font-medium text-foreground" : "text-muted-foreground"
1535
1535
  ),
1536
1536
  children: hit.label
@@ -2205,7 +2205,7 @@ function CiteLink({ entry, label }) {
2205
2205
  href: `#ref-${cssId(entry.key)}`,
2206
2206
  title: entry.data ? hoverTitle(entry.data) : void 0,
2207
2207
  "aria-label": `Citation: ${name}`,
2208
- className: "text-primary-text underline hover:underline focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2208
+ className: "text-primary-text underline hover:underline focus-visible:rounded-sm focus-ring",
2209
2209
  children: label
2210
2210
  }
2211
2211
  );
@@ -2310,7 +2310,7 @@ var Bibliography = forwardRef4(function Bibliography2({ entries, style, title =
2310
2310
  href,
2311
2311
  target: "_blank",
2312
2312
  rel: "noopener noreferrer",
2313
- className: "break-words text-primary-text underline underline-offset-2 hover:underline focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2313
+ className: "break-words text-primary-text underline underline-offset-2 hover:underline focus-visible:rounded-sm focus-ring",
2314
2314
  children: data.url ?? `doi:${data.doi}`
2315
2315
  }
2316
2316
  ) : null
@@ -2413,7 +2413,7 @@ function FootnoteRef({ node: _n, children: _c, ...rest }) {
2413
2413
  href: `#fn-${id}`,
2414
2414
  "data-footnote-ref": "",
2415
2415
  "aria-label": `Footnote ${n}`,
2416
- className: "px-0.5 font-medium text-primary-text underline tabular-nums hover:underline focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2416
+ className: "px-0.5 font-medium text-primary-text underline tabular-nums hover:underline focus-visible:rounded-sm focus-ring",
2417
2417
  children: n
2418
2418
  }
2419
2419
  ) });
@@ -2437,7 +2437,7 @@ function FootnoteItem({ node: _n, children, ...rest }) {
2437
2437
  href: `#${refId}`,
2438
2438
  "data-footnote-backref": "",
2439
2439
  "aria-label": refList.length > 1 ? `Back to reference ${n}, mention ${i + 1}` : `Back to reference ${n}`,
2440
- className: "ms-0.5 inline-flex items-center text-muted-foreground no-underline hover:text-foreground focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2440
+ className: "ms-0.5 inline-flex items-center text-muted-foreground no-underline hover:text-foreground focus-visible:rounded-sm focus-ring",
2441
2441
  children: [
2442
2442
  /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "\u21A9" }),
2443
2443
  refList.length > 1 ? /* @__PURE__ */ jsx8("sub", { className: "ms-0.5 leading-none tabular-nums", children: i + 1 }) : null
@@ -2596,7 +2596,7 @@ var TableOfContents = forwardRef6(
2596
2596
  "a",
2597
2597
  {
2598
2598
  href: `#${it.id}`,
2599
- className: "text-muted-foreground underline hover:text-foreground hover:underline focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2599
+ className: "text-muted-foreground underline hover:text-foreground hover:underline focus-visible:rounded-sm focus-ring",
2600
2600
  children: it.text
2601
2601
  }
2602
2602
  ) }, it.id)) })
@@ -4277,7 +4277,7 @@ function kindMeta(kind) {
4277
4277
  var entityChipVariants = cva2(
4278
4278
  // inline-flex + align-middle keeps the chip in the text baseline;
4279
4279
  // no `block` wrapper so it is valid inside <p>.
4280
- "inline-flex items-center gap-1 rounded-sm border px-1.5 py-0.5 text-meta font-medium align-middle focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4280
+ "inline-flex items-center gap-1 rounded-sm border px-1.5 py-0.5 text-meta font-medium align-middle focus-ring",
4281
4281
  {
4282
4282
  variants: {
4283
4283
  kind: {
@@ -4375,7 +4375,7 @@ function SourceRow({ path, resolve }) {
4375
4375
  href: resolved.href,
4376
4376
  rel: "noopener noreferrer",
4377
4377
  target: "_blank",
4378
- className: "flex min-w-0 items-center gap-1.5 text-meta text-primary-text underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
4378
+ className: "flex min-w-0 items-center gap-1.5 text-meta text-primary-text underline-offset-2 hover:underline focus-ring",
4379
4379
  "aria-label": `Source: ${display}`,
4380
4380
  children: [
4381
4381
  /* @__PURE__ */ jsx18(FileText, { className: "size-3 shrink-0", "aria-hidden": "true" }),