@elabs-ai/components-editor 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/dist/{chunk-LBC5VJBD.js → chunk-FO5S3YZM.js} +349 -177
- package/dist/chunk-FO5S3YZM.js.map +1 -0
- package/dist/index.css +4 -2
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +112 -57
- package/dist/index.js.map +1 -1
- package/dist/markdown/index.css +4 -2
- package/dist/markdown/index.css.map +1 -1
- package/dist/markdown/index.d.ts +2 -2
- package/dist/markdown/index.js +268 -191
- package/dist/markdown/index.js.map +1 -1
- package/dist/{markdown-editor-DfBZibAn.d.ts → markdown-editor-Dn-0L_MM.d.ts} +9 -2
- package/dist/monaco.d.ts +2 -0
- package/dist/monaco.js +9 -0
- package/dist/monaco.js.map +1 -0
- package/package.json +10 -6
- package/src/ai-objects/decision-card.tsx +15 -9
- package/src/ai-objects/entity.tsx +1 -1
- package/src/ai-objects/knowledge-card.tsx +18 -9
- package/src/barrel-monaco-lazy.test.ts +50 -0
- package/src/calc-block/calc-block.tsx +10 -4
- package/src/calc-block/calc-editor.css +1 -1
- package/src/code-editor/code-editor.stories.tsx +76 -1
- package/src/code-editor/code-editor.test.tsx +141 -14
- package/src/code-editor/code-editor.tsx +167 -36
- package/src/code-workspace/code-workspace.test.tsx +113 -11
- package/src/code-workspace/code-workspace.tsx +73 -20
- package/src/copy-button/copy-button.tsx +6 -3
- package/src/diff-editor/diff-editor.stories.tsx +9 -1
- package/src/diff-editor/diff-editor.test.tsx +9 -3
- package/src/diff-editor/diff-editor.tsx +47 -23
- package/src/editor-toolbar/editor-toolbar.tsx +8 -2
- package/src/index.ts +4 -3
- package/src/lib/monaco-deep-imports.d.ts +59 -0
- package/src/lib/monaco-theme-bridge.test.ts +307 -0
- package/src/lib/monaco-theme-bridge.ts +154 -10
- package/src/markdown-academic/citations.tsx +14 -7
- package/src/markdown-academic/footnotes.tsx +2 -2
- package/src/markdown-academic/math.tsx +7 -4
- package/src/markdown-academic/toc.tsx +1 -1
- package/src/markdown-editor/completions/completions-menu.tsx +5 -2
- package/src/markdown-editor/directive-views.tsx +35 -24
- package/src/markdown-editor/markdown-editor.css +26 -3
- package/src/markdown-editor/markdown-editor.focus.test.ts +49 -0
- package/src/markdown-editor/markdown-editor.stories.tsx +128 -7
- package/src/markdown-editor/markdown-editor.tsx +8 -5
- package/src/markdown-editor/milkdown-react/use-get-editor.timer-leak.test.ts +80 -0
- package/src/markdown-editor/milkdown-react/use-get-editor.ts +37 -1
- package/src/markdown-editor/paste-embed.ts +2 -1
- package/src/markdown-editor/slash/slash-menu.stories.tsx +8 -3
- package/src/markdown-editor/slash/slash-menu.test.tsx +27 -0
- package/src/markdown-editor/slash/slash-menu.tsx +18 -3
- package/src/markdown-editor/table-view.tsx +31 -17
- package/src/markdown-iteration/iteration-builder-dialog.stories.tsx +57 -19
- package/src/markdown-iteration/iteration-builder-dialog.tsx +39 -18
- package/src/markdown-iteration/template-dialog.tsx +25 -7
- package/src/markdown-outline/document-outline.stories.tsx +1 -1
- package/src/markdown-outline/document-outline.tsx +7 -3
- package/src/markdown-preview/markdown-preview-academic.test.tsx +3 -3
- package/src/markdown-preview/markdown-preview-transclusion.test.tsx +1 -1
- package/src/markdown-preview/markdown-preview.stories.tsx +24 -2
- package/src/markdown-preview/markdown-preview.test.tsx +20 -3
- package/src/markdown-toolbar/markdown-toolbar.stories.tsx +3 -0
- package/src/markdown-toolbar/markdown-toolbar.tsx +42 -24
- package/src/markdown-workspace/markdown-workspace.test.tsx +53 -4
- package/src/markdown-workspace/markdown-workspace.tsx +6 -3
- package/src/mermaid-diagram/mermaid-diagram-fixes.test.tsx +130 -0
- package/src/mermaid-diagram/mermaid-diagram.test.tsx +2 -1
- package/src/mermaid-diagram/mermaid-diagram.tsx +89 -40
- package/src/mermaid-diagram/mermaid-viewer.tsx +22 -21
- package/src/monaco.ts +21 -0
- package/src/prose/prose.stories.tsx +3 -0
- package/src/prose/prose.test.ts +54 -0
- package/src/prose/prose.tsx +7 -0
- 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:
|
|
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;
|
package/dist/index.css.map
CHANGED
|
@@ -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,9 +1,8 @@
|
|
|
1
|
-
import { E as EditorContentAccess, M as MonacoCodeEditor } from './markdown-editor-
|
|
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-
|
|
1
|
+
import { E as EditorContentAccess, M as MonacoCodeEditor } from './markdown-editor-Dn-0L_MM.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-Dn-0L_MM.js';
|
|
3
3
|
import * as react from 'react';
|
|
4
4
|
import { HTMLAttributes, ReactNode, ComponentProps } from 'react';
|
|
5
5
|
import * as monaco from 'monaco-editor';
|
|
6
|
-
export { monaco };
|
|
7
6
|
import { Button } from '@elabs-ai/components-ui';
|
|
8
7
|
import { ThemeName } from '@elabs-ai/components-tokens';
|
|
9
8
|
import '@milkdown/kit/ctx';
|
|
@@ -11,6 +10,8 @@ import '@milkdown/kit/prose/model';
|
|
|
11
10
|
import '@milkdown/kit/prose/state';
|
|
12
11
|
|
|
13
12
|
type MonacoDiffEditor = monaco.editor.IStandaloneDiffEditor;
|
|
13
|
+
/** Same pattern as `code-editor.tsx`'s `MonacoNamespace`. */
|
|
14
|
+
type MonacoNamespace = typeof monaco;
|
|
14
15
|
interface DiffEditorProps extends Omit<HTMLAttributes<HTMLDivElement>, "defaultValue"> {
|
|
15
16
|
/** Left/original document. */
|
|
16
17
|
original: string;
|
|
@@ -27,7 +28,7 @@ interface DiffEditorProps extends Omit<HTMLAttributes<HTMLDivElement>, "defaultV
|
|
|
27
28
|
/** Passthrough Monaco diff options (merged over the defaults). */
|
|
28
29
|
options?: monaco.editor.IStandaloneDiffEditorConstructionOptions;
|
|
29
30
|
/** Called once the diff editor + monaco namespace are ready. */
|
|
30
|
-
onMount?: (editor: MonacoDiffEditor, monacoApi:
|
|
31
|
+
onMount?: (editor: MonacoDiffEditor, monacoApi: MonacoNamespace) => void;
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
34
|
* A token-themed Monaco diff editor (original ↔ modified). Same theming bridge
|
package/dist/index.js
CHANGED
|
@@ -10,10 +10,9 @@ import {
|
|
|
10
10
|
buildBrandThemeData,
|
|
11
11
|
monacoContentAccess,
|
|
12
12
|
useDataTheme
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FO5S3YZM.js";
|
|
14
14
|
|
|
15
15
|
// src/diff-editor/diff-editor.tsx
|
|
16
|
-
import * as monaco from "monaco-editor";
|
|
17
16
|
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
18
17
|
import {
|
|
19
18
|
forwardRef,
|
|
@@ -46,59 +45,72 @@ var DiffEditor = forwardRef(function DiffEditor2({
|
|
|
46
45
|
...props
|
|
47
46
|
}, ref) {
|
|
48
47
|
const containerRef = useRef(null);
|
|
49
|
-
const [
|
|
48
|
+
const [editor, setEditor] = useState(null);
|
|
50
49
|
const { theme, revision } = useDataTheme();
|
|
50
|
+
const monacoRef = useRef(null);
|
|
51
51
|
const onMountRef = useRef(onMount);
|
|
52
52
|
onMountRef.current = onMount;
|
|
53
|
-
useImperativeHandle(ref, () =>
|
|
54
|
-
|
|
53
|
+
useImperativeHandle(ref, () => editor, [
|
|
54
|
+
editor
|
|
55
55
|
]);
|
|
56
56
|
useEffect(() => {
|
|
57
57
|
const container = containerRef.current;
|
|
58
58
|
if (!container) return;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
let cancelled = false;
|
|
60
|
+
let instance = null;
|
|
61
|
+
let originalModel = null;
|
|
62
|
+
let modifiedModel = null;
|
|
63
|
+
import("monaco-editor").then((monacoApi) => {
|
|
64
|
+
if (cancelled) return;
|
|
65
|
+
monacoRef.current = monacoApi;
|
|
66
|
+
instance = monacoApi.editor.createDiffEditor(container, {
|
|
67
|
+
...BASE_OPTIONS,
|
|
68
|
+
readOnly,
|
|
69
|
+
renderSideBySide,
|
|
70
|
+
...options
|
|
71
|
+
});
|
|
72
|
+
originalModel = monacoApi.editor.createModel(original, language);
|
|
73
|
+
modifiedModel = monacoApi.editor.createModel(modified, language);
|
|
74
|
+
instance.setModel({ original: originalModel, modified: modifiedModel });
|
|
75
|
+
setEditor(instance);
|
|
76
|
+
onMountRef.current?.(instance, monacoApi);
|
|
64
77
|
});
|
|
65
|
-
const originalModel = monaco.editor.createModel(original, language);
|
|
66
|
-
const modifiedModel = monaco.editor.createModel(modified, language);
|
|
67
|
-
instance.setModel({ original: originalModel, modified: modifiedModel });
|
|
68
|
-
setEditor(instance);
|
|
69
|
-
onMountRef.current?.(instance, monaco);
|
|
70
78
|
return () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
cancelled = true;
|
|
80
|
+
instance?.dispose();
|
|
81
|
+
originalModel?.dispose();
|
|
82
|
+
modifiedModel?.dispose();
|
|
83
|
+
monacoRef.current = null;
|
|
74
84
|
setEditor(null);
|
|
75
85
|
};
|
|
76
86
|
}, []);
|
|
77
87
|
useEffect(() => {
|
|
78
|
-
const original_ =
|
|
88
|
+
const original_ = editor?.getModel()?.original;
|
|
79
89
|
if (original_ && original_.getValue() !== original) original_.setValue(original);
|
|
80
|
-
}, [
|
|
90
|
+
}, [editor, original]);
|
|
81
91
|
useEffect(() => {
|
|
82
|
-
const modified_ =
|
|
92
|
+
const modified_ = editor?.getModel()?.modified;
|
|
83
93
|
if (modified_ && modified_.getValue() !== modified) modified_.setValue(modified);
|
|
84
|
-
}, [
|
|
94
|
+
}, [editor, modified]);
|
|
85
95
|
useEffect(() => {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
const monacoApi = monacoRef.current;
|
|
97
|
+
const models = editor?.getModel();
|
|
98
|
+
if (!models || !monacoApi) return;
|
|
99
|
+
monacoApi.editor.setModelLanguage(models.original, language);
|
|
100
|
+
monacoApi.editor.setModelLanguage(models.modified, language);
|
|
101
|
+
}, [editor, language]);
|
|
91
102
|
useEffect(() => {
|
|
92
|
-
|
|
93
|
-
}, [
|
|
103
|
+
editor?.updateOptions({ readOnly, renderSideBySide });
|
|
104
|
+
}, [editor, readOnly, renderSideBySide]);
|
|
94
105
|
useEffect(() => {
|
|
95
|
-
|
|
106
|
+
const monacoApi = monacoRef.current;
|
|
107
|
+
if (!editor || !monacoApi) return;
|
|
96
108
|
try {
|
|
97
|
-
applyBrandTheme(
|
|
109
|
+
applyBrandTheme(monacoApi, theme);
|
|
98
110
|
} catch (err) {
|
|
99
111
|
console.error("[@elabs-ai/components-editor] failed to apply brand theme", err);
|
|
100
112
|
}
|
|
101
|
-
}, [
|
|
113
|
+
}, [editor, theme, revision]);
|
|
102
114
|
const resolvedStyle = {
|
|
103
115
|
height: typeof height === "number" ? `${height}px` : height,
|
|
104
116
|
...style
|
|
@@ -116,7 +128,7 @@ var DiffEditor = forwardRef(function DiffEditor2({
|
|
|
116
128
|
});
|
|
117
129
|
|
|
118
130
|
// src/code-workspace/code-workspace.tsx
|
|
119
|
-
import { Tabs, TabsList, TabsTrigger } from "@elabs-ai/components-ui";
|
|
131
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@elabs-ai/components-ui";
|
|
120
132
|
import { cn as cn2 } from "@elabs-ai/components-ui/lib/cn";
|
|
121
133
|
import {
|
|
122
134
|
forwardRef as forwardRef2,
|
|
@@ -150,6 +162,13 @@ var EXT_LANGUAGE = {
|
|
|
150
162
|
sh: "shell"
|
|
151
163
|
};
|
|
152
164
|
var basename = (path) => path.split("/").pop() || path;
|
|
165
|
+
function tabIdForFile(path) {
|
|
166
|
+
let out = "";
|
|
167
|
+
for (const char of path) {
|
|
168
|
+
out += /[A-Za-z0-9-]/.test(char) ? char : `_${char.codePointAt(0).toString(16)}_`;
|
|
169
|
+
}
|
|
170
|
+
return `tab-${out}`;
|
|
171
|
+
}
|
|
153
172
|
function inferLanguage(file) {
|
|
154
173
|
if (file.language) return file.language;
|
|
155
174
|
const ext = file.path.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -181,6 +200,8 @@ var CodeWorkspace = forwardRef2(
|
|
|
181
200
|
const requested = isControlled ? activePath : internalActive;
|
|
182
201
|
const activeFile = files.find((f) => f.path === requested) ?? files[0];
|
|
183
202
|
const active = activeFile?.path ?? "";
|
|
203
|
+
const tabs = files.map((file) => ({ file, id: tabIdForFile(file.path) }));
|
|
204
|
+
const activeTabId = tabs.find((t) => t.file.path === active)?.id ?? "";
|
|
184
205
|
const rootRef = useRef2(null);
|
|
185
206
|
const [activeEditor, setActiveEditor] = useState2(null);
|
|
186
207
|
useEffect2(() => {
|
|
@@ -236,35 +257,63 @@ var CodeWorkspace = forwardRef2(
|
|
|
236
257
|
children: /* @__PURE__ */ jsxs(
|
|
237
258
|
Tabs,
|
|
238
259
|
{
|
|
239
|
-
value:
|
|
240
|
-
onValueChange:
|
|
260
|
+
value: activeTabId,
|
|
261
|
+
onValueChange: (id) => {
|
|
262
|
+
const found = tabs.find((t) => t.id === id);
|
|
263
|
+
if (found) selectTab(found.file.path);
|
|
264
|
+
},
|
|
241
265
|
className: "flex min-h-0 flex-1 flex-col gap-0",
|
|
242
266
|
children: [
|
|
243
267
|
/* @__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:
|
|
268
|
+
/* @__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
269
|
TabsTrigger,
|
|
246
270
|
{
|
|
247
|
-
value:
|
|
271
|
+
value: id,
|
|
248
272
|
className: "h-9 rounded-none border-e border-border px-3 text-xs data-[state=active]:bg-background data-[state=active]:shadow-none",
|
|
249
273
|
children: basename(file.path)
|
|
250
274
|
},
|
|
251
|
-
|
|
275
|
+
id
|
|
252
276
|
)) }),
|
|
253
277
|
activeFile ? /* @__PURE__ */ jsx2(CopyButton, { value: activeFile.value, className: "me-1.5" }) : null
|
|
254
278
|
] }),
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
279
|
+
tabs.map(({ file, id }) => {
|
|
280
|
+
const isActive = file.path === activeFile?.path;
|
|
281
|
+
return (
|
|
282
|
+
// `forceMount` keeps every tab's content in the DOM so each
|
|
283
|
+
// trigger's `aria-controls` resolves to a real element, not only
|
|
284
|
+
// the active one (#154). Radix derives its own `hidden` from
|
|
285
|
+
// `forceMount || isSelected`, so under `forceMount` it hides
|
|
286
|
+
// NOTHING: without the explicit `hidden` below every inactive
|
|
287
|
+
// panel stays a visible `flex-1` child and the editor height is
|
|
288
|
+
// split across all of them. `tabIndex={-1}` keeps the empty
|
|
289
|
+
// panels out of the tab sequence for the same reason. Only the
|
|
290
|
+
// active file mounts a (single) Monaco instance.
|
|
291
|
+
/* @__PURE__ */ jsx2(
|
|
292
|
+
TabsContent,
|
|
293
|
+
{
|
|
294
|
+
value: id,
|
|
295
|
+
forceMount: true,
|
|
296
|
+
hidden: !isActive,
|
|
297
|
+
tabIndex: isActive ? 0 : -1,
|
|
298
|
+
className: "mt-0 min-h-0 flex-1",
|
|
299
|
+
children: isActive ? /* @__PURE__ */ jsx2(
|
|
300
|
+
CodeEditor,
|
|
301
|
+
{
|
|
302
|
+
path: file.path,
|
|
303
|
+
language: inferLanguage(file),
|
|
304
|
+
value: file.value,
|
|
305
|
+
readOnly,
|
|
306
|
+
onChange: (next) => onFileChange?.(file.path, next),
|
|
307
|
+
onMount: (editor) => setActiveEditor(editor),
|
|
308
|
+
height: "100%"
|
|
309
|
+
},
|
|
310
|
+
file.path
|
|
311
|
+
) : null
|
|
312
|
+
},
|
|
313
|
+
id
|
|
314
|
+
)
|
|
315
|
+
);
|
|
316
|
+
})
|
|
268
317
|
]
|
|
269
318
|
}
|
|
270
319
|
)
|
|
@@ -279,7 +328,8 @@ import {
|
|
|
279
328
|
SelectContent,
|
|
280
329
|
SelectItem,
|
|
281
330
|
SelectTrigger,
|
|
282
|
-
SelectValue
|
|
331
|
+
SelectValue,
|
|
332
|
+
useLocale
|
|
283
333
|
} from "@elabs-ai/components-ui";
|
|
284
334
|
import { cn as cn3 } from "@elabs-ai/components-ui/lib/cn";
|
|
285
335
|
import { forwardRef as forwardRef3 } from "react";
|
|
@@ -316,6 +366,7 @@ var EditorToolbar = forwardRef3(function EditorToolbar2({
|
|
|
316
366
|
className,
|
|
317
367
|
...props
|
|
318
368
|
}, ref) {
|
|
369
|
+
const { t } = useLocale();
|
|
319
370
|
return /* @__PURE__ */ jsxs2(
|
|
320
371
|
"div",
|
|
321
372
|
{
|
|
@@ -330,7 +381,15 @@ var EditorToolbar = forwardRef3(function EditorToolbar2({
|
|
|
330
381
|
/* @__PURE__ */ jsxs2("div", { className: "ml-auto flex items-center gap-1.5", children: [
|
|
331
382
|
actions,
|
|
332
383
|
onLanguageChange ? /* @__PURE__ */ jsxs2(Select, { value: language, onValueChange: onLanguageChange, children: [
|
|
333
|
-
/* @__PURE__ */ jsx3(
|
|
384
|
+
/* @__PURE__ */ jsx3(
|
|
385
|
+
SelectTrigger,
|
|
386
|
+
{
|
|
387
|
+
size: "sm",
|
|
388
|
+
className: "h-7 w-[140px]",
|
|
389
|
+
"aria-label": t("editor.editorToolbar.language"),
|
|
390
|
+
children: /* @__PURE__ */ jsx3(SelectValue, { placeholder: t("editor.editorToolbar.language") })
|
|
391
|
+
}
|
|
392
|
+
),
|
|
334
393
|
/* @__PURE__ */ jsx3(SelectContent, { children: languages.map((l) => /* @__PURE__ */ jsx3(SelectItem, { value: l.id, children: l.label }, l.id)) })
|
|
335
394
|
] }) : null,
|
|
336
395
|
copyValue != null ? /* @__PURE__ */ jsx3(CopyButton, { value: copyValue }) : null
|
|
@@ -339,9 +398,6 @@ var EditorToolbar = forwardRef3(function EditorToolbar2({
|
|
|
339
398
|
}
|
|
340
399
|
);
|
|
341
400
|
});
|
|
342
|
-
|
|
343
|
-
// src/index.ts
|
|
344
|
-
import * as monaco2 from "monaco-editor";
|
|
345
401
|
export {
|
|
346
402
|
CodeEditor,
|
|
347
403
|
CodeWorkspace,
|
|
@@ -355,7 +411,6 @@ export {
|
|
|
355
411
|
brandThemeId,
|
|
356
412
|
buildBrandThemeData,
|
|
357
413
|
languageLabel,
|
|
358
|
-
monaco2 as monaco,
|
|
359
414
|
monacoContentAccess,
|
|
360
415
|
useDataTheme
|
|
361
416
|
};
|
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"],"sourcesContent":["\"use client\";\n\n// Type-only — see the matching note in `../code-editor/code-editor.tsx`. The\n// engine loads at RUNTIME via `import(\"monaco-editor\")` in the mount effect\n// below (`monacoRef`), so importing this module (e.g. transitively, via the\n// barrel) never evaluates Monaco.\nimport type * as monaco from \"monaco-editor\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n} from \"react\";\nimport { applyBrandTheme } from \"../lib/monaco-theme-bridge\";\nimport { useDataTheme } from \"../lib/use-data-theme\";\n\nexport type MonacoDiffEditor = monaco.editor.IStandaloneDiffEditor;\n\n/** Same pattern as `code-editor.tsx`'s `MonacoNamespace`. */\ntype MonacoNamespace = typeof monaco;\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: MonacoNamespace) => 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 // Populated once the dynamic `import(\"monaco-editor\")` below resolves.\n // `editor` (React state) is only ever set AFTER this ref, so every other\n // effect that reads both may assume: `editor` truthy implies this truthy.\n const monacoRef = useRef<MonacoNamespace | null>(null);\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. Monaco itself loads lazily (see the top-of-file note) — merely\n // importing this module never evaluates the engine.\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n let cancelled = false;\n let instance: MonacoDiffEditor | null = null;\n let originalModel: monaco.editor.ITextModel | null = null;\n let modifiedModel: monaco.editor.ITextModel | null = null;\n\n import(\"monaco-editor\").then((monacoApi) => {\n if (cancelled) return;\n monacoRef.current = monacoApi;\n instance = monacoApi.editor.createDiffEditor(container, {\n ...BASE_OPTIONS,\n readOnly,\n renderSideBySide,\n ...options,\n });\n originalModel = monacoApi.editor.createModel(original, language);\n modifiedModel = monacoApi.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, monacoApi);\n });\n\n return () => {\n cancelled = true;\n instance?.dispose();\n originalModel?.dispose();\n modifiedModel?.dispose();\n monacoRef.current = null;\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 monacoApi = monacoRef.current;\n const models = editor?.getModel();\n if (!models || !monacoApi) return;\n monacoApi.editor.setModelLanguage(models.original, language);\n monacoApi.editor.setModelLanguage(models.modified, language);\n }, [editor, language]);\n\n useEffect(() => {\n editor?.updateOptions({ readOnly, renderSideBySide });\n }, [editor, readOnly, renderSideBySide]);\n\n useEffect(() => {\n const monacoApi = monacoRef.current;\n if (!editor || !monacoApi) return;\n try {\n applyBrandTheme(monacoApi, theme);\n } catch (err) {\n console.error(\"[@elabs-ai/components-editor] failed to apply brand theme\", err);\n }\n // `revision` 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 useLocale,\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 const { t } = useLocale();\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\n size=\"sm\"\n className=\"h-7 w-[140px]\"\n aria-label={t(\"editor.editorToolbar.language\")}\n >\n <SelectValue placeholder={t(\"editor.editorToolbar.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"],"mappings":"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","names":["DiffEditor","cn","forwardRef","useEffect","useImperativeHandle","useRef","useState","jsx","forwardRef","CodeWorkspace","useState","useRef","useEffect","useImperativeHandle","cn","cn","forwardRef","jsx","jsxs","forwardRef","EditorToolbar","cn"]}
|
package/dist/markdown/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:
|
|
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;
|