@elabs-ai/components-editor 4.0.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 (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -0
  3. package/dist/chunk-LBC5VJBD.js +3748 -0
  4. package/dist/chunk-LBC5VJBD.js.map +1 -0
  5. package/dist/index.css +233 -0
  6. package/dist/index.css.map +1 -0
  7. package/dist/index.d.ts +199 -0
  8. package/dist/index.js +362 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib/monaco-environment.d.ts +2 -0
  11. package/dist/lib/monaco-environment.js +44 -0
  12. package/dist/lib/monaco-environment.js.map +1 -0
  13. package/dist/markdown/frontmatter.d.ts +11 -0
  14. package/dist/markdown/frontmatter.js +33 -0
  15. package/dist/markdown/frontmatter.js.map +1 -0
  16. package/dist/markdown/index.css +233 -0
  17. package/dist/markdown/index.css.map +1 -0
  18. package/dist/markdown/index.d.ts +1486 -0
  19. package/dist/markdown/index.js +4882 -0
  20. package/dist/markdown/index.js.map +1 -0
  21. package/dist/markdown/parse.d.ts +25 -0
  22. package/dist/markdown/parse.js +14 -0
  23. package/dist/markdown/parse.js.map +1 -0
  24. package/dist/markdown-editor-DfBZibAn.d.ts +702 -0
  25. package/package.json +101 -0
  26. package/src/ai-objects/ai-objects-integration.test.tsx +128 -0
  27. package/src/ai-objects/ai-objects.stories.tsx +233 -0
  28. package/src/ai-objects/ai-objects.test.tsx +205 -0
  29. package/src/ai-objects/decision-card.tsx +178 -0
  30. package/src/ai-objects/directives.ts +136 -0
  31. package/src/ai-objects/entity.tsx +181 -0
  32. package/src/ai-objects/index.ts +34 -0
  33. package/src/ai-objects/knowledge-card.tsx +143 -0
  34. package/src/calc-block/calc-block.stories.tsx +297 -0
  35. package/src/calc-block/calc-block.test.tsx +164 -0
  36. package/src/calc-block/calc-block.tsx +398 -0
  37. package/src/calc-block/calc-editor-monaco.ts +214 -0
  38. package/src/calc-block/calc-editor-prose.ts +128 -0
  39. package/src/calc-block/calc-editor.css +80 -0
  40. package/src/calc-block/calc-editor.stories.tsx +272 -0
  41. package/src/calc-block/calc-editor.test.ts +228 -0
  42. package/src/calc-block/calc-editor.ts +270 -0
  43. package/src/calc-block/calc-inline.stories.tsx +44 -0
  44. package/src/calc-block/calc-inline.test.tsx +44 -0
  45. package/src/calc-block/calc-inline.tsx +75 -0
  46. package/src/calc-block/index.ts +20 -0
  47. package/src/calc-block/types.ts +164 -0
  48. package/src/code-editor/code-editor.stories.tsx +218 -0
  49. package/src/code-editor/code-editor.test.tsx +134 -0
  50. package/src/code-editor/code-editor.tsx +250 -0
  51. package/src/code-editor/index.ts +6 -0
  52. package/src/code-workspace/code-workspace.stories.tsx +51 -0
  53. package/src/code-workspace/code-workspace.test.tsx +146 -0
  54. package/src/code-workspace/code-workspace.tsx +253 -0
  55. package/src/code-workspace/index.ts +6 -0
  56. package/src/copy-button/copy-button.tsx +51 -0
  57. package/src/copy-button/index.ts +1 -0
  58. package/src/css.d.ts +3 -0
  59. package/src/diff-editor/diff-editor.stories.tsx +44 -0
  60. package/src/diff-editor/diff-editor.test.tsx +68 -0
  61. package/src/diff-editor/diff-editor.tsx +151 -0
  62. package/src/diff-editor/index.ts +1 -0
  63. package/src/editor-context-menu/editor-context-menu.tsx +120 -0
  64. package/src/editor-context-menu/index.ts +1 -0
  65. package/src/editor-toolbar/editor-toolbar.tsx +83 -0
  66. package/src/editor-toolbar/index.ts +1 -0
  67. package/src/index.ts +48 -0
  68. package/src/lib/editor-completions-monaco.test.ts +206 -0
  69. package/src/lib/editor-completions-monaco.ts +127 -0
  70. package/src/lib/editor-completions.test.ts +156 -0
  71. package/src/lib/editor-completions.ts +153 -0
  72. package/src/lib/editor-content-access-prose.test.ts +411 -0
  73. package/src/lib/editor-content-access-prose.ts +172 -0
  74. package/src/lib/editor-content-access.test.ts +182 -0
  75. package/src/lib/editor-content-access.ts +109 -0
  76. package/src/lib/languages.ts +32 -0
  77. package/src/lib/markdown/diff.test.ts +80 -0
  78. package/src/lib/markdown/diff.ts +194 -0
  79. package/src/lib/markdown/directives.ts +342 -0
  80. package/src/lib/markdown/frontmatter.ts +50 -0
  81. package/src/lib/markdown/markdown-scale.test.ts +65 -0
  82. package/src/lib/markdown/markdown-scale.ts +62 -0
  83. package/src/lib/markdown/merge.test.ts +64 -0
  84. package/src/lib/markdown/merge.ts +158 -0
  85. package/src/lib/markdown/slugify.test.ts +156 -0
  86. package/src/lib/markdown/slugify.ts +47 -0
  87. package/src/lib/monaco-environment.ts +103 -0
  88. package/src/lib/monaco-theme-bridge.ts +293 -0
  89. package/src/lib/use-data-theme.ts +61 -0
  90. package/src/markdown/frontmatter.ts +14 -0
  91. package/src/markdown/index.ts +323 -0
  92. package/src/markdown/parse.test.ts +60 -0
  93. package/src/markdown/parse.ts +38 -0
  94. package/src/markdown-academic/citations.test.ts +77 -0
  95. package/src/markdown-academic/citations.tsx +442 -0
  96. package/src/markdown-academic/footnotes.tsx +235 -0
  97. package/src/markdown-academic/index.ts +32 -0
  98. package/src/markdown-academic/math.tsx +163 -0
  99. package/src/markdown-academic/toc.tsx +88 -0
  100. package/src/markdown-editor/completions/completions-menu.tsx +96 -0
  101. package/src/markdown-editor/completions/completions-prose.test.ts +356 -0
  102. package/src/markdown-editor/completions/completions-prose.ts +313 -0
  103. package/src/markdown-editor/completions/completions-widget.tsx +75 -0
  104. package/src/markdown-editor/completions/index.ts +37 -0
  105. package/src/markdown-editor/directive-nodes.ts +151 -0
  106. package/src/markdown-editor/directive-views.test.tsx +189 -0
  107. package/src/markdown-editor/directive-views.tsx +795 -0
  108. package/src/markdown-editor/exit-keymap.test.ts +88 -0
  109. package/src/markdown-editor/exit-keymap.ts +87 -0
  110. package/src/markdown-editor/index.ts +6 -0
  111. package/src/markdown-editor/markdown-editor.css +250 -0
  112. package/src/markdown-editor/markdown-editor.directives.test.tsx +105 -0
  113. package/src/markdown-editor/markdown-editor.fill.test.ts +34 -0
  114. package/src/markdown-editor/markdown-editor.paste-embed.test.tsx +297 -0
  115. package/src/markdown-editor/markdown-editor.stories.tsx +560 -0
  116. package/src/markdown-editor/markdown-editor.strictmode.test.tsx +38 -0
  117. package/src/markdown-editor/markdown-editor.table.test.tsx +287 -0
  118. package/src/markdown-editor/markdown-editor.tsx +560 -0
  119. package/src/markdown-editor/milkdown-react/editor.tsx +33 -0
  120. package/src/markdown-editor/milkdown-react/index.ts +8 -0
  121. package/src/markdown-editor/milkdown-react/types.ts +27 -0
  122. package/src/markdown-editor/milkdown-react/use-editor.ts +27 -0
  123. package/src/markdown-editor/milkdown-react/use-get-editor.ts +56 -0
  124. package/src/markdown-editor/milkdown-react/use-instance.ts +23 -0
  125. package/src/markdown-editor/paste-embed.ts +355 -0
  126. package/src/markdown-editor/slash/brand-slash-commands.test.ts +194 -0
  127. package/src/markdown-editor/slash/brand-slash-commands.ts +324 -0
  128. package/src/markdown-editor/slash/brand-slash-plugin.test.ts +254 -0
  129. package/src/markdown-editor/slash/brand-slash-plugin.ts +385 -0
  130. package/src/markdown-editor/slash/index.ts +107 -0
  131. package/src/markdown-editor/slash/insert-directive.test.ts +263 -0
  132. package/src/markdown-editor/slash/insert-directive.ts +345 -0
  133. package/src/markdown-editor/slash/monaco-slash-menu.test.tsx +198 -0
  134. package/src/markdown-editor/slash/monaco-slash-menu.tsx +356 -0
  135. package/src/markdown-editor/slash/shortcut-monaco.ts +69 -0
  136. package/src/markdown-editor/slash/shortcut.test.ts +157 -0
  137. package/src/markdown-editor/slash/shortcut.ts +57 -0
  138. package/src/markdown-editor/slash/slash-menu.stories.tsx +139 -0
  139. package/src/markdown-editor/slash/slash-menu.tsx +115 -0
  140. package/src/markdown-editor/slash/slash-scroll.test.tsx +52 -0
  141. package/src/markdown-editor/slash/slash-widget.tsx +97 -0
  142. package/src/markdown-editor/slash/source-slash-trigger.test.ts +42 -0
  143. package/src/markdown-editor/slash/source-slash-trigger.ts +46 -0
  144. package/src/markdown-editor/table-view.tsx +249 -0
  145. package/src/markdown-iteration/directive.tsx +92 -0
  146. package/src/markdown-iteration/edit-context.ts +56 -0
  147. package/src/markdown-iteration/index.ts +24 -0
  148. package/src/markdown-iteration/iteration-block.test.tsx +138 -0
  149. package/src/markdown-iteration/iteration-builder-dialog.stories.tsx +293 -0
  150. package/src/markdown-iteration/iteration-builder-dialog.tsx +312 -0
  151. package/src/markdown-iteration/iteration-builder.test.ts +252 -0
  152. package/src/markdown-iteration/iteration-builder.ts +303 -0
  153. package/src/markdown-iteration/iteration.test.ts +56 -0
  154. package/src/markdown-iteration/iteration.tsx +333 -0
  155. package/src/markdown-iteration/template-dialog.stories.tsx +63 -0
  156. package/src/markdown-iteration/template-dialog.test.tsx +104 -0
  157. package/src/markdown-iteration/template-dialog.tsx +116 -0
  158. package/src/markdown-outline/document-outline.stories.tsx +76 -0
  159. package/src/markdown-outline/document-outline.tsx +83 -0
  160. package/src/markdown-outline/index.ts +2 -0
  161. package/src/markdown-outline/markdown-outline.test.ts +49 -0
  162. package/src/markdown-outline/markdown-outline.ts +60 -0
  163. package/src/markdown-preview/code-fence.tsx +193 -0
  164. package/src/markdown-preview/index.ts +8 -0
  165. package/src/markdown-preview/markdown-preview-academic.stories.tsx +203 -0
  166. package/src/markdown-preview/markdown-preview-academic.test.tsx +257 -0
  167. package/src/markdown-preview/markdown-preview-iteration-calc.test.tsx +71 -0
  168. package/src/markdown-preview/markdown-preview-iteration-nested.test.tsx +58 -0
  169. package/src/markdown-preview/markdown-preview-iteration.stories.tsx +187 -0
  170. package/src/markdown-preview/markdown-preview-iteration.test.tsx +90 -0
  171. package/src/markdown-preview/markdown-preview-linking.test.tsx +61 -0
  172. package/src/markdown-preview/markdown-preview-transclusion.test.tsx +33 -0
  173. package/src/markdown-preview/markdown-preview.stories.tsx +108 -0
  174. package/src/markdown-preview/markdown-preview.test.tsx +411 -0
  175. package/src/markdown-preview/markdown-preview.tsx +1541 -0
  176. package/src/markdown-toolbar/index.ts +8 -0
  177. package/src/markdown-toolbar/markdown-commands.test.ts +84 -0
  178. package/src/markdown-toolbar/markdown-commands.ts +104 -0
  179. package/src/markdown-toolbar/markdown-toolbar.stories.tsx +56 -0
  180. package/src/markdown-toolbar/markdown-toolbar.tsx +263 -0
  181. package/src/markdown-workspace/focus-writing.test.ts +52 -0
  182. package/src/markdown-workspace/focus-writing.ts +38 -0
  183. package/src/markdown-workspace/index.ts +6 -0
  184. package/src/markdown-workspace/markdown-workspace.stories.tsx +688 -0
  185. package/src/markdown-workspace/markdown-workspace.test.tsx +326 -0
  186. package/src/markdown-workspace/markdown-workspace.tsx +787 -0
  187. package/src/mermaid-diagram/index.ts +1 -0
  188. package/src/mermaid-diagram/mermaid-diagram.stories.tsx +42 -0
  189. package/src/mermaid-diagram/mermaid-diagram.test.tsx +86 -0
  190. package/src/mermaid-diagram/mermaid-diagram.tsx +318 -0
  191. package/src/mermaid-diagram/mermaid-viewer.test.tsx +89 -0
  192. package/src/mermaid-diagram/mermaid-viewer.tsx +367 -0
  193. package/src/mermaid-diagram/real-parse.test.ts +42 -0
  194. package/src/mermaid-diagram/remediate.test.ts +60 -0
  195. package/src/mermaid-diagram/remediate.ts +124 -0
  196. package/src/mermaid-workspace/index.ts +1 -0
  197. package/src/mermaid-workspace/mermaid-workspace.stories.tsx +28 -0
  198. package/src/mermaid-workspace/mermaid-workspace.tsx +71 -0
  199. package/src/metric-block/index.ts +1 -0
  200. package/src/metric-block/metric-block.stories.tsx +40 -0
  201. package/src/metric-block/metric-block.tsx +14 -0
  202. package/src/monaco-workers.d.ts +10 -0
  203. package/src/prose/index.ts +14 -0
  204. package/src/prose/prose.stories.tsx +44 -0
  205. package/src/prose/prose.test.tsx +44 -0
  206. package/src/prose/prose.tsx +21 -0
  207. package/src/timeline/index.ts +18 -0
@@ -0,0 +1,4882 @@
1
+ "use client";
2
+ import "./index.css";
3
+ import {
4
+ BRAND_SLASH_COMMANDS,
5
+ CALC_FENCE_SEED,
6
+ CodeEditor,
7
+ CopyButton,
8
+ DEFAULT_SLASH_SHORTCUT,
9
+ DEFAULT_TEMPLATE,
10
+ DocumentOutline,
11
+ ITERATION_LAYOUTS,
12
+ IterationBlock,
13
+ IterationEditContext,
14
+ MARKDOWN_HEADING_REM,
15
+ MARKDOWN_HEADING_TRACKING,
16
+ MARKDOWN_HEADING_WEIGHT,
17
+ MARKDOWN_MEASURE,
18
+ MarkdownEditor,
19
+ MetricBlock,
20
+ SlashMenu,
21
+ brandSlashViewPlugins,
22
+ builderValueFromParts,
23
+ calcDecorationSpecs,
24
+ calcInlaySpecs,
25
+ calcProsePlugins,
26
+ calcTokenClassName,
27
+ collectCompletions,
28
+ defaultInterpolate,
29
+ directivePartsFromValue,
30
+ emptyBuilderValue,
31
+ evaluateEmbedded,
32
+ filterSlashCommands,
33
+ findCalcFences,
34
+ groupSlashCommands,
35
+ identifierPrefix,
36
+ insertBasicBlock,
37
+ insertBrandDirective,
38
+ insertCalcFence,
39
+ markdownScaleVars,
40
+ monacoContentAccess,
41
+ parseAttributes,
42
+ parseFrontmatter,
43
+ parseIterationDirective,
44
+ parseMarkdownOutline,
45
+ proseMirrorContentAccess,
46
+ resolveCalcInsert,
47
+ resolveReplaceRange,
48
+ selectionWatchPlugin,
49
+ serializeFrontmatter,
50
+ serializeIterationDirective,
51
+ slashOptionId,
52
+ splitList,
53
+ staticMarkdownFromValue,
54
+ transposeIterationValue,
55
+ triggerQueryStart,
56
+ useMarkdownOutline
57
+ } from "../chunk-LBC5VJBD.js";
58
+
59
+ // src/lib/editor-completions-monaco.ts
60
+ import * as monaco from "monaco-editor";
61
+ var REGISTRY = /* @__PURE__ */ new Map();
62
+ var refCount = 0;
63
+ var registration = null;
64
+ function ensureRegistered() {
65
+ if (registration) return;
66
+ registration = monaco.languages.registerCompletionItemProvider("markdown", {
67
+ provideCompletionItems(model, position) {
68
+ const getProviders = REGISTRY.get(model);
69
+ if (!getProviders) return { suggestions: [] };
70
+ const providers = getProviders();
71
+ if (!providers || providers.length === 0) return { suggestions: [] };
72
+ const lineText = model.getLineContent(position.lineNumber);
73
+ const ctx = {
74
+ source: model.getValue(),
75
+ line: position.lineNumber,
76
+ column: position.column,
77
+ lineText
78
+ };
79
+ return collectCompletions(providers, ctx).then((matches) => ({
80
+ suggestions: matches.map(({ provider, item }) => ({
81
+ label: item.label,
82
+ kind: monaco.languages.CompletionItemKind.Text,
83
+ insertText: item.insertText,
84
+ detail: item.detail,
85
+ range: resolveReplaceRange(item, position, lineText, provider.triggerCharacters)
86
+ }))
87
+ }));
88
+ }
89
+ });
90
+ }
91
+ function attachCompletionsMonaco(editor2, getProviders) {
92
+ ensureRegistered();
93
+ refCount++;
94
+ let model = editor2.getModel();
95
+ if (model) REGISTRY.set(model, getProviders);
96
+ const contentSub = editor2.onDidChangeModelContent((e) => {
97
+ const providers = getProviders();
98
+ if (!providers || providers.length === 0) return;
99
+ for (const change of e.changes) {
100
+ if (change.text.length !== 1) continue;
101
+ if (providers.some((p) => p.triggerCharacters?.includes(change.text))) {
102
+ editor2.trigger("brand-completions", "editor.action.triggerSuggest", {});
103
+ return;
104
+ }
105
+ }
106
+ });
107
+ const modelSub = editor2.onDidChangeModel(() => {
108
+ if (model) REGISTRY.delete(model);
109
+ model = editor2.getModel();
110
+ if (model) REGISTRY.set(model, getProviders);
111
+ });
112
+ return () => {
113
+ contentSub.dispose();
114
+ modelSub.dispose();
115
+ if (model) REGISTRY.delete(model);
116
+ refCount = Math.max(0, refCount - 1);
117
+ if (refCount === 0) {
118
+ registration?.dispose();
119
+ registration = null;
120
+ }
121
+ };
122
+ }
123
+
124
+ // src/markdown-editor/slash/monaco-slash-menu.tsx
125
+ import { cn } from "@elabs-ai/components-ui/lib/cn";
126
+ import { useEffect, useRef, useState } from "react";
127
+
128
+ // src/markdown-toolbar/markdown-commands.ts
129
+ import * as monaco2 from "monaco-editor";
130
+ function wrapSelection(editor2, before, after = before, placeholder = "text") {
131
+ const model = editor2.getModel();
132
+ const selection = editor2.getSelection();
133
+ if (!model || !selection) return;
134
+ const selected = model.getValueInRange(selection) || placeholder;
135
+ editor2.executeEdits("markdown-toolbar", [
136
+ { range: selection, text: `${before}${selected}${after}`, forceMoveMarkers: true }
137
+ ]);
138
+ const startCol = selection.startColumn + before.length;
139
+ editor2.setSelection(
140
+ new monaco2.Selection(
141
+ selection.startLineNumber,
142
+ startCol,
143
+ selection.startLineNumber,
144
+ startCol + selected.length
145
+ )
146
+ );
147
+ editor2.focus();
148
+ }
149
+ function toggleLinePrefix(editor2, prefix) {
150
+ const model = editor2.getModel();
151
+ const selection = editor2.getSelection();
152
+ if (!model || !selection) return;
153
+ const edits = [];
154
+ const allPrefixed = (() => {
155
+ for (let line = selection.startLineNumber; line <= selection.endLineNumber; line++) {
156
+ if (!model.getLineContent(line).startsWith(prefix)) return false;
157
+ }
158
+ return true;
159
+ })();
160
+ for (let line = selection.startLineNumber; line <= selection.endLineNumber; line++) {
161
+ const content = model.getLineContent(line);
162
+ if (allPrefixed) {
163
+ edits.push({
164
+ range: new monaco2.Range(line, 1, line, prefix.length + 1),
165
+ text: ""
166
+ });
167
+ } else if (!content.startsWith(prefix)) {
168
+ edits.push({ range: new monaco2.Range(line, 1, line, 1), text: prefix });
169
+ }
170
+ }
171
+ editor2.executeEdits("markdown-toolbar", edits);
172
+ editor2.focus();
173
+ }
174
+ function insertLink(editor2) {
175
+ const model = editor2.getModel();
176
+ const selection = editor2.getSelection();
177
+ if (!model || !selection) return;
178
+ const label = model.getValueInRange(selection) || "label";
179
+ editor2.executeEdits("markdown-toolbar", [
180
+ { range: selection, text: `[${label}](https://)`, forceMoveMarkers: true }
181
+ ]);
182
+ editor2.focus();
183
+ }
184
+ function insertHorizontalRule(editor2) {
185
+ const selection = editor2.getSelection();
186
+ if (!selection) return;
187
+ const line = selection.endLineNumber;
188
+ const col = editor2.getModel()?.getLineMaxColumn(line) ?? 1;
189
+ editor2.executeEdits("markdown-toolbar", [
190
+ { range: new monaco2.Range(line, col, line, col), text: `
191
+
192
+ ---
193
+ `, forceMoveMarkers: true }
194
+ ]);
195
+ editor2.focus();
196
+ }
197
+ function insertDirective(editor2, snippet) {
198
+ const selection = editor2.getSelection();
199
+ if (!selection) return;
200
+ const line = selection.endLineNumber;
201
+ const col = editor2.getModel()?.getLineMaxColumn(line) ?? 1;
202
+ editor2.executeEdits("markdown-toolbar", [
203
+ {
204
+ range: new monaco2.Range(line, col, line, col),
205
+ text: `
206
+
207
+ ${snippet}
208
+ `,
209
+ forceMoveMarkers: true
210
+ }
211
+ ]);
212
+ editor2.focus();
213
+ }
214
+
215
+ // src/markdown-editor/slash/monaco-slash-menu.tsx
216
+ import { jsx, jsxs } from "react/jsx-runtime";
217
+ var ID_PREFIX = "brand-monaco-slash";
218
+ var LISTBOX_ID = `${ID_PREFIX}-listbox`;
219
+ function getCaretCoords(editor2) {
220
+ const pos = editor2.getPosition();
221
+ if (!pos) return null;
222
+ const scrolled = editor2.getScrolledVisiblePosition(pos);
223
+ if (!scrolled) return null;
224
+ const domNode = editor2.getDomNode();
225
+ if (!domNode) return null;
226
+ const rect = domNode.getBoundingClientRect();
227
+ return {
228
+ top: rect.top + scrolled.top + (scrolled.height ?? 20),
229
+ left: rect.left + scrolled.left
230
+ };
231
+ }
232
+ function MonacoSlashMenu({
233
+ editor: editor2,
234
+ commands,
235
+ open,
236
+ onOpenChange,
237
+ onInsert,
238
+ triggerRange,
239
+ className
240
+ }) {
241
+ const [query, setQuery] = useState("");
242
+ const [activeIndex, setActiveIndex] = useState(0);
243
+ const [coords, setCoords] = useState(null);
244
+ const menuRef = useRef(null);
245
+ const commitSnippet = (snippet) => {
246
+ if (triggerRange) {
247
+ editor2.executeEdits("brand-slash-typed", [
248
+ { range: triggerRange, text: snippet, forceMoveMarkers: true }
249
+ ]);
250
+ } else {
251
+ (onInsert ?? insertDirective)(editor2, snippet);
252
+ }
253
+ onOpenChange(false);
254
+ editor2.focus();
255
+ };
256
+ const commitRunInSource = (command) => {
257
+ if (triggerRange) {
258
+ editor2.executeEdits("brand-slash-typed", [{ range: triggerRange, text: "" }]);
259
+ }
260
+ command.runInSource?.({
261
+ editor: editor2,
262
+ range: triggerRange ?? null,
263
+ content: monacoContentAccess(editor2)
264
+ });
265
+ onOpenChange(false);
266
+ editor2.focus();
267
+ };
268
+ const selectCommand = (command) => {
269
+ if (typeof command.runInSource === "function") {
270
+ commitRunInSource(command);
271
+ } else if (command.snippet) {
272
+ commitSnippet(command.snippet);
273
+ }
274
+ };
275
+ const cancel = () => {
276
+ if (triggerRange) {
277
+ editor2.executeEdits("brand-slash-cancel", [{ range: triggerRange, text: "" }]);
278
+ }
279
+ onOpenChange(false);
280
+ editor2.focus();
281
+ };
282
+ const selectRef = useRef(selectCommand);
283
+ selectRef.current = selectCommand;
284
+ const cancelRef = useRef(cancel);
285
+ cancelRef.current = cancel;
286
+ useEffect(() => {
287
+ if (open) {
288
+ setQuery("");
289
+ setActiveIndex(0);
290
+ }
291
+ }, [open]);
292
+ useEffect(() => {
293
+ if (!open) return;
294
+ const update = () => {
295
+ setCoords(getCaretCoords(editor2));
296
+ };
297
+ update();
298
+ const scrollSub = editor2.onDidScrollChange(update);
299
+ const cursorSub = editor2.onDidChangeCursorPosition(update);
300
+ const onResize = () => update();
301
+ window.addEventListener("resize", onResize);
302
+ return () => {
303
+ scrollSub.dispose();
304
+ cursorSub.dispose();
305
+ window.removeEventListener("resize", onResize);
306
+ };
307
+ }, [open, editor2]);
308
+ useEffect(() => {
309
+ if (!open) return;
310
+ const blurSub = editor2.onDidBlurEditorText(() => {
311
+ setTimeout(() => {
312
+ if (!menuRef.current?.contains(document.activeElement)) {
313
+ onOpenChange(false);
314
+ }
315
+ }, 100);
316
+ });
317
+ return () => blurSub.dispose();
318
+ }, [open, editor2, onOpenChange]);
319
+ useEffect(() => {
320
+ if (!open) return;
321
+ const keydown = (e) => {
322
+ const filtered2 = filterSlashCommands(commands, query);
323
+ if (e.key === "Escape") {
324
+ e.preventDefault();
325
+ e.stopPropagation();
326
+ cancelRef.current();
327
+ return;
328
+ }
329
+ if (e.key === "ArrowDown") {
330
+ e.preventDefault();
331
+ e.stopPropagation();
332
+ setActiveIndex((i) => (i + 1) % Math.max(filtered2.length, 1));
333
+ return;
334
+ }
335
+ if (e.key === "ArrowUp") {
336
+ e.preventDefault();
337
+ e.stopPropagation();
338
+ setActiveIndex(
339
+ (i) => (i - 1 + Math.max(filtered2.length, 1)) % Math.max(filtered2.length, 1)
340
+ );
341
+ return;
342
+ }
343
+ if (e.key === "Enter" || e.key === "Tab") {
344
+ e.preventDefault();
345
+ e.stopPropagation();
346
+ const command = filtered2[Math.min(activeIndex, filtered2.length - 1)];
347
+ if (command) selectRef.current(command);
348
+ return;
349
+ }
350
+ if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
351
+ e.preventDefault();
352
+ e.stopPropagation();
353
+ setQuery((q) => q + e.key);
354
+ setActiveIndex(0);
355
+ return;
356
+ }
357
+ if (e.key === "Backspace") {
358
+ e.preventDefault();
359
+ e.stopPropagation();
360
+ if (query.length === 0) {
361
+ cancelRef.current();
362
+ return;
363
+ }
364
+ setQuery((q) => q.slice(0, -1));
365
+ setActiveIndex(0);
366
+ }
367
+ };
368
+ const domNode = editor2.getDomNode();
369
+ domNode?.addEventListener("keydown", keydown, true);
370
+ return () => domNode?.removeEventListener("keydown", keydown, true);
371
+ }, [open, editor2, commands, query, activeIndex]);
372
+ useEffect(() => {
373
+ if (!open) return;
374
+ const filtered2 = filterSlashCommands(commands, query);
375
+ const active = filtered2[Math.min(activeIndex, Math.max(filtered2.length - 1, 0))];
376
+ if (!active) return;
377
+ const el = menuRef.current?.querySelector(
378
+ `#${CSS.escape(slashOptionId(ID_PREFIX, active.id))}`
379
+ );
380
+ el?.scrollIntoView({ block: "nearest" });
381
+ }, [open, commands, query, activeIndex]);
382
+ useEffect(() => {
383
+ if (!open) return;
384
+ const filtered2 = filterSlashCommands(commands, query);
385
+ const activeCommand2 = filtered2[Math.min(activeIndex, Math.max(filtered2.length - 1, 0))];
386
+ const textarea = editor2.getDomNode()?.querySelector("textarea");
387
+ if (!textarea) return;
388
+ textarea.setAttribute("aria-expanded", "true");
389
+ textarea.setAttribute("aria-controls", LISTBOX_ID);
390
+ if (activeCommand2) {
391
+ textarea.setAttribute("aria-activedescendant", slashOptionId(ID_PREFIX, activeCommand2.id));
392
+ } else {
393
+ textarea.removeAttribute("aria-activedescendant");
394
+ }
395
+ return () => {
396
+ textarea.removeAttribute("aria-expanded");
397
+ textarea.removeAttribute("aria-controls");
398
+ textarea.removeAttribute("aria-activedescendant");
399
+ };
400
+ }, [open, editor2, commands, query, activeIndex]);
401
+ if (!open || !coords) return null;
402
+ const filtered = filterSlashCommands(commands, query);
403
+ const activeCommand = filtered[Math.min(activeIndex, Math.max(filtered.length - 1, 0))];
404
+ const handleSelect = (command) => {
405
+ selectCommand(command);
406
+ };
407
+ const resultCount = filtered.length;
408
+ const statusText = resultCount === 0 ? "No matching blocks" : query ? `${resultCount} result${resultCount === 1 ? "" : "s"} for \u201C${query}\u201D` : `${resultCount} block${resultCount === 1 ? "" : "s"}`;
409
+ return /* @__PURE__ */ jsxs(
410
+ "div",
411
+ {
412
+ ref: menuRef,
413
+ className: cn(className),
414
+ style: { position: "fixed", top: coords.top, left: coords.left, zIndex: 50 },
415
+ onMouseDown: (e) => e.preventDefault(),
416
+ children: [
417
+ /* @__PURE__ */ jsx("span", { role: "status", "aria-live": "polite", className: "sr-only", children: statusText }),
418
+ query && /* @__PURE__ */ jsxs(
419
+ "div",
420
+ {
421
+ "aria-hidden": "true",
422
+ className: "mb-0.5 rounded-sm border border-border bg-popover px-2 py-1 text-caption text-muted-foreground",
423
+ children: [
424
+ "Filter: ",
425
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: query })
426
+ ]
427
+ }
428
+ ),
429
+ /* @__PURE__ */ jsx(
430
+ SlashMenu,
431
+ {
432
+ id: LISTBOX_ID,
433
+ commands: filtered,
434
+ activeId: activeCommand?.id,
435
+ onSelect: handleSelect,
436
+ idPrefix: ID_PREFIX
437
+ }
438
+ )
439
+ ]
440
+ }
441
+ );
442
+ }
443
+
444
+ // src/markdown-workspace/markdown-workspace.tsx
445
+ import {
446
+ ResizableHandle,
447
+ ResizablePanel,
448
+ ResizablePanelGroup,
449
+ Toggle,
450
+ ToggleGroup,
451
+ ToggleGroupItem,
452
+ Tooltip as Tooltip2,
453
+ TooltipContent as TooltipContent2,
454
+ TooltipProvider as TooltipProvider2,
455
+ TooltipTrigger as TooltipTrigger2
456
+ } from "@elabs-ai/components-ui";
457
+ import { cn as cn13 } from "@elabs-ai/components-ui/lib/cn";
458
+ import { Columns2, Eye, Focus, SquareCode } from "lucide-react";
459
+ import {
460
+ forwardRef as forwardRef9,
461
+ useEffect as useEffect5,
462
+ useImperativeHandle,
463
+ useMemo as useMemo6,
464
+ useRef as useRef5,
465
+ useState as useState5
466
+ } from "react";
467
+
468
+ // src/calc-block/calc-editor-monaco.ts
469
+ import * as monaco3 from "monaco-editor";
470
+ var REGISTRY2 = /* @__PURE__ */ new Map();
471
+ var providersRegistered = false;
472
+ var inlayEmitter = null;
473
+ function bodyLineTexts(model, fence) {
474
+ const out = [];
475
+ for (let ln = fence.bodyStartLine; ln <= fence.bodyEndLine; ln++) {
476
+ out.push(model.getLineContent(ln));
477
+ }
478
+ return out;
479
+ }
480
+ function modelFences(model) {
481
+ return findCalcFences(model.getValue(monaco3.editor.EndOfLinePreference.LF));
482
+ }
483
+ function buildDecorations(model, hooks) {
484
+ const decorations = [];
485
+ for (const fence of modelFences(model)) {
486
+ if (fence.bodyEndLine < fence.bodyStartLine) continue;
487
+ const specs = calcDecorationSpecs(hooks, fence.bodyStartLine, bodyLineTexts(model, fence));
488
+ for (const s of specs) {
489
+ decorations.push({
490
+ range: new monaco3.Range(s.lineNumber, s.startColumn, s.lineNumber, s.endColumn),
491
+ options: { inlineClassName: s.className }
492
+ });
493
+ }
494
+ }
495
+ return decorations;
496
+ }
497
+ var COMPLETION_KIND = {
498
+ variable: () => monaco3.languages.CompletionItemKind.Variable,
499
+ function: () => monaco3.languages.CompletionItemKind.Function,
500
+ unit: () => monaco3.languages.CompletionItemKind.Unit,
501
+ currency: () => monaco3.languages.CompletionItemKind.Unit,
502
+ constant: () => monaco3.languages.CompletionItemKind.Constant,
503
+ reference: () => monaco3.languages.CompletionItemKind.Reference,
504
+ keyword: () => monaco3.languages.CompletionItemKind.Keyword,
505
+ snippet: () => monaco3.languages.CompletionItemKind.Snippet
506
+ };
507
+ function mapCompletionKind(kind) {
508
+ return (COMPLETION_KIND[kind ?? "variable"] ?? COMPLETION_KIND.variable)();
509
+ }
510
+ function ensureProviders() {
511
+ if (providersRegistered) return;
512
+ providersRegistered = true;
513
+ inlayEmitter = new monaco3.Emitter();
514
+ monaco3.languages.registerInlayHintsProvider("markdown", {
515
+ onDidChangeInlayHints: inlayEmitter.event,
516
+ provideInlayHints(model, range) {
517
+ const empty = { hints: [], dispose() {
518
+ } };
519
+ const hooks = REGISTRY2.get(model)?.();
520
+ if (!hooks?.evaluate) return empty;
521
+ const hints = [];
522
+ for (const fence of modelFences(model)) {
523
+ if (fence.bodyEndLine < fence.bodyStartLine) continue;
524
+ if (fence.bodyEndLine < range.startLineNumber || fence.bodyStartLine > range.endLineNumber) {
525
+ continue;
526
+ }
527
+ for (const inlay of calcInlaySpecs(
528
+ hooks,
529
+ fence.bodyStartLine,
530
+ bodyLineTexts(model, fence)
531
+ )) {
532
+ hints.push({
533
+ position: { lineNumber: inlay.lineNumber, column: inlay.column },
534
+ label: inlay.text,
535
+ kind: monaco3.languages.InlayHintKind.Type,
536
+ paddingLeft: true
537
+ });
538
+ }
539
+ }
540
+ return { hints, dispose() {
541
+ } };
542
+ }
543
+ });
544
+ monaco3.languages.registerCompletionItemProvider("markdown", {
545
+ provideCompletionItems(model, position) {
546
+ const hooks = REGISTRY2.get(model)?.();
547
+ if (!hooks?.complete) return { suggestions: [] };
548
+ const fence = modelFences(model).find(
549
+ (f) => position.lineNumber >= f.bodyStartLine && position.lineNumber <= f.bodyEndLine
550
+ );
551
+ if (!fence) return { suggestions: [] };
552
+ const lines = bodyLineTexts(model, fence);
553
+ const line = lines[position.lineNumber - fence.bodyStartLine] ?? "";
554
+ const column = position.column - 1;
555
+ const prefix = identifierPrefix(line, column);
556
+ let completions;
557
+ try {
558
+ completions = hooks.complete({
559
+ source: lines.join("\n"),
560
+ line,
561
+ lineNumber: position.lineNumber - fence.bodyStartLine + 1,
562
+ column,
563
+ prefix
564
+ });
565
+ } catch {
566
+ return { suggestions: [] };
567
+ }
568
+ const replace = new monaco3.Range(
569
+ position.lineNumber,
570
+ position.column - prefix.length,
571
+ position.lineNumber,
572
+ position.column
573
+ );
574
+ return {
575
+ suggestions: completions.map((c) => ({
576
+ label: c.label,
577
+ insertText: c.insert,
578
+ detail: c.detail,
579
+ kind: mapCompletionKind(c.kind),
580
+ range: replace
581
+ }))
582
+ };
583
+ }
584
+ });
585
+ }
586
+ function attachCalcMonaco(editor2, getHooks) {
587
+ ensureProviders();
588
+ const collection = editor2.createDecorationsCollection();
589
+ const subs = [];
590
+ let model = editor2.getModel();
591
+ if (model) REGISTRY2.set(model, getHooks);
592
+ const refresh = () => {
593
+ const current = editor2.getModel();
594
+ const hooks = getHooks();
595
+ if (!current || !hooks) {
596
+ collection.clear();
597
+ return;
598
+ }
599
+ REGISTRY2.set(current, getHooks);
600
+ collection.set(buildDecorations(current, hooks));
601
+ inlayEmitter?.fire();
602
+ };
603
+ refresh();
604
+ subs.push(editor2.onDidChangeModelContent(refresh));
605
+ subs.push(
606
+ editor2.onDidChangeModel(() => {
607
+ if (model) REGISTRY2.delete(model);
608
+ model = editor2.getModel();
609
+ refresh();
610
+ })
611
+ );
612
+ return () => {
613
+ for (const s of subs) s.dispose();
614
+ collection.clear();
615
+ if (model) REGISTRY2.delete(model);
616
+ };
617
+ }
618
+
619
+ // src/lib/markdown/diff.ts
620
+ var MAX_DIFF_LINES = 5e3;
621
+ var splitLines = (s) => s.split("\n");
622
+ function lcsPairs(a, b) {
623
+ const n = a.length;
624
+ const m = b.length;
625
+ const dp = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
626
+ for (let i2 = n - 1; i2 >= 0; i2--) {
627
+ const row = dp[i2];
628
+ const next = dp[i2 + 1];
629
+ for (let j2 = m - 1; j2 >= 0; j2--) {
630
+ row[j2] = a[i2] === b[j2] ? next[j2 + 1] + 1 : Math.max(next[j2], row[j2 + 1]);
631
+ }
632
+ }
633
+ const pairs = [];
634
+ let i = 0;
635
+ let j = 0;
636
+ while (i < n && j < m) {
637
+ if (a[i] === b[j]) {
638
+ pairs.push([i, j]);
639
+ i++;
640
+ j++;
641
+ } else if (dp[i + 1][j] >= dp[i][j + 1]) {
642
+ i++;
643
+ } else {
644
+ j++;
645
+ }
646
+ }
647
+ return pairs;
648
+ }
649
+ function computeMarkdownAnnotations(before, after) {
650
+ if (before === after) return [];
651
+ const a = splitLines(before);
652
+ const b = splitLines(after);
653
+ if (a.length > MAX_DIFF_LINES || b.length > MAX_DIFF_LINES) return [];
654
+ const pairs = lcsPairs(a, b);
655
+ const annotations = [];
656
+ let prevA = -1;
657
+ let prevB = -1;
658
+ const walk = [...pairs, [a.length, b.length]];
659
+ for (const [ai, bi] of walk) {
660
+ const removed = ai - prevA - 1;
661
+ const added = bi - prevB - 1;
662
+ if (added > 0 && removed > 0) {
663
+ annotations.push({ kind: "modified", startLine: prevB + 2, endLine: bi });
664
+ } else if (added > 0) {
665
+ annotations.push({ kind: "added", startLine: prevB + 2, endLine: bi });
666
+ } else if (removed > 0) {
667
+ const anchor = Math.min(bi + 1, b.length);
668
+ annotations.push({
669
+ kind: "removed-before",
670
+ startLine: anchor,
671
+ endLine: anchor,
672
+ removedCount: removed
673
+ });
674
+ }
675
+ prevA = ai;
676
+ prevB = bi;
677
+ }
678
+ return mergeAdjacent(annotations);
679
+ }
680
+ function summarizeAnnotations(annotations) {
681
+ let added = 0;
682
+ let removed = 0;
683
+ for (const a of annotations) {
684
+ const span = a.endLine - a.startLine + 1;
685
+ if (a.kind === "added") added += span;
686
+ else if (a.kind === "modified") {
687
+ added += span;
688
+ removed += span;
689
+ } else if (a.kind === "removed-before") removed += a.removedCount ?? 1;
690
+ }
691
+ return { added, removed };
692
+ }
693
+ function mergeAdjacent(annotations) {
694
+ const out = [];
695
+ for (const ann of annotations) {
696
+ const last = out[out.length - 1];
697
+ if (last && last.kind !== "removed-before" && last.kind === ann.kind && ann.startLine <= last.endLine + 1) {
698
+ last.endLine = Math.max(last.endLine, ann.endLine);
699
+ } else {
700
+ out.push({ ...ann });
701
+ }
702
+ }
703
+ return out;
704
+ }
705
+ function shiftAnnotations(annotations, offset) {
706
+ if (offset === 0) return annotations;
707
+ const out = [];
708
+ for (const ann of annotations) {
709
+ const startLine = ann.startLine - offset;
710
+ const endLine = ann.endLine - offset;
711
+ if (endLine < 1) continue;
712
+ out.push({ ...ann, startLine: Math.max(1, startLine), endLine });
713
+ }
714
+ return out;
715
+ }
716
+ function annotationForRange(annotations, startLine, endLine) {
717
+ let hit;
718
+ for (const ann of annotations) {
719
+ if (ann.kind === "removed-before") continue;
720
+ if (ann.startLine <= endLine && ann.endLine >= startLine) {
721
+ if (!hit || ann.endLine - ann.startLine < hit.endLine - hit.startLine) hit = ann;
722
+ }
723
+ }
724
+ return hit;
725
+ }
726
+ function removedMarkerAt(annotations, startLine) {
727
+ return annotations.find((a) => a.kind === "removed-before" && a.startLine === startLine);
728
+ }
729
+
730
+ // src/lib/markdown/merge.ts
731
+ function alignRegions(o, b) {
732
+ const pairs = lcsPairs(o, b);
733
+ const regions = [];
734
+ let prevO = -1;
735
+ let prevB = -1;
736
+ const walk = [...pairs, [o.length, b.length]];
737
+ for (const [oi, bi] of walk) {
738
+ if (oi - prevO > 1 || bi - prevB > 1) {
739
+ regions.push({
740
+ bStart: prevB + 1,
741
+ bEnd: bi,
742
+ oLines: o.slice(prevO + 1, oi),
743
+ exact: false
744
+ });
745
+ }
746
+ if (oi < o.length && bi < b.length) {
747
+ regions.push({ bStart: bi, bEnd: bi + 1, oLines: [o[oi]], exact: true });
748
+ }
749
+ prevO = oi;
750
+ prevB = bi;
751
+ }
752
+ return regions;
753
+ }
754
+ function mergeNormalizedEdit(original, baseline, edited) {
755
+ if (baseline === edited) return original;
756
+ if (original === baseline) return edited;
757
+ const o = original.split("\n");
758
+ const b = baseline.split("\n");
759
+ const n = edited.split("\n");
760
+ if (o.length > MAX_DIFF_LINES || b.length > MAX_DIFF_LINES || n.length > MAX_DIFF_LINES) {
761
+ return edited;
762
+ }
763
+ const keptB = new Uint8Array(b.length);
764
+ const inserts = /* @__PURE__ */ new Map();
765
+ {
766
+ const pairs = lcsPairs(b, n);
767
+ let prevB = -1;
768
+ let prevN = -1;
769
+ const walk = [...pairs, [b.length, n.length]];
770
+ for (const [bi, ni] of walk) {
771
+ if (ni - prevN > 1) inserts.set(bi, n.slice(prevN + 1, ni));
772
+ if (bi < b.length) keptB[bi] = 1;
773
+ prevB = bi;
774
+ prevN = ni;
775
+ }
776
+ void prevB;
777
+ }
778
+ const regions = alignRegions(o, b);
779
+ const out = [];
780
+ const flushInsertsBefore = (bIndex, pendingFrom) => {
781
+ for (let at = pendingFrom; at <= bIndex; at++) {
782
+ const ins = inserts.get(at);
783
+ if (ins) out.push(...ins);
784
+ }
785
+ return bIndex + 1;
786
+ };
787
+ let insertCursor = 0;
788
+ for (const region of regions) {
789
+ if (region.bStart === region.bEnd) {
790
+ const before = region.bStart - 1;
791
+ const contextKept = (before < 0 || keptB[before] === 1) && (region.bStart >= b.length || keptB[region.bStart] === 1);
792
+ insertCursor = flushInsertsBefore(region.bStart - 1, insertCursor);
793
+ if (contextKept) out.push(...region.oLines);
794
+ continue;
795
+ }
796
+ let allKept = true;
797
+ for (let bi = region.bStart; bi < region.bEnd; bi++) {
798
+ if (keptB[bi] !== 1) {
799
+ allKept = false;
800
+ break;
801
+ }
802
+ }
803
+ if (region.exact || allKept) {
804
+ for (let bi = region.bStart; bi < region.bEnd; bi++) {
805
+ insertCursor = flushInsertsBefore(bi, insertCursor);
806
+ if (keptB[bi] === 1) {
807
+ if (region.exact) out.push(...region.oLines);
808
+ }
809
+ }
810
+ if (!region.exact) out.push(...region.oLines);
811
+ } else {
812
+ for (let bi = region.bStart; bi < region.bEnd; bi++) {
813
+ insertCursor = flushInsertsBefore(bi, insertCursor);
814
+ if (keptB[bi] === 1) out.push(b[bi]);
815
+ }
816
+ }
817
+ }
818
+ flushInsertsBefore(b.length, insertCursor);
819
+ return out.join("\n");
820
+ }
821
+
822
+ // src/markdown-editor/slash/source-slash-trigger.ts
823
+ function slashTriggerRange(line, lineContent, slashColumn) {
824
+ if (lineContent.charAt(slashColumn - 1) !== "/") return null;
825
+ if (slashColumn > 1) {
826
+ const before = lineContent.charAt(slashColumn - 2);
827
+ if (!/\s/.test(before)) return null;
828
+ }
829
+ return {
830
+ startLineNumber: line,
831
+ startColumn: slashColumn,
832
+ endLineNumber: line,
833
+ endColumn: slashColumn + 1
834
+ };
835
+ }
836
+
837
+ // src/markdown-editor/slash/shortcut-monaco.ts
838
+ import * as monaco4 from "monaco-editor";
839
+ var LETTER_KEY_MAP = (() => {
840
+ const map = {};
841
+ const kc = monaco4.KeyCode;
842
+ for (let i = 0; i < 26; i++) {
843
+ const letter = String.fromCharCode(65 + i);
844
+ const code = kc[`Key${letter}`];
845
+ if (code !== void 0) map[letter.toLowerCase()] = code;
846
+ }
847
+ return map;
848
+ })();
849
+ var NAMED_KEY_MAP = {
850
+ "/": monaco4.KeyCode.Slash,
851
+ backspace: monaco4.KeyCode.Backspace,
852
+ delete: monaco4.KeyCode.Delete,
853
+ escape: monaco4.KeyCode.Escape,
854
+ enter: monaco4.KeyCode.Enter,
855
+ tab: monaco4.KeyCode.Tab,
856
+ arrowup: monaco4.KeyCode.UpArrow,
857
+ arrowdown: monaco4.KeyCode.DownArrow,
858
+ arrowleft: monaco4.KeyCode.LeftArrow,
859
+ arrowright: monaco4.KeyCode.RightArrow
860
+ };
861
+ function parseShortcut(shortcut) {
862
+ const parts = shortcut.split("-");
863
+ let binding = 0;
864
+ const keyPart = parts[parts.length - 1] ?? "";
865
+ const modifiers = parts.slice(0, -1).map((m) => m.toLowerCase());
866
+ for (const mod of modifiers) {
867
+ if (mod === "mod") binding |= monaco4.KeyMod.CtrlCmd;
868
+ else if (mod === "shift") binding |= monaco4.KeyMod.Shift;
869
+ else if (mod === "alt") binding |= monaco4.KeyMod.Alt;
870
+ else if (mod === "ctrl") binding |= monaco4.KeyMod.WinCtrl;
871
+ }
872
+ const key = keyPart.toLowerCase();
873
+ const keyCode = NAMED_KEY_MAP[key] ?? LETTER_KEY_MAP[key] ?? (() => {
874
+ throw new Error(`[@elabs-ai/components-editor] parseShortcut: unrecognized key "${keyPart}"`);
875
+ })();
876
+ return binding | keyCode;
877
+ }
878
+
879
+ // src/markdown-preview/markdown-preview.tsx
880
+ import {
881
+ Alert,
882
+ AlertDescription,
883
+ AlertTitle,
884
+ Card,
885
+ CardContent,
886
+ CardHeader,
887
+ CardTitle,
888
+ Separator as Separator3,
889
+ Table,
890
+ TableBody,
891
+ TableCell,
892
+ TableHead,
893
+ TableHeader,
894
+ TableRow
895
+ } from "@elabs-ai/components-ui";
896
+ import { cn as cn11 } from "@elabs-ai/components-ui/lib/cn";
897
+ import {
898
+ createContext as createContext4,
899
+ forwardRef as forwardRef7,
900
+ isValidElement,
901
+ useContext as useContext4,
902
+ useMemo as useMemo5
903
+ } from "react";
904
+ import {
905
+ Streamdown,
906
+ defaultRehypePlugins,
907
+ defaultRemarkPlugins
908
+ } from "streamdown";
909
+ import { visit as visit5 } from "unist-util-visit";
910
+
911
+ // src/lib/markdown/directives.ts
912
+ import remarkDirective from "remark-directive";
913
+ import { visit } from "unist-util-visit";
914
+ var BRAND_DIRECTIVES = ["card", "callout", "metric", "timeline"];
915
+ var BRAND_DIRECTIVE_TAG = "brand-directive";
916
+ var BRAND_DIRECTIVE_INLINE_TAG = "brand-directive-inline";
917
+ var BRAND_DIRECTIVE_ATTR = "data-brand";
918
+ var BRAND_DIRECTIVE_PROP = "dataBrand";
919
+ var DIRECTIVE_TYPES = /* @__PURE__ */ new Set(["containerDirective", "leafDirective", "textDirective"]);
920
+ function mdastText(node) {
921
+ if (typeof node.value === "string") return node.value;
922
+ if (node.children) return node.children.map(mdastText).join("");
923
+ return "";
924
+ }
925
+ function extractTimelineItems(node) {
926
+ const list = node.children?.find((c) => c.type === "list");
927
+ if (!list?.children) return [];
928
+ const MARKER = /^\((done|complete|completed|active|current|pending|todo)\)\s*/i;
929
+ return list.children.filter((c) => c.type === "listItem").map((li) => {
930
+ let title = mdastText(li).trim();
931
+ let status = "pending";
932
+ const marker = title.match(MARKER);
933
+ if (marker) {
934
+ status = marker[1].toLowerCase();
935
+ title = title.slice(marker[0].length).trim();
936
+ }
937
+ return { title, status };
938
+ });
939
+ }
940
+ function cleanAttributes(attrs) {
941
+ const out = {};
942
+ for (const [k, v] of Object.entries(attrs ?? {})) {
943
+ if (typeof v === "string") out[k] = v;
944
+ }
945
+ return out;
946
+ }
947
+ function originalText(node, source) {
948
+ const start = node.position?.start?.offset;
949
+ const end = node.position?.end?.offset;
950
+ if (source != null && typeof start === "number" && typeof end === "number") {
951
+ return source.slice(start, end);
952
+ }
953
+ const colons = node.type === "leafDirective" ? "::" : ":";
954
+ const label = node.children?.length ? `[${mdastText(node)}]` : "";
955
+ return `${colons}${node.name ?? ""}${label}`;
956
+ }
957
+ function directiveKind(type) {
958
+ if (type === "containerDirective") return "container";
959
+ if (type === "leafDirective") return "leaf";
960
+ return "inline";
961
+ }
962
+ function rawLabel(node, source) {
963
+ const kids = node.children;
964
+ if (!source || !kids || kids.length === 0) return void 0;
965
+ const start = kids[0]?.position?.start?.offset;
966
+ const end = kids[kids.length - 1]?.position?.end?.offset;
967
+ if (typeof start === "number" && typeof end === "number") return source.slice(start, end);
968
+ return void 0;
969
+ }
970
+ function rawContainerBody(node, source) {
971
+ const kids = node.children;
972
+ if (!source || !kids || kids.length === 0) return void 0;
973
+ const body = kids.filter(
974
+ (c) => !c.data?.directiveLabel
975
+ );
976
+ if (body.length === 0) return void 0;
977
+ const start = body[0]?.position?.start?.offset;
978
+ const end = body[body.length - 1]?.position?.end?.offset;
979
+ if (typeof start === "number" && typeof end === "number") return source.slice(start, end).trim();
980
+ return void 0;
981
+ }
982
+ function remarkBrandDirectives(knownNames = BRAND_DIRECTIVES, rawBodyNames = []) {
983
+ return (tree, file) => {
984
+ const source = typeof file?.value === "string" ? file.value : void 0;
985
+ visit(tree, (node, index, parent) => {
986
+ if (!DIRECTIVE_TYPES.has(node.type) || !node.name) return void 0;
987
+ const name = node.name;
988
+ const known = knownNames.includes(name);
989
+ const kind = directiveKind(node.type);
990
+ if (!known && node.type !== "containerDirective" && parent?.children && index != null) {
991
+ const literal = { type: "text", value: originalText(node, source) };
992
+ parent.children.splice(
993
+ index,
994
+ 1,
995
+ node.type === "leafDirective" ? { type: "paragraph", children: [literal] } : literal
996
+ );
997
+ return index + 1;
998
+ }
999
+ const payload = {
1000
+ name,
1001
+ known,
1002
+ kind,
1003
+ attributes: cleanAttributes(node.attributes)
1004
+ };
1005
+ if (kind !== "container") {
1006
+ const label = rawLabel(node, source);
1007
+ if (label != null) payload.label = label;
1008
+ }
1009
+ if (kind === "container" && rawBodyNames.includes(name)) {
1010
+ const body = rawContainerBody(node, source);
1011
+ if (body != null) payload.body = body;
1012
+ node.children = [];
1013
+ }
1014
+ if (name === "timeline") {
1015
+ payload.items = extractTimelineItems(node);
1016
+ node.children = [];
1017
+ }
1018
+ const data = node.data ?? (node.data = {});
1019
+ data.hName = kind === "inline" ? BRAND_DIRECTIVE_INLINE_TAG : BRAND_DIRECTIVE_TAG;
1020
+ data.hProperties = { [BRAND_DIRECTIVE_PROP]: JSON.stringify(payload) };
1021
+ });
1022
+ };
1023
+ }
1024
+ function buildMarkdownPlugins(options = {}) {
1025
+ const known = options.directiveNames && options.directiveNames.length > 0 ? [...BRAND_DIRECTIVES, ...options.directiveNames] : BRAND_DIRECTIVES;
1026
+ const rawBodyNames = options.rawBodyNames ?? [];
1027
+ return [remarkDirective, [remarkBrandDirectives, known, rawBodyNames]];
1028
+ }
1029
+
1030
+ // src/calc-block/calc-block.tsx
1031
+ import { cn as cn2 } from "@elabs-ai/components-ui/lib/cn";
1032
+ import { TriangleAlert } from "lucide-react";
1033
+ import { forwardRef, useId, useMemo } from "react";
1034
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1035
+ var TINT_MARKERS = {
1036
+ primary: "primary",
1037
+ success: "success",
1038
+ warning: "warning",
1039
+ danger: "destructive",
1040
+ destructive: "destructive",
1041
+ info: "info",
1042
+ muted: "muted",
1043
+ note: "muted"
1044
+ };
1045
+ var RULE_MARKERS = {
1046
+ line: "single",
1047
+ rule: "single",
1048
+ line2: "double",
1049
+ double: "double",
1050
+ dotted: "dotted"
1051
+ };
1052
+ function parseMarkers(line) {
1053
+ let text = line;
1054
+ let tint;
1055
+ let rule;
1056
+ for (; ; ) {
1057
+ const m = /\s+@([A-Za-z][A-Za-z0-9]*)\s*$/.exec(text);
1058
+ if (!m) break;
1059
+ const key = (m[1] ?? "").toLowerCase();
1060
+ if (key in TINT_MARKERS) {
1061
+ tint ??= TINT_MARKERS[key];
1062
+ } else if (key in RULE_MARKERS) {
1063
+ rule ??= RULE_MARKERS[key];
1064
+ } else {
1065
+ break;
1066
+ }
1067
+ text = text.slice(0, m.index);
1068
+ }
1069
+ return { text, tint, rule };
1070
+ }
1071
+ var RULE_CLASS = {
1072
+ dotted: "border-b border-dotted border-border-strong pb-1",
1073
+ single: "border-b border-border-strong pb-1",
1074
+ double: "border-b-4 border-double border-border-strong pb-1"
1075
+ };
1076
+ var TINT_CLASS = {
1077
+ primary: "bg-primary/10",
1078
+ success: "bg-success/10",
1079
+ warning: "bg-warning/10",
1080
+ destructive: "bg-destructive/10",
1081
+ info: "bg-info/10",
1082
+ muted: "bg-muted"
1083
+ };
1084
+ function isCalcValue(x) {
1085
+ return typeof x === "object" && x !== null && !("$$typeof" in x) && "kind" in x && "display" in x;
1086
+ }
1087
+ function tokenClass(kind, resolved) {
1088
+ let base;
1089
+ let role;
1090
+ switch (kind) {
1091
+ case "comment":
1092
+ base = "text-calc-comment italic";
1093
+ role = "brand-calc-tok--comment";
1094
+ break;
1095
+ case "operator":
1096
+ base = "text-calc-operator";
1097
+ role = "brand-calc-tok--operator";
1098
+ break;
1099
+ case "unit":
1100
+ base = "text-calc-unit";
1101
+ role = "brand-calc-tok--unit";
1102
+ break;
1103
+ case "currency":
1104
+ base = "text-calc-currency";
1105
+ role = "brand-calc-tok--currency";
1106
+ break;
1107
+ case "function":
1108
+ base = "text-calc-function";
1109
+ role = "brand-calc-tok--function";
1110
+ break;
1111
+ case "var-ref":
1112
+ base = "text-calc-variable";
1113
+ role = "brand-calc-tok--var-ref";
1114
+ break;
1115
+ case "line-ref":
1116
+ base = "text-calc-reference tabular-nums";
1117
+ role = "brand-calc-tok--line-ref";
1118
+ break;
1119
+ case "var-def":
1120
+ base = "text-calc-variable font-medium";
1121
+ role = "brand-calc-tok--var-def";
1122
+ break;
1123
+ case "unknown":
1124
+ base = "text-calc-warning";
1125
+ role = "brand-calc-tok--unknown";
1126
+ break;
1127
+ default:
1128
+ base = "text-calc-number";
1129
+ role = "brand-calc-tok--number";
1130
+ }
1131
+ return cn2(
1132
+ "brand-calc-tok",
1133
+ role,
1134
+ base,
1135
+ !resolved && "underline decoration-dotted decoration-1 underline-offset-2"
1136
+ );
1137
+ }
1138
+ function renderSource(text, tokens) {
1139
+ const out = [];
1140
+ let cursor = 0;
1141
+ for (const [i, t] of tokens.entries()) {
1142
+ if (t.start > cursor) {
1143
+ out.push(/* @__PURE__ */ jsx2("span", { children: text.slice(cursor, t.start) }, `gap-${String(i)}`));
1144
+ }
1145
+ out.push(
1146
+ /* @__PURE__ */ jsx2("span", { className: tokenClass(t.kind, t.resolved), children: text.slice(t.start, t.end) }, `tok-${String(i)}`)
1147
+ );
1148
+ cursor = t.end;
1149
+ }
1150
+ if (cursor < text.length) out.push(/* @__PURE__ */ jsx2("span", { children: text.slice(cursor) }, "tail"));
1151
+ if (out.length === 0) out.push(/* @__PURE__ */ jsx2("span", { children: text || " " }, "empty"));
1152
+ return out;
1153
+ }
1154
+ function ResultCell({ result }) {
1155
+ if (result.error) {
1156
+ return /* @__PURE__ */ jsx2(
1157
+ "span",
1158
+ {
1159
+ className: "inline-flex shrink-0 text-calc-warning",
1160
+ title: result.error.message,
1161
+ "aria-label": `Error: ${result.error.message}`,
1162
+ children: /* @__PURE__ */ jsx2(TriangleAlert, { className: "size-3.5", "aria-hidden": "true" })
1163
+ }
1164
+ );
1165
+ }
1166
+ if (result.value) {
1167
+ return /* @__PURE__ */ jsxs2("div", { className: "brand-calc-tok--result shrink-0 tabular-nums text-calc-result", children: [
1168
+ /* @__PURE__ */ jsx2("span", { className: "sr-only", children: "equals " }),
1169
+ /* @__PURE__ */ jsx2("span", { children: result.value.display })
1170
+ ] });
1171
+ }
1172
+ return null;
1173
+ }
1174
+ var CalcBlock = forwardRef(function CalcBlock2({
1175
+ source,
1176
+ evaluate,
1177
+ title,
1178
+ showTotal = true,
1179
+ total,
1180
+ totalLabel = "Total",
1181
+ markers = true,
1182
+ readOnly: _readOnly,
1183
+ className,
1184
+ ...props
1185
+ }, ref) {
1186
+ const { lines, evalSource, hints } = useMemo(() => {
1187
+ const raw = source.split("\n");
1188
+ if (!markers) return { lines: raw, evalSource: source, hints: [] };
1189
+ const parsed = raw.map(parseMarkers);
1190
+ return {
1191
+ lines: parsed.map((p) => p.text),
1192
+ evalSource: parsed.map((p) => p.text).join("\n"),
1193
+ hints: parsed
1194
+ };
1195
+ }, [source, markers]);
1196
+ const sheet = useMemo(() => evaluate(evalSource), [evaluate, evalSource]);
1197
+ const empty = evalSource.trim() === "";
1198
+ const titleId = useId();
1199
+ const { titleLine, derivedTitle } = useMemo(() => {
1200
+ const idx = lines.findIndex((l) => l.trim() !== "");
1201
+ const first = (lines[idx] ?? "").trim();
1202
+ return /^#+\s+/.test(first) ? { titleLine: idx + 1, derivedTitle: first.replace(/^#+\s*/, "") } : { titleLine: 0, derivedTitle: void 0 };
1203
+ }, [lines]);
1204
+ const hasTitle = title != null || derivedTitle != null;
1205
+ const resolvedTitle = title ?? derivedTitle ?? "calc";
1206
+ const resolvedTotal = total ?? sheet.total;
1207
+ const totalDisplay = isCalcValue(resolvedTotal) ? resolvedTotal.display : resolvedTotal;
1208
+ const rows = [];
1209
+ for (const result of sheet.results) {
1210
+ if (result.line === titleLine) continue;
1211
+ const text = lines[result.line - 1] ?? "";
1212
+ const key = `row-${String(result.line)}`;
1213
+ if (text.trim() === "") {
1214
+ rows.push(/* @__PURE__ */ jsx2("div", { className: "h-1.5", "aria-hidden": "true" }, key));
1215
+ continue;
1216
+ }
1217
+ if (text.trim().startsWith("#")) {
1218
+ rows.push(
1219
+ /* @__PURE__ */ jsx2("div", { className: "pt-1 font-semibold text-foreground first:pt-0", children: text.replace(/^#+\s*/, "") }, key)
1220
+ );
1221
+ continue;
1222
+ }
1223
+ const hint = hints[result.line - 1];
1224
+ const rule = result.rule ?? hint?.rule;
1225
+ const tint = result.tint ?? hint?.tint;
1226
+ rows.push(
1227
+ /* @__PURE__ */ jsxs2(
1228
+ "div",
1229
+ {
1230
+ "data-rule": rule,
1231
+ "data-tint": tint,
1232
+ className: cn2(
1233
+ "flex items-baseline justify-between gap-x-6",
1234
+ rule && RULE_CLASS[rule],
1235
+ tint != null && cn2("-mx-2 rounded-sm px-2", TINT_CLASS[tint])
1236
+ ),
1237
+ children: [
1238
+ /* @__PURE__ */ jsx2("div", { className: "min-w-0 whitespace-pre-wrap break-words text-foreground", children: renderSource(text, result.tokens) }),
1239
+ /* @__PURE__ */ jsx2(ResultCell, { result })
1240
+ ]
1241
+ },
1242
+ key
1243
+ )
1244
+ );
1245
+ }
1246
+ return /* @__PURE__ */ jsxs2(
1247
+ "div",
1248
+ {
1249
+ ref,
1250
+ "data-testid": "calc-block",
1251
+ role: "group",
1252
+ "aria-labelledby": titleId,
1253
+ className: cn2("my-4 overflow-hidden rounded-md border border-border bg-card", className),
1254
+ ...props,
1255
+ children: [
1256
+ /* @__PURE__ */ jsx2("div", { className: "border-b border-border px-4 py-1.5", children: /* @__PURE__ */ jsx2(
1257
+ "span",
1258
+ {
1259
+ id: titleId,
1260
+ className: cn2(
1261
+ "text-meta",
1262
+ hasTitle ? "font-medium text-foreground" : "font-mono uppercase tracking-wide text-muted-foreground"
1263
+ ),
1264
+ children: resolvedTitle
1265
+ }
1266
+ ) }),
1267
+ empty ? /* @__PURE__ */ jsx2("p", { className: "px-4 py-6 text-body text-muted-foreground", children: "Empty calc block." }) : /* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-y-1 px-4 py-3 font-mono text-code leading-relaxed", children: rows }),
1268
+ showTotal && resolvedTotal != null && !empty ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between border-t border-border px-4 py-2", children: [
1269
+ /* @__PURE__ */ jsx2("span", { className: "text-meta font-medium uppercase tracking-wide text-muted-foreground", children: totalLabel }),
1270
+ /* @__PURE__ */ jsx2("span", { className: "font-mono text-code font-medium tabular-nums text-foreground", children: totalDisplay })
1271
+ ] }) : null
1272
+ ]
1273
+ }
1274
+ );
1275
+ });
1276
+
1277
+ // src/calc-block/calc-inline.tsx
1278
+ import { cn as cn3 } from "@elabs-ai/components-ui/lib/cn";
1279
+ import { TriangleAlert as TriangleAlert2 } from "lucide-react";
1280
+ import { forwardRef as forwardRef2, useMemo as useMemo2 } from "react";
1281
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1282
+ var CalcInline = forwardRef2(function CalcInline2({ source, evaluate, className, ...props }, ref) {
1283
+ const sheet = useMemo2(() => evaluate(source), [evaluate, source]);
1284
+ const first = sheet.results[0];
1285
+ const display = first?.value?.display;
1286
+ const error = first?.error ?? (display == null ? { message: "No result" } : void 0);
1287
+ if (error) {
1288
+ return /* @__PURE__ */ jsxs3(
1289
+ "span",
1290
+ {
1291
+ ref,
1292
+ "data-testid": "calc-inline",
1293
+ "data-calc-error": "",
1294
+ title: error.message,
1295
+ "aria-label": `${source}: ${error.message}`,
1296
+ className: cn3(
1297
+ "inline-flex items-center gap-0.5 align-baseline font-mono text-calc-warning underline decoration-dotted decoration-1 underline-offset-2",
1298
+ className
1299
+ ),
1300
+ ...props,
1301
+ children: [
1302
+ /* @__PURE__ */ jsx3(TriangleAlert2, { className: "size-3", "aria-hidden": "true" }),
1303
+ /* @__PURE__ */ jsx3("span", { children: source })
1304
+ ]
1305
+ }
1306
+ );
1307
+ }
1308
+ return /* @__PURE__ */ jsx3(
1309
+ "span",
1310
+ {
1311
+ ref,
1312
+ "data-testid": "calc-inline",
1313
+ title: source,
1314
+ "aria-label": `${source} = ${display}`,
1315
+ className: cn3(
1316
+ "inline-flex items-center rounded-sm bg-calc-result/10 px-1 align-baseline font-mono tabular-nums text-calc-result",
1317
+ className
1318
+ ),
1319
+ ...props,
1320
+ children: display
1321
+ }
1322
+ );
1323
+ });
1324
+
1325
+ // src/mermaid-diagram/mermaid-diagram.tsx
1326
+ import { Button as Button2, Dialog, DialogContent, DialogTitle } from "@elabs-ai/components-ui";
1327
+ import { cn as cn5 } from "@elabs-ai/components-ui/lib/cn";
1328
+ import { Download, Maximize2 } from "lucide-react";
1329
+ import { forwardRef as forwardRef3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
1330
+ import { oklchToHex } from "@elabs-ai/components-tokens";
1331
+
1332
+ // src/mermaid-diagram/mermaid-viewer.tsx
1333
+ import { Button, Input } from "@elabs-ai/components-ui";
1334
+ import { cn as cn4 } from "@elabs-ai/components-ui/lib/cn";
1335
+ import { Maximize, Minus, Plus } from "lucide-react";
1336
+ import {
1337
+ useCallback,
1338
+ useEffect as useEffect2,
1339
+ useMemo as useMemo3,
1340
+ useRef as useRef2,
1341
+ useState as useState2
1342
+ } from "react";
1343
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1344
+ var MIN_SCALE = 0.2;
1345
+ var MAX_SCALE = 6;
1346
+ var HIT_CLASS = "wb-dg-hit";
1347
+ var HIT_ACTIVE_CLASS = "wb-dg-hit-active";
1348
+ var clampScale = (s) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, s));
1349
+ function fitTransform(container, natural, pad = 24) {
1350
+ if (container.width <= pad || container.height <= pad) return null;
1351
+ if (!natural.width || !natural.height) return null;
1352
+ const scale = clampScale(
1353
+ Math.min(1, (container.width - pad) / natural.width, (container.height - pad) / natural.height)
1354
+ );
1355
+ return {
1356
+ scale,
1357
+ tx: (container.width - natural.width * scale) / 2,
1358
+ ty: (container.height - natural.height * scale) / 2
1359
+ };
1360
+ }
1361
+ function diagramNodeLabel(node) {
1362
+ const candidates = Array.from(node.querySelectorAll("tspan, p, span"));
1363
+ const leaves = candidates.filter((el) => !el.querySelector("tspan, p, span"));
1364
+ const parts = leaves.map((el) => (el.textContent ?? "").trim().replace(/\s+/g, " ")).filter(Boolean);
1365
+ if (parts.length === 0) return (node.textContent ?? "").trim().replace(/\s+/g, " ");
1366
+ return parts.join(" \xB7 ");
1367
+ }
1368
+ function MermaidViewer({ svg, label }) {
1369
+ const containerRef = useRef2(null);
1370
+ const stageRef = useRef2(null);
1371
+ const [transform, setTransform] = useState2({ scale: 1, tx: 0, ty: 0 });
1372
+ const [hits, setHits] = useState2([]);
1373
+ const [query, setQuery] = useState2("");
1374
+ const [activeHit, setActiveHit] = useState2(null);
1375
+ const dragRef = useRef2(null);
1376
+ const userDrivenRef = useRef2(false);
1377
+ const naturalSize = useRef2({ w: 0, h: 0 });
1378
+ const fit = useCallback(() => {
1379
+ const container = containerRef.current;
1380
+ const { w, h } = naturalSize.current;
1381
+ if (!container) return false;
1382
+ const next = fitTransform(
1383
+ { width: container.clientWidth, height: container.clientHeight },
1384
+ { width: w, height: h }
1385
+ );
1386
+ if (next) setTransform(next);
1387
+ return next !== null;
1388
+ }, []);
1389
+ useEffect2(() => {
1390
+ const stage = stageRef.current;
1391
+ const svgEl = stage?.querySelector("svg");
1392
+ if (!stage || !svgEl) return;
1393
+ const viewBox = svgEl.viewBox?.baseVal;
1394
+ const w = viewBox?.width || svgEl.getBoundingClientRect().width || 800;
1395
+ const h = viewBox?.height || svgEl.getBoundingClientRect().height || 600;
1396
+ svgEl.setAttribute("style", `max-width:none;width:${w}px;height:${h}px;`);
1397
+ svgEl.setAttribute("width", String(w));
1398
+ svgEl.setAttribute("height", String(h));
1399
+ const rect = svgEl.getBoundingClientRect();
1400
+ naturalSize.current = {
1401
+ w: rect.width > 0 ? rect.width : w,
1402
+ h: rect.height > 0 ? rect.height : h
1403
+ };
1404
+ const found = [];
1405
+ const seen = /* @__PURE__ */ new Set();
1406
+ for (const node of svgEl.querySelectorAll("g.node, g.edgeLabel")) {
1407
+ const text = diagramNodeLabel(node);
1408
+ if (!text || !node.id) continue;
1409
+ if (seen.has(node.id)) continue;
1410
+ seen.add(node.id);
1411
+ found.push({ id: node.id, label: text });
1412
+ }
1413
+ setHits(found);
1414
+ if (!fit()) {
1415
+ let tries = 0;
1416
+ let raf = 0;
1417
+ const attempt = () => {
1418
+ if (userDrivenRef.current) return;
1419
+ if (!fit() && ++tries < 30) raf = requestAnimationFrame(attempt);
1420
+ };
1421
+ raf = requestAnimationFrame(attempt);
1422
+ return () => cancelAnimationFrame(raf);
1423
+ }
1424
+ }, [svg, fit]);
1425
+ useEffect2(() => {
1426
+ const container = containerRef.current;
1427
+ if (!container || typeof ResizeObserver === "undefined") return;
1428
+ const observer = new ResizeObserver(() => {
1429
+ if (!userDrivenRef.current) fit();
1430
+ });
1431
+ observer.observe(container);
1432
+ return () => observer.disconnect();
1433
+ }, [fit]);
1434
+ const q = query.trim().toLowerCase();
1435
+ const matches = useMemo3(
1436
+ () => q.length >= 2 ? hits.filter((hit) => hit.label.toLowerCase().includes(q)) : [],
1437
+ [hits, q]
1438
+ );
1439
+ useEffect2(() => {
1440
+ const svgEl = stageRef.current?.querySelector("svg");
1441
+ if (!svgEl) return;
1442
+ const matchIds = new Set(matches.map((m) => m.id));
1443
+ for (const node of svgEl.querySelectorAll("g.node, g.edgeLabel")) {
1444
+ node.classList.toggle(HIT_CLASS, matchIds.has(node.id));
1445
+ node.classList.toggle(HIT_ACTIVE_CLASS, node.id === activeHit);
1446
+ }
1447
+ }, [matches, activeHit]);
1448
+ const zoomAt = useCallback((clientX, clientY, factor) => {
1449
+ const container = containerRef.current;
1450
+ if (!container) return;
1451
+ userDrivenRef.current = true;
1452
+ const rect = container.getBoundingClientRect();
1453
+ setTransform((t) => {
1454
+ const scale = clampScale(t.scale * factor);
1455
+ const px = (clientX - rect.left - t.tx) / t.scale;
1456
+ const py = (clientY - rect.top - t.ty) / t.scale;
1457
+ return { scale, tx: clientX - rect.left - px * scale, ty: clientY - rect.top - py * scale };
1458
+ });
1459
+ }, []);
1460
+ const zoomCenter = (factor) => {
1461
+ const container = containerRef.current;
1462
+ if (!container) return;
1463
+ const rect = container.getBoundingClientRect();
1464
+ zoomAt(rect.left + rect.width / 2, rect.top + rect.height / 2, factor);
1465
+ };
1466
+ const zoomToHit = useCallback((id) => {
1467
+ const container = containerRef.current;
1468
+ const svgEl = stageRef.current?.querySelector("svg");
1469
+ const node = svgEl?.querySelector(`[id="${CSS.escape(id)}"]`);
1470
+ if (!container || !svgEl || !node) return;
1471
+ userDrivenRef.current = true;
1472
+ setActiveHit(id);
1473
+ setTransform((t) => {
1474
+ const nodeRect = node.getBoundingClientRect();
1475
+ const containerRect = container.getBoundingClientRect();
1476
+ const cx = (nodeRect.left + nodeRect.width / 2 - containerRect.left - t.tx) / t.scale;
1477
+ const cy = (nodeRect.top + nodeRect.height / 2 - containerRect.top - t.ty) / t.scale;
1478
+ const scale = clampScale(Math.max(t.scale, 1.25));
1479
+ return {
1480
+ scale,
1481
+ tx: containerRect.width / 2 - cx * scale,
1482
+ ty: containerRect.height / 2 - cy * scale
1483
+ };
1484
+ });
1485
+ }, []);
1486
+ const onWheel = (e) => {
1487
+ e.preventDefault();
1488
+ zoomAt(e.clientX, e.clientY, e.deltaY < 0 ? 1.12 : 1 / 1.12);
1489
+ };
1490
+ const onPointerDown = (e) => {
1491
+ if (e.button !== 0) return;
1492
+ userDrivenRef.current = true;
1493
+ dragRef.current = { x: e.clientX, y: e.clientY, tx: transform.tx, ty: transform.ty };
1494
+ e.currentTarget.setPointerCapture(e.pointerId);
1495
+ };
1496
+ const onPointerMove = (e) => {
1497
+ const drag = dragRef.current;
1498
+ if (!drag) return;
1499
+ setTransform((t) => ({
1500
+ ...t,
1501
+ tx: drag.tx + (e.clientX - drag.x),
1502
+ ty: drag.ty + (e.clientY - drag.y)
1503
+ }));
1504
+ };
1505
+ const onPointerUp = () => {
1506
+ dragRef.current = null;
1507
+ };
1508
+ return /* @__PURE__ */ jsxs4("div", { className: "flex min-h-0 flex-1 gap-3", children: [
1509
+ /* @__PURE__ */ jsxs4("div", { className: "flex w-60 shrink-0 flex-col border-e border-border pe-3", children: [
1510
+ /* @__PURE__ */ jsx4(
1511
+ Input,
1512
+ {
1513
+ autoFocus: true,
1514
+ type: "search",
1515
+ placeholder: "Find in diagram\u2026",
1516
+ "aria-label": "Find in diagram",
1517
+ value: query,
1518
+ onChange: (e) => setQuery(e.target.value),
1519
+ spellCheck: false,
1520
+ className: "h-8 text-body"
1521
+ }
1522
+ ),
1523
+ /* @__PURE__ */ jsx4("p", { "aria-live": "polite", className: "px-1 pt-1.5 text-meta text-muted-foreground tabular-nums", children: q.length >= 2 ? `${matches.length} ${matches.length === 1 ? "node" : "nodes"}` : `${hits.length} nodes \xB7 type to filter` }),
1524
+ /* @__PURE__ */ jsx4("ul", { className: "m-0 mt-1 min-h-0 flex-1 list-none overflow-auto p-0", children: (q.length >= 2 ? matches : hits).map((hit) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
1525
+ "button",
1526
+ {
1527
+ type: "button",
1528
+ onClick: () => zoomToHit(hit.id),
1529
+ "aria-pressed": activeHit === hit.id,
1530
+ className: cn4(
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
+ "hover:bg-accent hover:text-accent-foreground",
1533
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
1534
+ activeHit === hit.id ? "bg-accent font-medium text-foreground" : "text-muted-foreground"
1535
+ ),
1536
+ children: hit.label
1537
+ }
1538
+ ) }, hit.id)) })
1539
+ ] }),
1540
+ /* @__PURE__ */ jsxs4("div", { className: "relative min-h-0 min-w-0 flex-1", children: [
1541
+ /* @__PURE__ */ jsxs4("div", { className: "absolute end-2 top-2 z-10 flex gap-1", children: [
1542
+ /* @__PURE__ */ jsx4(
1543
+ Button,
1544
+ {
1545
+ variant: "outline",
1546
+ size: "icon-sm",
1547
+ "aria-label": "Zoom out",
1548
+ onClick: () => zoomCenter(1 / 1.25),
1549
+ children: /* @__PURE__ */ jsx4(Minus, { className: "size-3.5" })
1550
+ }
1551
+ ),
1552
+ /* @__PURE__ */ jsx4(
1553
+ Button,
1554
+ {
1555
+ variant: "outline",
1556
+ size: "icon-sm",
1557
+ "aria-label": "Zoom in",
1558
+ onClick: () => zoomCenter(1.25),
1559
+ children: /* @__PURE__ */ jsx4(Plus, { className: "size-3.5" })
1560
+ }
1561
+ ),
1562
+ /* @__PURE__ */ jsxs4(
1563
+ Button,
1564
+ {
1565
+ variant: "outline",
1566
+ size: "sm",
1567
+ className: "h-7 px-2 font-mono text-meta tabular-nums",
1568
+ "aria-label": "Reset zoom to 100%",
1569
+ onClick: () => {
1570
+ userDrivenRef.current = true;
1571
+ setTransform((t) => ({ ...t, scale: 1 }));
1572
+ },
1573
+ children: [
1574
+ Math.round(transform.scale * 100),
1575
+ "%"
1576
+ ]
1577
+ }
1578
+ ),
1579
+ /* @__PURE__ */ jsx4(
1580
+ Button,
1581
+ {
1582
+ variant: "outline",
1583
+ size: "icon-sm",
1584
+ "aria-label": "Fit diagram",
1585
+ onClick: () => {
1586
+ userDrivenRef.current = false;
1587
+ fit();
1588
+ },
1589
+ children: /* @__PURE__ */ jsx4(Maximize, { className: "size-3.5" })
1590
+ }
1591
+ )
1592
+ ] }),
1593
+ /* @__PURE__ */ jsx4(
1594
+ "div",
1595
+ {
1596
+ ref: containerRef,
1597
+ role: "img",
1598
+ "aria-label": label,
1599
+ className: cn4(
1600
+ "h-full w-full touch-none select-none overflow-hidden rounded-md bg-surface-muted/50",
1601
+ dragRef.current ? "cursor-grabbing" : "cursor-grab"
1602
+ ),
1603
+ onWheel,
1604
+ onPointerDown,
1605
+ onPointerMove,
1606
+ onPointerUp,
1607
+ onPointerCancel: onPointerUp,
1608
+ onDoubleClick: (e) => zoomAt(e.clientX, e.clientY, 1.5),
1609
+ children: /* @__PURE__ */ jsx4(
1610
+ "div",
1611
+ {
1612
+ ref: stageRef,
1613
+ style: {
1614
+ transform: `translate(${transform.tx}px, ${transform.ty}px) scale(${transform.scale})`,
1615
+ transformOrigin: "0 0"
1616
+ },
1617
+ dangerouslySetInnerHTML: { __html: svg }
1618
+ }
1619
+ )
1620
+ }
1621
+ )
1622
+ ] })
1623
+ ] });
1624
+ }
1625
+
1626
+ // src/mermaid-diagram/remediate.ts
1627
+ var FLOWCHART_RESERVED = /* @__PURE__ */ new Set([
1628
+ "graph",
1629
+ "flowchart",
1630
+ "subgraph",
1631
+ "end",
1632
+ "style",
1633
+ "linkstyle",
1634
+ "classdef",
1635
+ "class",
1636
+ "click",
1637
+ "direction",
1638
+ "default",
1639
+ "state"
1640
+ ]);
1641
+ function offendingToken(errorMessage) {
1642
+ const m = /got '([A-Za-z_]+)'/.exec(errorMessage);
1643
+ return m ? m[1].toLowerCase() : null;
1644
+ }
1645
+ var DECLARATION_RE = /^(graph|flowchart)\s+(tb|td|bt|rl|lr)\s*;?\s*$/;
1646
+ function replaceOutsideLabels(line, re, repl) {
1647
+ let out = "";
1648
+ let buf = "";
1649
+ let depth = 0;
1650
+ let inQuote = false;
1651
+ let inPipe = false;
1652
+ const flush = () => {
1653
+ out += buf.replace(re, repl);
1654
+ buf = "";
1655
+ };
1656
+ for (const ch of line) {
1657
+ if (inQuote) {
1658
+ out += ch;
1659
+ if (ch === '"') inQuote = false;
1660
+ continue;
1661
+ }
1662
+ if (depth === 0 && ch === "|") {
1663
+ if (!inPipe) flush();
1664
+ inPipe = !inPipe;
1665
+ out += ch;
1666
+ continue;
1667
+ }
1668
+ if (inPipe) {
1669
+ out += ch;
1670
+ continue;
1671
+ }
1672
+ if (ch === '"') {
1673
+ flush();
1674
+ out += ch;
1675
+ inQuote = true;
1676
+ continue;
1677
+ }
1678
+ if (ch === "[" || ch === "(" || ch === "{") {
1679
+ if (depth === 0) flush();
1680
+ depth++;
1681
+ out += ch;
1682
+ continue;
1683
+ }
1684
+ if (ch === "]" || ch === ")" || ch === "}") {
1685
+ if (depth > 0) depth--;
1686
+ if (depth === 0) {
1687
+ out += ch;
1688
+ continue;
1689
+ }
1690
+ out += ch;
1691
+ continue;
1692
+ }
1693
+ if (depth === 0) buf += ch;
1694
+ else out += ch;
1695
+ }
1696
+ flush();
1697
+ return out;
1698
+ }
1699
+ function remediateReservedIds(chart, token) {
1700
+ const t = token.toLowerCase();
1701
+ if (!FLOWCHART_RESERVED.has(t)) return null;
1702
+ const re = new RegExp(`\\b${t}\\b`, "gi");
1703
+ let changed = false;
1704
+ const repl = `${t}_`;
1705
+ const out = chart.split("\n").map((line) => {
1706
+ const trimmed = line.trim().toLowerCase();
1707
+ if (trimmed === t) return line;
1708
+ if (DECLARATION_RE.test(trimmed)) return line;
1709
+ if (t === "subgraph" && trimmed.startsWith("subgraph")) return line;
1710
+ const next = replaceOutsideLabels(line, re, repl);
1711
+ if (next !== line) changed = true;
1712
+ return next;
1713
+ }).join("\n");
1714
+ return changed ? out : null;
1715
+ }
1716
+
1717
+ // src/mermaid-diagram/mermaid-diagram.tsx
1718
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1719
+ var TOKEN_VARS = {
1720
+ background: "--background",
1721
+ mainBkg: "--card",
1722
+ primaryColor: "--muted",
1723
+ primaryTextColor: "--foreground",
1724
+ primaryBorderColor: "--border-strong",
1725
+ secondaryColor: "--secondary",
1726
+ tertiaryColor: "--muted",
1727
+ lineColor: "--muted-foreground",
1728
+ textColor: "--foreground",
1729
+ nodeBorder: "--border-strong",
1730
+ clusterBkg: "--surface-muted",
1731
+ clusterBorder: "--border",
1732
+ titleColor: "--foreground",
1733
+ edgeLabelBackground: "--background",
1734
+ errorBkgColor: "--destructive",
1735
+ errorTextColor: "--destructive-foreground"
1736
+ };
1737
+ var renderSeq = 0;
1738
+ var HIT_CSS = `
1739
+ .wb-dg-hit :is(rect, polygon, circle, ellipse, path.basic) { stroke: var(--warning) !important; stroke-width: 2.5px !important; }
1740
+ .wb-dg-hit-active :is(rect, polygon, circle, ellipse, path.basic) { stroke: var(--primary) !important; stroke-width: 3px !important; }
1741
+ .wb-dg-hit.edgeLabel { outline: 2px solid var(--warning); border-radius: 2px; }
1742
+ .wb-dg-hit-active.edgeLabel { outline: 2px solid var(--primary); border-radius: 2px; }
1743
+ `;
1744
+ var KHROMA_SAFE_RE = /^(#|rgba?\(|hsla?\()/i;
1745
+ function normalizeColor(value, ctx) {
1746
+ const v = value.trim();
1747
+ if (!v || KHROMA_SAFE_RE.test(v)) return v;
1748
+ const fromOklch = oklchToHex(v);
1749
+ if (fromOklch) return fromOklch;
1750
+ if (ctx) {
1751
+ try {
1752
+ ctx.fillStyle = "#000";
1753
+ ctx.fillStyle = v;
1754
+ if (KHROMA_SAFE_RE.test(ctx.fillStyle)) return ctx.fillStyle;
1755
+ } catch {
1756
+ }
1757
+ }
1758
+ return v;
1759
+ }
1760
+ function resolveThemeVariables(el) {
1761
+ const styles = getComputedStyle(el);
1762
+ let ctx = null;
1763
+ try {
1764
+ ctx = document.createElement("canvas").getContext("2d");
1765
+ } catch {
1766
+ ctx = null;
1767
+ }
1768
+ const vars = {
1769
+ fontFamily: styles.getPropertyValue("--font-sans").trim() || "inherit"
1770
+ };
1771
+ for (const [mermaidVar, token] of Object.entries(TOKEN_VARS)) {
1772
+ const raw = styles.getPropertyValue(token).trim();
1773
+ if (raw) vars[mermaidVar] = normalizeColor(raw, ctx);
1774
+ }
1775
+ return vars;
1776
+ }
1777
+ var MermaidDiagram = forwardRef3(
1778
+ function MermaidDiagram2({
1779
+ chart,
1780
+ label = "Diagram",
1781
+ copyable = true,
1782
+ expandable = true,
1783
+ highlightTerm,
1784
+ activeText,
1785
+ className,
1786
+ ...props
1787
+ }, ref) {
1788
+ const hostRef = useRef3(null);
1789
+ const svgHostRef = useRef3(null);
1790
+ const [svg, setSvg] = useState3(null);
1791
+ const [error, setError] = useState3(null);
1792
+ const [expanded, setExpanded] = useState3(false);
1793
+ const downloadSvg = () => {
1794
+ if (!svg) return;
1795
+ const blob = new Blob([svg], { type: "image/svg+xml" });
1796
+ const url = URL.createObjectURL(blob);
1797
+ const a = document.createElement("a");
1798
+ a.href = url;
1799
+ a.download = `${label.toLowerCase().replace(/[^a-z0-9]+/g, "-") || "diagram"}.svg`;
1800
+ a.click();
1801
+ URL.revokeObjectURL(url);
1802
+ };
1803
+ const [themeVersion, setThemeVersion] = useState3(0);
1804
+ useEffect3(() => {
1805
+ const host = hostRef.current;
1806
+ if (!host) return;
1807
+ const scope = host.closest("[data-theme]") ?? document.documentElement;
1808
+ const observer = new MutationObserver(() => setThemeVersion((v) => v + 1));
1809
+ observer.observe(scope, { attributes: true, attributeFilter: ["data-theme"] });
1810
+ return () => observer.disconnect();
1811
+ }, []);
1812
+ useEffect3(() => {
1813
+ let cancelled = false;
1814
+ const host = hostRef.current;
1815
+ if (!host || !chart.trim()) {
1816
+ setSvg(null);
1817
+ setError(null);
1818
+ return;
1819
+ }
1820
+ (async () => {
1821
+ try {
1822
+ const mermaid = (await import("mermaid")).default;
1823
+ if (cancelled) return;
1824
+ mermaid.initialize({
1825
+ startOnLoad: false,
1826
+ securityLevel: "strict",
1827
+ suppressErrorRendering: true,
1828
+ theme: "base",
1829
+ themeVariables: resolveThemeVariables(host)
1830
+ });
1831
+ const render = (source) => mermaid.render(`brand-mermaid-${++renderSeq}`, source);
1832
+ let out;
1833
+ try {
1834
+ out = await render(chart);
1835
+ } catch (firstErr) {
1836
+ const token = offendingToken(
1837
+ firstErr instanceof Error ? firstErr.message : String(firstErr)
1838
+ );
1839
+ const fixed = token ? remediateReservedIds(chart, token) : null;
1840
+ if (!fixed) throw firstErr;
1841
+ out = await render(fixed);
1842
+ }
1843
+ if (cancelled) return;
1844
+ setSvg(out.svg);
1845
+ setError(null);
1846
+ } catch (err) {
1847
+ if (cancelled) return;
1848
+ setSvg(null);
1849
+ setError(err instanceof Error ? err.message : String(err));
1850
+ }
1851
+ })();
1852
+ return () => {
1853
+ cancelled = true;
1854
+ };
1855
+ }, [chart, themeVersion]);
1856
+ useEffect3(() => {
1857
+ const root = svgHostRef.current;
1858
+ if (!root) return;
1859
+ const term = (highlightTerm ?? "").trim().toLowerCase();
1860
+ const active = (activeText ?? "").trim().toLowerCase();
1861
+ for (const node of root.querySelectorAll("g.node, g.edgeLabel")) {
1862
+ const text = (node.textContent ?? "").trim().toLowerCase();
1863
+ const hit = term.length >= 2 && text.length > 0 && text.includes(term);
1864
+ node.classList.toggle("wb-dg-hit", hit);
1865
+ node.classList.toggle(
1866
+ "wb-dg-hit-active",
1867
+ hit && active.length > 0 && (active.includes(text) || text.includes(active))
1868
+ );
1869
+ }
1870
+ }, [svg, highlightTerm, activeText]);
1871
+ return /* @__PURE__ */ jsxs5(
1872
+ "div",
1873
+ {
1874
+ ref: (el) => {
1875
+ hostRef.current = el;
1876
+ if (typeof ref === "function") ref(el);
1877
+ else if (ref) ref.current = el;
1878
+ },
1879
+ "data-testid": "mermaid-diagram",
1880
+ className: cn5("group/mermaid relative", className),
1881
+ ...props,
1882
+ children: [
1883
+ svg && (copyable || expandable) ? /* @__PURE__ */ jsxs5("div", { className: "absolute end-2 top-2 z-10 flex gap-1 opacity-0 transition-opacity duration-fast ease-standard focus-within:opacity-100 group-hover/mermaid:opacity-100 motion-reduce:transition-none", children: [
1884
+ expandable ? /* @__PURE__ */ jsx5(
1885
+ Button2,
1886
+ {
1887
+ variant: "outline",
1888
+ size: "icon-sm",
1889
+ "aria-label": "Expand diagram",
1890
+ onClick: () => setExpanded(true),
1891
+ children: /* @__PURE__ */ jsx5(Maximize2, { className: "size-3.5" })
1892
+ }
1893
+ ) : null,
1894
+ /* @__PURE__ */ jsx5(
1895
+ Button2,
1896
+ {
1897
+ variant: "outline",
1898
+ size: "icon-sm",
1899
+ "aria-label": "Download diagram as SVG",
1900
+ onClick: downloadSvg,
1901
+ children: /* @__PURE__ */ jsx5(Download, { className: "size-3.5" })
1902
+ }
1903
+ ),
1904
+ copyable ? /* @__PURE__ */ jsx5(
1905
+ CopyButton,
1906
+ {
1907
+ value: chart,
1908
+ label: false,
1909
+ "aria-label": "Copy diagram source",
1910
+ size: "icon-sm"
1911
+ }
1912
+ ) : null
1913
+ ] }) : null,
1914
+ error ? /* @__PURE__ */ jsxs5(
1915
+ "div",
1916
+ {
1917
+ role: "alert",
1918
+ className: "space-y-2 border-s-2 border-s-destructive bg-destructive/10 p-3 text-body",
1919
+ children: [
1920
+ /* @__PURE__ */ jsx5("p", { className: "font-medium text-destructive-text", children: "Diagram failed to render" }),
1921
+ /* @__PURE__ */ jsx5("p", { className: "text-muted-foreground", children: error }),
1922
+ /* @__PURE__ */ jsx5("pre", { className: "overflow-x-auto rounded bg-muted p-2 font-mono text-code text-foreground", children: chart })
1923
+ ]
1924
+ }
1925
+ ) : svg ? /* @__PURE__ */ jsxs5(Fragment, { children: [
1926
+ /* @__PURE__ */ jsx5("style", { children: HIT_CSS }),
1927
+ /* @__PURE__ */ jsx5(
1928
+ "div",
1929
+ {
1930
+ ref: svgHostRef,
1931
+ role: "img",
1932
+ "aria-label": label,
1933
+ className: "overflow-x-auto rounded-md bg-card p-3 [&_svg]:mx-auto [&_svg]:h-auto [&_svg]:max-w-full",
1934
+ dangerouslySetInnerHTML: { __html: svg }
1935
+ }
1936
+ )
1937
+ ] }) : /* @__PURE__ */ jsx5(
1938
+ "div",
1939
+ {
1940
+ role: "status",
1941
+ "aria-label": "Rendering diagram\u2026",
1942
+ className: "h-24 animate-pulse rounded-md bg-surface-muted motion-reduce:animate-none"
1943
+ }
1944
+ ),
1945
+ expandable ? /* @__PURE__ */ jsx5(Dialog, { open: expanded, onOpenChange: setExpanded, children: /* @__PURE__ */ jsxs5(DialogContent, { className: "flex h-[88dvh] w-[92vw] max-w-[92vw] flex-col p-4 sm:max-w-[92vw]", children: [
1946
+ /* @__PURE__ */ jsx5(DialogTitle, { className: "sr-only", children: label }),
1947
+ svg ? /* @__PURE__ */ jsx5(MermaidViewer, { svg, label }) : null
1948
+ ] }) }) : null
1949
+ ]
1950
+ }
1951
+ );
1952
+ }
1953
+ );
1954
+
1955
+ // src/prose/prose.tsx
1956
+ import {
1957
+ ProseHeading,
1958
+ ProseText,
1959
+ ProseLink,
1960
+ ProseList,
1961
+ ProseListItem,
1962
+ ProseBlockquote,
1963
+ ProseInlineCode
1964
+ } from "@elabs-ai/components-ui";
1965
+
1966
+ // src/timeline/index.ts
1967
+ import {
1968
+ Timeline
1969
+ } from "@elabs-ai/components-ui";
1970
+
1971
+ // src/markdown-preview/code-fence.tsx
1972
+ import { cn as cn6 } from "@elabs-ai/components-ui/lib/cn";
1973
+ import { code as codeHighlighter } from "@streamdown/code";
1974
+ import {
1975
+ useEffect as useEffect4,
1976
+ useRef as useRef4,
1977
+ useState as useState4
1978
+ } from "react";
1979
+ import { createCssVariablesTheme } from "shiki";
1980
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1981
+ function fenceLanguage(className) {
1982
+ return /\blanguage-([\w+#.-]+)\b/.exec(className ?? "")?.[1];
1983
+ }
1984
+ var cssVariablesTheme = createCssVariablesTheme({
1985
+ name: "brand-tokens",
1986
+ variablePrefix: "--md-code-",
1987
+ fontStyle: true
1988
+ });
1989
+ var SHIKI_THEMES = [
1990
+ cssVariablesTheme,
1991
+ cssVariablesTheme
1992
+ ];
1993
+ var SHIKI_TOKEN_VARS = cn6(
1994
+ "[--md-code-foreground:var(--foreground)]",
1995
+ "[--md-code-background:transparent]",
1996
+ "[--md-code-token-comment:var(--muted-foreground)]",
1997
+ "[--md-code-token-constant:var(--chart-1)]",
1998
+ "[--md-code-token-function:var(--chart-3)]",
1999
+ "[--md-code-token-keyword:var(--chart-4)]",
2000
+ "[--md-code-token-link:var(--primary)]",
2001
+ "[--md-code-token-parameter:var(--chart-5)]",
2002
+ "[--md-code-token-punctuation:var(--muted-foreground)]",
2003
+ "[--md-code-token-string-expression:var(--chart-2)]",
2004
+ "[--md-code-token-string:var(--chart-2)]"
2005
+ );
2006
+ var hasFontFlag = (fontStyle, flag) => ((fontStyle ?? 0) & flag) === flag;
2007
+ function tokenStyle(token) {
2008
+ return {
2009
+ // htmlStyle.color carries the theme var; token.color is the fallback path.
2010
+ color: token.htmlStyle?.color ?? token.color,
2011
+ fontStyle: hasFontFlag(token.fontStyle, 1) ? "italic" : void 0,
2012
+ fontWeight: hasFontFlag(token.fontStyle, 2) ? "bold" : void 0,
2013
+ textDecoration: hasFontFlag(token.fontStyle, 4) ? "underline" : void 0
2014
+ };
2015
+ }
2016
+ function useHighlightedTokens(codeText, language) {
2017
+ const [result, setResult] = useState4(null);
2018
+ const keyRef = useRef4({ codeText, language });
2019
+ if (keyRef.current.codeText !== codeText || keyRef.current.language !== language) {
2020
+ keyRef.current = { codeText, language };
2021
+ setResult(null);
2022
+ }
2023
+ useEffect4(() => {
2024
+ if (!language) return void 0;
2025
+ let cancelled = false;
2026
+ const sync = codeHighlighter.highlight(
2027
+ // Unknown languages fall back to "text" inside the plugin.
2028
+ { code: codeText, language, themes: SHIKI_THEMES },
2029
+ (r) => {
2030
+ if (!cancelled) setResult(r);
2031
+ }
2032
+ );
2033
+ if (sync && !cancelled) setResult(sync);
2034
+ return () => {
2035
+ cancelled = true;
2036
+ };
2037
+ }, [codeText, language]);
2038
+ return result;
2039
+ }
2040
+ function CodeFence({
2041
+ codeText,
2042
+ language,
2043
+ searchActive,
2044
+ className,
2045
+ children,
2046
+ ...props
2047
+ }) {
2048
+ const tokens = useHighlightedTokens(codeText, language)?.tokens ?? null;
2049
+ return /* @__PURE__ */ jsxs6(
2050
+ "div",
2051
+ {
2052
+ "data-code-fence": language ?? "",
2053
+ className: cn6("group/code-fence relative my-3", className),
2054
+ ...props,
2055
+ children: [
2056
+ /* @__PURE__ */ jsx6(
2057
+ "pre",
2058
+ {
2059
+ "data-search-active": searchActive ? "" : void 0,
2060
+ className: cn6(
2061
+ "!my-0 overflow-x-auto rounded-md p-3 font-mono text-code",
2062
+ SHIKI_TOKEN_VARS,
2063
+ searchActive ? "bg-primary/10" : "bg-surface-muted"
2064
+ ),
2065
+ children: tokens ? /* @__PURE__ */ jsx6("code", { children: tokens.map((line, lineIdx) => (
2066
+ // Lines are positionally stable for a given source string (the
2067
+ // whole list is rebuilt when `codeText` changes).
2068
+ /* @__PURE__ */ jsx6("span", { className: "block", children: line.length === 0 ? "\n" : line.map((token, tokenIdx) => /* @__PURE__ */ jsx6("span", { style: tokenStyle(token), children: token.content }, `token-${lineIdx}-${tokenIdx}`)) }, `line-${lineIdx}`)
2069
+ )) }) : children
2070
+ }
2071
+ ),
2072
+ /* @__PURE__ */ jsxs6("div", { className: "absolute end-2 top-2 flex items-center gap-1", children: [
2073
+ /* @__PURE__ */ jsx6(
2074
+ CopyButton,
2075
+ {
2076
+ value: codeText,
2077
+ label: false,
2078
+ size: "icon-sm",
2079
+ className: "opacity-0 transition-opacity duration-fast ease-standard focus-visible:opacity-100 group-hover/code-fence:opacity-100 motion-reduce:transition-none"
2080
+ }
2081
+ ),
2082
+ language ? /* @__PURE__ */ jsx6(
2083
+ "span",
2084
+ {
2085
+ "aria-hidden": "true",
2086
+ className: "pointer-events-none select-none rounded-sm bg-surface-muted px-1.5 py-0.5 font-mono text-meta text-muted-foreground",
2087
+ children: language
2088
+ }
2089
+ ) : null
2090
+ ] })
2091
+ ]
2092
+ }
2093
+ );
2094
+ }
2095
+
2096
+ // src/markdown-preview/markdown-preview.tsx
2097
+ import remarkMath from "remark-math";
2098
+
2099
+ // src/markdown-academic/citations.tsx
2100
+ import { cn as cn7 } from "@elabs-ai/components-ui/lib/cn";
2101
+ import { Separator } from "@elabs-ai/components-ui";
2102
+ import {
2103
+ createContext,
2104
+ forwardRef as forwardRef4,
2105
+ useContext,
2106
+ useId as useId2
2107
+ } from "react";
2108
+ import { visit as visit2 } from "unist-util-visit";
2109
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2110
+ var ITEM_RE = /^\s*([^@]*?)\s*(-)?@([\p{L}\d][\w:.#$%&+?<>~/-]*)\s*(.*)$/u;
2111
+ function parseItem(raw) {
2112
+ const m = ITEM_RE.exec(raw);
2113
+ if (!m) return null;
2114
+ const [, prefix, suppress, key, rest] = m;
2115
+ if (!key) return null;
2116
+ const locator = (rest ?? "").replace(/^\s*,\s*/, "").trim();
2117
+ const item = { key };
2118
+ if (suppress) item.suppressAuthor = true;
2119
+ if (prefix?.trim()) item.prefix = prefix.trim();
2120
+ if (locator) item.locator = locator;
2121
+ return item;
2122
+ }
2123
+ function parseCitationBracket(inner) {
2124
+ if (!inner.includes("@")) return null;
2125
+ const items = [];
2126
+ for (const part of inner.split(";")) {
2127
+ const item = parseItem(part);
2128
+ if (!item) return null;
2129
+ items.push(item);
2130
+ }
2131
+ return items.length > 0 ? items : null;
2132
+ }
2133
+ var BRACKET_RE = /(?<![\]!])\[([^[\]]+)\](?![([])/g;
2134
+ function collectCitations(markdown, resolve) {
2135
+ const byKey = /* @__PURE__ */ new Map();
2136
+ const order = [];
2137
+ let m;
2138
+ BRACKET_RE.lastIndex = 0;
2139
+ while ((m = BRACKET_RE.exec(markdown)) !== null) {
2140
+ const items = parseCitationBracket(m[1]);
2141
+ if (!items) continue;
2142
+ for (const { key } of items) {
2143
+ if (byKey.has(key)) continue;
2144
+ const data = resolve(key);
2145
+ const entry = { key, data };
2146
+ if (data) {
2147
+ entry.n = order.length + 1;
2148
+ order.push(entry);
2149
+ }
2150
+ byKey.set(key, entry);
2151
+ }
2152
+ }
2153
+ return { order, byKey };
2154
+ }
2155
+ var CITE_TAG = "brand-cite";
2156
+ var CITE_PROP = "dataCite";
2157
+ var CITE_ATTR = "data-cite";
2158
+ function remarkBrandCitations() {
2159
+ return (tree) => {
2160
+ visit2(tree, "text", (node, index, parent) => {
2161
+ const p = parent;
2162
+ const text = node.value;
2163
+ if (!p?.children || index == null || typeof text !== "string" || !text.includes("@")) return;
2164
+ const next = [];
2165
+ let last = 0;
2166
+ BRACKET_RE.lastIndex = 0;
2167
+ let m;
2168
+ while ((m = BRACKET_RE.exec(text)) !== null) {
2169
+ const items = parseCitationBracket(m[1]);
2170
+ if (!items) continue;
2171
+ if (m.index > last) next.push({ type: "text", value: text.slice(last, m.index) });
2172
+ const payload = { items, original: m[0] };
2173
+ next.push({
2174
+ type: "brandCite",
2175
+ data: { hName: CITE_TAG, hProperties: { [CITE_PROP]: JSON.stringify(payload) } }
2176
+ });
2177
+ last = m.index + m[0].length;
2178
+ }
2179
+ if (next.length === 0) return;
2180
+ if (last < text.length) next.push({ type: "text", value: text.slice(last) });
2181
+ p.children.splice(index, 1, ...next);
2182
+ return index + next.length;
2183
+ });
2184
+ };
2185
+ }
2186
+ var CitationContext = createContext(null);
2187
+ function CitationProvider({ byKey, order, style, children }) {
2188
+ return /* @__PURE__ */ jsx7(CitationContext.Provider, { value: { byKey, order, style }, children });
2189
+ }
2190
+ function hoverTitle(data) {
2191
+ if (data.formatted) return data.formatted;
2192
+ const parts = [
2193
+ data.author,
2194
+ data.year != null ? `(${data.year})` : void 0,
2195
+ data.title,
2196
+ data.container
2197
+ ].filter(Boolean);
2198
+ return parts.join(". ");
2199
+ }
2200
+ function CiteLink({ entry, label }) {
2201
+ const name = entry.data ? hoverTitle(entry.data) : entry.key;
2202
+ return /* @__PURE__ */ jsx7(
2203
+ "a",
2204
+ {
2205
+ href: `#ref-${cssId(entry.key)}`,
2206
+ title: entry.data ? hoverTitle(entry.data) : void 0,
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",
2209
+ children: label
2210
+ }
2211
+ );
2212
+ }
2213
+ function cssId(key) {
2214
+ return key.replace(/[^\w-]/g, "-");
2215
+ }
2216
+ function readCite(rest) {
2217
+ const raw = rest[CITE_ATTR] ?? rest[CITE_PROP];
2218
+ if (!raw) return null;
2219
+ try {
2220
+ return JSON.parse(raw);
2221
+ } catch {
2222
+ return null;
2223
+ }
2224
+ }
2225
+ function InlineCite({ node: _n, children: _c, ...rest }) {
2226
+ const ctx = useContext(CitationContext);
2227
+ const payload = readCite(rest);
2228
+ if (!payload) return null;
2229
+ if (!ctx) return /* @__PURE__ */ jsx7("span", { children: payload.original });
2230
+ const resolved = payload.items.map((it) => ({ it, entry: ctx.byKey.get(it.key) }));
2231
+ const anyResolved = resolved.some((r) => r.entry?.data);
2232
+ if (!anyResolved) {
2233
+ return /* @__PURE__ */ jsx7("span", { className: "text-muted-foreground", title: "Unresolved citation", children: payload.original });
2234
+ }
2235
+ const numeric = ctx.style === "numeric";
2236
+ const open = numeric ? "[" : "(";
2237
+ const close = numeric ? "]" : ")";
2238
+ const sep = numeric ? ", " : "; ";
2239
+ return /* @__PURE__ */ jsxs7("span", { className: "whitespace-nowrap text-meta tabular-nums", children: [
2240
+ open,
2241
+ resolved.map(({ it, entry }, i) => {
2242
+ const label = numeric ? numericLabel(it, entry) : authorYearLabel(it, entry);
2243
+ return /* @__PURE__ */ jsxs7("span", { children: [
2244
+ i > 0 ? sep : null,
2245
+ entry?.data ? /* @__PURE__ */ jsx7(CiteLink, { entry, label }) : /* @__PURE__ */ jsx7("span", { className: "text-muted-foreground", title: `Unresolved: @${it.key}`, children: label })
2246
+ ] }, `${it.key}-${i}`);
2247
+ }),
2248
+ close
2249
+ ] });
2250
+ }
2251
+ function numericLabel(it, entry) {
2252
+ if (!entry?.data || entry.n == null) return "?";
2253
+ return it.locator ? `${entry.n}, ${it.locator}` : String(entry.n);
2254
+ }
2255
+ function authorYearLabel(it, entry) {
2256
+ if (!entry?.data) return `@${it.key}?`;
2257
+ const d = entry.data;
2258
+ const head = it.suppressAuthor ? "" : d.author ? `${d.author} ` : "";
2259
+ const year = d.year != null ? String(d.year) : "";
2260
+ const core = `${head}${year}`.trim() || d.title || it.key;
2261
+ const prefixed = it.prefix ? `${it.prefix} ${core}` : core;
2262
+ return it.locator ? `${prefixed}, ${it.locator}` : prefixed;
2263
+ }
2264
+ function assembledReference(data) {
2265
+ if (data.formatted) return data.formatted;
2266
+ const parts = [
2267
+ data.author,
2268
+ data.year != null ? `(${data.year}).` : void 0,
2269
+ data.title ? `${data.title}.` : void 0,
2270
+ data.container ? `${data.container}.` : void 0
2271
+ ].filter(Boolean);
2272
+ return parts.join(" ");
2273
+ }
2274
+ function referenceHref(data) {
2275
+ if (data.url) return data.url;
2276
+ if (data.doi) return `https://doi.org/${data.doi}`;
2277
+ return void 0;
2278
+ }
2279
+ var Bibliography = forwardRef4(function Bibliography2({ entries, style, title = "References", className, ...props }, ref) {
2280
+ const ctx = useContext(CitationContext);
2281
+ const labelId = useId2();
2282
+ const list = entries ?? ctx?.order ?? [];
2283
+ const resolvedStyle = style ?? ctx?.style ?? "numeric";
2284
+ const numeric = resolvedStyle === "numeric";
2285
+ if (list.length === 0) return null;
2286
+ return /* @__PURE__ */ jsxs7("section", { ref, "aria-labelledby": labelId, className: cn7("mt-8", className), ...props, children: [
2287
+ /* @__PURE__ */ jsx7(Separator, { className: "mb-3" }),
2288
+ /* @__PURE__ */ jsx7("p", { id: labelId, className: "mb-2 text-meta font-medium text-muted-foreground", children: title }),
2289
+ /* @__PURE__ */ jsx7("ol", { className: "space-y-2", children: list.map((entry) => {
2290
+ const data = entry.data;
2291
+ if (!data) return null;
2292
+ const href = referenceHref(data);
2293
+ return /* @__PURE__ */ jsxs7(
2294
+ "li",
2295
+ {
2296
+ id: `ref-${cssId(entry.key)}`,
2297
+ className: "flex gap-2 text-caption text-foreground scroll-mt-4",
2298
+ children: [
2299
+ numeric && entry.n != null ? /* @__PURE__ */ jsxs7("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: [
2300
+ "[",
2301
+ entry.n,
2302
+ "]"
2303
+ ] }) : null,
2304
+ /* @__PURE__ */ jsxs7("span", { className: "min-w-0", children: [
2305
+ assembledReference(data),
2306
+ " ",
2307
+ href ? /* @__PURE__ */ jsx7(
2308
+ "a",
2309
+ {
2310
+ href,
2311
+ target: "_blank",
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",
2314
+ children: data.url ?? `doi:${data.doi}`
2315
+ }
2316
+ ) : null
2317
+ ] })
2318
+ ]
2319
+ },
2320
+ entry.key
2321
+ );
2322
+ }) })
2323
+ ] });
2324
+ });
2325
+
2326
+ // src/markdown-academic/footnotes.tsx
2327
+ import { Separator as Separator2 } from "@elabs-ai/components-ui";
2328
+ import { cn as cn8 } from "@elabs-ai/components-ui/lib/cn";
2329
+ import { forwardRef as forwardRef5, useId as useId3 } from "react";
2330
+ import { visit as visit3 } from "unist-util-visit";
2331
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2332
+ var FOOTNOTE_REF_TAG = "brand-footnote-ref";
2333
+ var FOOTNOTE_LIST_TAG = "brand-footnote-list";
2334
+ var FOOTNOTE_ITEM_TAG = "brand-footnote-item";
2335
+ var FOOTNOTE_PROP = "dataFn";
2336
+ var FOOTNOTE_ATTR = "data-fn";
2337
+ function payloadProps(payload) {
2338
+ return { [FOOTNOTE_PROP]: JSON.stringify(payload) };
2339
+ }
2340
+ function remarkBrandFootnotes() {
2341
+ return (tree) => {
2342
+ const root = tree;
2343
+ const defs = /* @__PURE__ */ new Map();
2344
+ visit3(
2345
+ root,
2346
+ "footnoteDefinition",
2347
+ (node, index, parent) => {
2348
+ if (!node.identifier || !parent?.children || index == null) return;
2349
+ defs.set(node.identifier, node);
2350
+ parent.children.splice(index, 1);
2351
+ return index;
2352
+ }
2353
+ );
2354
+ const numberOf = /* @__PURE__ */ new Map();
2355
+ const refsOf = /* @__PURE__ */ new Map();
2356
+ const order = [];
2357
+ visit3(root, "footnoteReference", (node, index, parent) => {
2358
+ if (!node.identifier || !parent?.children || index == null) return;
2359
+ const id = node.identifier;
2360
+ let n = numberOf.get(id);
2361
+ if (n == null) {
2362
+ n = order.length + 1;
2363
+ numberOf.set(id, n);
2364
+ order.push(id);
2365
+ }
2366
+ const refs = refsOf.get(id) ?? [];
2367
+ const refId = refs.length === 0 ? `fnref-${id}` : `fnref-${id}-${refs.length + 1}`;
2368
+ refs.push(refId);
2369
+ refsOf.set(id, refs);
2370
+ parent.children[index] = {
2371
+ type: "brandFootnoteRef",
2372
+ data: { hName: FOOTNOTE_REF_TAG, hProperties: payloadProps({ id, n, refId }) }
2373
+ };
2374
+ });
2375
+ if (order.length === 0) return;
2376
+ const items = order.map((id, i) => {
2377
+ const def = defs.get(id);
2378
+ const body = def?.children ? def.children.map((c) => structuredClone(c)) : [{ type: "paragraph", children: [{ type: "text", value: "Missing footnote." }] }];
2379
+ return {
2380
+ type: "brandFootnoteItem",
2381
+ data: {
2382
+ hName: FOOTNOTE_ITEM_TAG,
2383
+ hProperties: payloadProps({ id, n: i + 1, refs: refsOf.get(id) ?? [`fnref-${id}`] })
2384
+ },
2385
+ children: body
2386
+ };
2387
+ });
2388
+ root.children = root.children ?? [];
2389
+ root.children.push({
2390
+ type: "brandFootnoteList",
2391
+ data: { hName: FOOTNOTE_LIST_TAG },
2392
+ children: items
2393
+ });
2394
+ };
2395
+ }
2396
+ function readPayload(rest) {
2397
+ const raw = rest[FOOTNOTE_ATTR] ?? rest[FOOTNOTE_PROP];
2398
+ if (!raw) return null;
2399
+ try {
2400
+ return JSON.parse(raw);
2401
+ } catch {
2402
+ return null;
2403
+ }
2404
+ }
2405
+ function FootnoteRef({ node: _n, children: _c, ...rest }) {
2406
+ const payload = readPayload(rest);
2407
+ if (!payload) return null;
2408
+ const { id, n, refId } = payload;
2409
+ return /* @__PURE__ */ jsx8("sup", { className: "leading-none", children: /* @__PURE__ */ jsx8(
2410
+ "a",
2411
+ {
2412
+ id: refId ?? `fnref-${id}`,
2413
+ href: `#fn-${id}`,
2414
+ "data-footnote-ref": "",
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",
2417
+ children: n
2418
+ }
2419
+ ) });
2420
+ }
2421
+ function FootnoteItem({ node: _n, children, ...rest }) {
2422
+ const payload = readPayload(rest);
2423
+ if (!payload) return null;
2424
+ const { id, n, refs } = payload;
2425
+ const refList = refs && refs.length > 0 ? refs : [`fnref-${id}`];
2426
+ return /* @__PURE__ */ jsxs8(
2427
+ "li",
2428
+ {
2429
+ id: `fn-${id}`,
2430
+ className: "scroll-mt-4 ps-1 [&>p]:m-0 [&>p]:inline [&>p]:text-caption [&>p]:text-muted-foreground",
2431
+ children: [
2432
+ children,
2433
+ " ",
2434
+ refList.map((refId, i) => /* @__PURE__ */ jsxs8(
2435
+ "a",
2436
+ {
2437
+ href: `#${refId}`,
2438
+ "data-footnote-backref": "",
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",
2441
+ children: [
2442
+ /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "\u21A9" }),
2443
+ refList.length > 1 ? /* @__PURE__ */ jsx8("sub", { className: "ms-0.5 leading-none tabular-nums", children: i + 1 }) : null
2444
+ ]
2445
+ },
2446
+ refId
2447
+ ))
2448
+ ]
2449
+ }
2450
+ );
2451
+ }
2452
+ var FootnoteList = forwardRef5(function FootnoteList2({ className, children, ...props }, ref) {
2453
+ const labelId = useId3();
2454
+ return /* @__PURE__ */ jsxs8("section", { ref, "aria-labelledby": labelId, className: cn8("mt-8", className), ...props, children: [
2455
+ /* @__PURE__ */ jsx8(Separator2, { className: "mb-3" }),
2456
+ /* @__PURE__ */ jsx8("p", { id: labelId, className: "mb-2 text-meta font-medium text-muted-foreground", children: "Footnotes" }),
2457
+ /* @__PURE__ */ jsx8("ol", { className: "list-decimal space-y-1.5 ps-6 text-caption text-muted-foreground marker:text-muted-foreground", children })
2458
+ ] });
2459
+ });
2460
+
2461
+ // src/markdown-academic/math.tsx
2462
+ import { cn as cn9 } from "@elabs-ai/components-ui/lib/cn";
2463
+ import katex from "katex";
2464
+ import { useMemo as useMemo4 } from "react";
2465
+ import { visit as visit4 } from "unist-util-visit";
2466
+ import { jsx as jsx9 } from "react/jsx-runtime";
2467
+ var MATH_BLOCK_TAG = "brand-math";
2468
+ var MATH_INLINE_TAG = "brand-math-inline";
2469
+ var MATH_PROP = "dataTex";
2470
+ var MATH_ATTR = "data-tex";
2471
+ var MAX_EXPAND = 1e3;
2472
+ function remarkBrandMath() {
2473
+ return (tree) => {
2474
+ visit4(tree, (node, index, parent) => {
2475
+ if (node.type !== "inlineMath" && node.type !== "math") return;
2476
+ if (!parent?.children || index == null) return;
2477
+ const display = node.type === "math";
2478
+ const tex = typeof node.value === "string" ? node.value : "";
2479
+ parent.children[index] = {
2480
+ type: display ? "brandMathBlock" : "brandMathInline",
2481
+ data: {
2482
+ hName: display ? MATH_BLOCK_TAG : MATH_INLINE_TAG,
2483
+ hProperties: { [MATH_PROP]: tex }
2484
+ }
2485
+ };
2486
+ });
2487
+ };
2488
+ }
2489
+ function readTex(rest) {
2490
+ return rest[MATH_ATTR] ?? rest[MATH_PROP] ?? "";
2491
+ }
2492
+ function renderKatex(tex, displayMode) {
2493
+ try {
2494
+ return {
2495
+ html: katex.renderToString(tex, {
2496
+ displayMode,
2497
+ throwOnError: false,
2498
+ errorColor: "var(--destructive)",
2499
+ trust: false,
2500
+ maxExpand: MAX_EXPAND,
2501
+ strict: "ignore",
2502
+ output: "htmlAndMathml"
2503
+ }),
2504
+ error: false
2505
+ };
2506
+ } catch {
2507
+ return { html: "", error: true };
2508
+ }
2509
+ }
2510
+ function MathInline({ tex, className, ...props }) {
2511
+ const { html, error } = useMemo4(() => renderKatex(tex, false), [tex]);
2512
+ if (error) {
2513
+ return /* @__PURE__ */ jsx9(
2514
+ "code",
2515
+ {
2516
+ className: cn9("text-destructive-text", className),
2517
+ "aria-label": `Math (could not render): ${tex}`,
2518
+ title: "Could not render math",
2519
+ ...props,
2520
+ children: tex
2521
+ }
2522
+ );
2523
+ }
2524
+ return /* @__PURE__ */ jsx9(
2525
+ "span",
2526
+ {
2527
+ role: "math",
2528
+ "aria-label": tex,
2529
+ className: cn9("inline-block align-middle", className),
2530
+ dangerouslySetInnerHTML: { __html: html },
2531
+ ...props
2532
+ }
2533
+ );
2534
+ }
2535
+ function MathBlock({ tex, className, ...props }) {
2536
+ const { html, error } = useMemo4(() => renderKatex(tex, true), [tex]);
2537
+ if (error) {
2538
+ return /* @__PURE__ */ jsx9(
2539
+ "pre",
2540
+ {
2541
+ className: cn9(
2542
+ "overflow-x-auto rounded-md bg-surface-muted p-3 text-destructive-text",
2543
+ className
2544
+ ),
2545
+ "aria-label": `Math (could not render): ${tex}`,
2546
+ title: "Could not render math",
2547
+ ...props,
2548
+ children: /* @__PURE__ */ jsx9("code", { children: tex })
2549
+ }
2550
+ );
2551
+ }
2552
+ return /* @__PURE__ */ jsx9(
2553
+ "div",
2554
+ {
2555
+ role: "math",
2556
+ "aria-label": tex,
2557
+ className: cn9("my-3 overflow-x-auto text-center", className),
2558
+ dangerouslySetInnerHTML: { __html: html },
2559
+ ...props
2560
+ }
2561
+ );
2562
+ }
2563
+ function MathInlineTag({ node: _n, children: _c, ...rest }) {
2564
+ return /* @__PURE__ */ jsx9(MathInline, { tex: readTex(rest) });
2565
+ }
2566
+ function MathBlockTag({ node: _n, children: _c, ...rest }) {
2567
+ return /* @__PURE__ */ jsx9(MathBlock, { tex: readTex(rest) });
2568
+ }
2569
+
2570
+ // src/markdown-academic/toc.tsx
2571
+ import { cn as cn10 } from "@elabs-ai/components-ui/lib/cn";
2572
+ import { createContext as createContext2, forwardRef as forwardRef6, useContext as useContext2 } from "react";
2573
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2574
+ var INDENT = ["ps-0", "ps-3", "ps-6", "ps-9", "ps-12", "ps-12"];
2575
+ var TocContext = createContext2(null);
2576
+ function TocProvider({ items, children }) {
2577
+ const idByLine = /* @__PURE__ */ new Map();
2578
+ for (const it of items) idByLine.set(it.line, it.id);
2579
+ return /* @__PURE__ */ jsx10(TocContext.Provider, { value: { items, idByLine }, children });
2580
+ }
2581
+ function useHeadingId(line) {
2582
+ const ctx = useContext2(TocContext);
2583
+ if (line == null || !ctx) return void 0;
2584
+ return ctx.idByLine.get(line);
2585
+ }
2586
+ var TableOfContents = forwardRef6(
2587
+ function TableOfContents2({ items, title = "Contents", maxLevel = 3, className, ...props }, ref) {
2588
+ const ctx = useContext2(TocContext);
2589
+ const source = items ?? ctx?.items ?? [];
2590
+ const list = source.filter((it) => it.level <= maxLevel);
2591
+ if (list.length === 0) return null;
2592
+ const minLevel = Math.min(...list.map((it) => it.level));
2593
+ return /* @__PURE__ */ jsxs9("nav", { ref, "aria-label": title, className: cn10("my-4 text-meta", className), ...props, children: [
2594
+ /* @__PURE__ */ jsx10("p", { className: "mb-2 font-medium text-muted-foreground", children: title }),
2595
+ /* @__PURE__ */ jsx10("ol", { className: "space-y-1", children: list.map((it) => /* @__PURE__ */ jsx10("li", { className: INDENT[Math.min(it.level - minLevel, INDENT.length - 1)], children: /* @__PURE__ */ jsx10(
2596
+ "a",
2597
+ {
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",
2600
+ children: it.text
2601
+ }
2602
+ ) }, it.id)) })
2603
+ ] });
2604
+ }
2605
+ );
2606
+
2607
+ // src/markdown-iteration/directive.tsx
2608
+ import { createContext as createContext3, useContext as useContext3 } from "react";
2609
+ import { jsx as jsx11 } from "react/jsx-runtime";
2610
+ var MAX_ITERATION_DEPTH = 3;
2611
+ var IterationDepthContext = createContext3(0);
2612
+ var DEFAULT_LAYOUT = {
2613
+ iterate: "stacked",
2614
+ pivot: "matrix"
2615
+ };
2616
+ var LAYOUTS = /* @__PURE__ */ new Set(["stacked", "grid", "matrix", "bento"]);
2617
+ function specFromDirective(name, attributes, rawBody) {
2618
+ const kind = name;
2619
+ const layoutAttr = attributes.layout;
2620
+ const layout = layoutAttr && LAYOUTS.has(layoutAttr) ? layoutAttr : DEFAULT_LAYOUT[kind];
2621
+ const columns = attributes.columns ? Number(attributes.columns) || void 0 : void 0;
2622
+ return {
2623
+ kind,
2624
+ layout,
2625
+ template: rawBody ?? "",
2626
+ as: attributes.as?.trim() || "item",
2627
+ source: attributes.source,
2628
+ rows: attributes.rows,
2629
+ cols: attributes.cols,
2630
+ columns,
2631
+ attributes
2632
+ };
2633
+ }
2634
+ function IterationDirective({
2635
+ spec,
2636
+ evaluate,
2637
+ interpolate,
2638
+ renderCell
2639
+ }) {
2640
+ const depth = useContext3(IterationDepthContext);
2641
+ if (depth >= MAX_ITERATION_DEPTH) {
2642
+ return /* @__PURE__ */ jsx11("div", { className: "my-4 text-meta text-muted-foreground italic", "data-iteration-too-deep": "", children: "Iteration nested too deep \u2014 skipped." });
2643
+ }
2644
+ return /* @__PURE__ */ jsx11(IterationDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsx11(
2645
+ IterationBlock,
2646
+ {
2647
+ spec,
2648
+ evaluate,
2649
+ interpolate,
2650
+ render: renderCell
2651
+ }
2652
+ ) });
2653
+ }
2654
+
2655
+ // src/markdown-preview/markdown-preview.tsx
2656
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2657
+ var baseRemarkPlugins = Object.values(defaultRemarkPlugins);
2658
+ var BRAND_TRANSCLUSION_TAG = "brand-transclusion";
2659
+ var BRAND_TRANSCLUSION_ATTR = "data-transclusion";
2660
+ var BRAND_TRANSCLUSION_PROP = "dataTransclusion";
2661
+ var allowedTags = {
2662
+ [BRAND_DIRECTIVE_TAG]: [BRAND_DIRECTIVE_PROP],
2663
+ [BRAND_DIRECTIVE_INLINE_TAG]: [BRAND_DIRECTIVE_PROP],
2664
+ [BRAND_TRANSCLUSION_TAG]: [BRAND_TRANSCLUSION_PROP],
2665
+ // Academic layer (footnotes / math / citations) — opt-in via props, but the
2666
+ // tags are always allow-listed (harmless when the feature is off).
2667
+ [FOOTNOTE_REF_TAG]: [FOOTNOTE_PROP],
2668
+ [FOOTNOTE_ITEM_TAG]: [FOOTNOTE_PROP],
2669
+ [FOOTNOTE_LIST_TAG]: [],
2670
+ [MATH_BLOCK_TAG]: [MATH_PROP],
2671
+ [MATH_INLINE_TAG]: [MATH_PROP],
2672
+ [CITE_TAG]: [CITE_PROP]
2673
+ };
2674
+ var ACADEMIC_TAGS = [
2675
+ FOOTNOTE_REF_TAG,
2676
+ FOOTNOTE_ITEM_TAG,
2677
+ FOOTNOTE_LIST_TAG,
2678
+ MATH_BLOCK_TAG,
2679
+ MATH_INLINE_TAG,
2680
+ CITE_TAG
2681
+ ];
2682
+ var ACADEMIC_TAG_ATTRS = {
2683
+ [FOOTNOTE_REF_TAG]: [FOOTNOTE_PROP],
2684
+ [FOOTNOTE_ITEM_TAG]: [FOOTNOTE_PROP],
2685
+ [FOOTNOTE_LIST_TAG]: [],
2686
+ [MATH_BLOCK_TAG]: [MATH_PROP],
2687
+ [MATH_INLINE_TAG]: [MATH_PROP],
2688
+ [CITE_TAG]: [CITE_PROP]
2689
+ };
2690
+ var rehypePlugins = (() => {
2691
+ const defaults = defaultRehypePlugins;
2692
+ const sanitize = defaults.sanitize;
2693
+ const schema = sanitize[1] ?? {};
2694
+ const protocols = schema.protocols ?? {};
2695
+ const extendedSanitize = [
2696
+ sanitize[0],
2697
+ {
2698
+ ...schema,
2699
+ protocols: { ...protocols, src: [...protocols.src ?? ["http", "https"], "data", "blob"] },
2700
+ // Custom rehypePlugins bypass Streamdown's `allowedTags` merge — so the brand-directive
2701
+ // tags, the transclusion tag, and their JSON payload properties all go into the schema here.
2702
+ tagNames: [
2703
+ ...schema.tagNames ?? [],
2704
+ BRAND_DIRECTIVE_TAG,
2705
+ BRAND_DIRECTIVE_INLINE_TAG,
2706
+ BRAND_TRANSCLUSION_TAG,
2707
+ ...ACADEMIC_TAGS
2708
+ ],
2709
+ attributes: {
2710
+ ...schema.attributes ?? {},
2711
+ [BRAND_DIRECTIVE_TAG]: [BRAND_DIRECTIVE_PROP],
2712
+ [BRAND_DIRECTIVE_INLINE_TAG]: [BRAND_DIRECTIVE_PROP],
2713
+ [BRAND_TRANSCLUSION_TAG]: [BRAND_TRANSCLUSION_PROP],
2714
+ ...ACADEMIC_TAG_ATTRS
2715
+ }
2716
+ }
2717
+ ];
2718
+ return [defaults.raw, extendedSanitize, defaults.harden];
2719
+ })();
2720
+ var singleBlock = (md) => [md];
2721
+ var AnnotationsContext = createContext4([]);
2722
+ var SearchContext = createContext4({ lines: [] });
2723
+ var HeadingActionsContext = createContext4(
2724
+ null
2725
+ );
2726
+ var EMPTY_REGISTRY = { directives: /* @__PURE__ */ new Map(), fences: /* @__PURE__ */ new Map() };
2727
+ var RegistryContext = createContext4(EMPTY_REGISTRY);
2728
+ var LinkPreviewContext = createContext4(
2729
+ null
2730
+ );
2731
+ var TransclusionResolverContext = createContext4(null);
2732
+ var TRANSCLUSION_MAX_DEPTH = 3;
2733
+ var TransclusionDepthContext = createContext4(0);
2734
+ function flattenNodeText(node) {
2735
+ if (typeof node === "string" || typeof node === "number") return String(node);
2736
+ if (Array.isArray(node)) return node.map((n) => flattenNodeText(n)).join("");
2737
+ if (isValidElement(node)) {
2738
+ return flattenNodeText(node.props.children);
2739
+ }
2740
+ return "";
2741
+ }
2742
+ function remarkResolveUrls(resolve) {
2743
+ return function attacher() {
2744
+ return (tree) => {
2745
+ visit5(tree, (node) => {
2746
+ const n = node;
2747
+ if (n.type === "image" || n.type === "imageReference") {
2748
+ if (typeof n.url === "string") n.url = resolve(n.url, "image");
2749
+ } else if (n.type === "link" || n.type === "definition") {
2750
+ if (typeof n.url === "string") n.url = resolve(n.url, "link");
2751
+ }
2752
+ });
2753
+ };
2754
+ };
2755
+ }
2756
+ function remarkResolveWikilinks(resolve) {
2757
+ const WIKILINK_RE = /(?<!!)\[\[([^\]]+)\]\]/g;
2758
+ return function attacher() {
2759
+ return (tree) => {
2760
+ visit5(tree, "text", (node, index, parent) => {
2761
+ const n = node;
2762
+ const p = parent;
2763
+ if (!p?.children || index == null || typeof n.value !== "string") return;
2764
+ const text = n.value;
2765
+ if (!text.includes("[[")) return;
2766
+ const newChildren = [];
2767
+ let lastIndex = 0;
2768
+ WIKILINK_RE.lastIndex = 0;
2769
+ let match;
2770
+ while ((match = WIKILINK_RE.exec(text)) !== null) {
2771
+ if (match.index > lastIndex) {
2772
+ newChildren.push({ type: "text", value: text.slice(lastIndex, match.index) });
2773
+ }
2774
+ const inner = match[1];
2775
+ const pipeIdx = inner.indexOf("|");
2776
+ const targetPart = pipeIdx !== -1 ? inner.slice(0, pipeIdx) : inner;
2777
+ const alias = pipeIdx !== -1 ? inner.slice(pipeIdx + 1) : void 0;
2778
+ const hashIdx = targetPart.indexOf("#");
2779
+ const target = hashIdx !== -1 ? targetPart.slice(0, hashIdx) : targetPart;
2780
+ const anchor = hashIdx !== -1 ? targetPart.slice(hashIdx + 1) : void 0;
2781
+ const opts = anchor ? { anchor } : {};
2782
+ const href = resolve(target.trim(), opts);
2783
+ const linkText = alias?.trim() || target.trim();
2784
+ if (href === null) {
2785
+ newChildren.push({ type: "text", value: match[0] });
2786
+ } else {
2787
+ newChildren.push({
2788
+ type: "link",
2789
+ url: href,
2790
+ title: null,
2791
+ children: [{ type: "text", value: linkText }]
2792
+ });
2793
+ }
2794
+ lastIndex = match.index + match[0].length;
2795
+ }
2796
+ if (lastIndex < text.length) {
2797
+ newChildren.push({ type: "text", value: text.slice(lastIndex) });
2798
+ }
2799
+ if (newChildren.length > 0) {
2800
+ p.children.splice(index, 1, ...newChildren);
2801
+ return index + newChildren.length;
2802
+ }
2803
+ });
2804
+ };
2805
+ };
2806
+ }
2807
+ function remarkResolveTransclusions() {
2808
+ const STANDALONE_RE = /^!\[\[([^\]]+)\]\]$/;
2809
+ return function attacher() {
2810
+ return (tree) => {
2811
+ visit5(tree, "paragraph", (node) => {
2812
+ const n = node;
2813
+ if (!n.children || n.children.length !== 1) return;
2814
+ const child = n.children[0];
2815
+ if (child.type !== "text" || typeof child.value !== "string") return;
2816
+ const match = child.value.trim().match(STANDALONE_RE);
2817
+ if (!match) return;
2818
+ const inner = match[1];
2819
+ const hashIdx = inner.indexOf("#");
2820
+ const target = (hashIdx !== -1 ? inner.slice(0, hashIdx) : inner).trim();
2821
+ const section = hashIdx !== -1 ? inner.slice(hashIdx + 1).trim() : void 0;
2822
+ const payload = section ? { target, section } : { target };
2823
+ const data = n.data ?? (n.data = {});
2824
+ data.hName = BRAND_TRANSCLUSION_TAG;
2825
+ data.hProperties = { [BRAND_TRANSCLUSION_PROP]: JSON.stringify(payload) };
2826
+ n.children = [];
2827
+ });
2828
+ };
2829
+ };
2830
+ }
2831
+ function nestedItemContains(node, line) {
2832
+ const kids = node?.children ?? [];
2833
+ for (const kid of kids) {
2834
+ const el = kid;
2835
+ if (el.tagName === "li") {
2836
+ const pos = getSourcePos(kid);
2837
+ if (pos && line >= pos.start && line <= pos.end) return true;
2838
+ }
2839
+ if (nestedItemContains(kid, line)) return true;
2840
+ }
2841
+ return false;
2842
+ }
2843
+ function getSourcePos(node) {
2844
+ const pos = node?.position;
2845
+ if (typeof pos?.start?.line !== "number") return void 0;
2846
+ return { start: pos.start.line, end: pos.end?.line ?? pos.start.line };
2847
+ }
2848
+ function RemovedMarker({ count }) {
2849
+ return /* @__PURE__ */ jsxs10(
2850
+ "div",
2851
+ {
2852
+ role: "note",
2853
+ "aria-label": `${count} ${count === 1 ? "line" : "lines"} removed here`,
2854
+ className: "flex items-center gap-2 text-meta text-destructive-text",
2855
+ children: [
2856
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "font-mono", children: "\u2212" }),
2857
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "flex-1 border-t border-dashed border-destructive/40" }),
2858
+ /* @__PURE__ */ jsxs10("span", { children: [
2859
+ count,
2860
+ " ",
2861
+ count === 1 ? "line" : "lines",
2862
+ " removed"
2863
+ ] }),
2864
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "flex-1 border-t border-dashed border-destructive/40" })
2865
+ ]
2866
+ }
2867
+ );
2868
+ }
2869
+ function annotated(render, searchWash = true) {
2870
+ return function AnnotatedBlock(props) {
2871
+ const annotations = useContext4(AnnotationsContext);
2872
+ const search = useContext4(SearchContext);
2873
+ const pos = getSourcePos(props.node);
2874
+ const enriched = pos ? { ...props, "data-sourcepos": `${pos.start}:${pos.end}` } : props;
2875
+ const wash = pos && annotations.length > 0 ? annotationForRange(annotations, pos.start, pos.end) : void 0;
2876
+ const removed = pos && annotations.length > 0 ? removedMarkerAt(annotations, pos.start) : void 0;
2877
+ const activeSearch = searchWash && pos != null && search.activeLine != null && search.activeLine >= pos.start && search.activeLine <= pos.end;
2878
+ let content = render(enriched);
2879
+ if (!wash && !removed && !activeSearch) return content;
2880
+ if (wash) {
2881
+ content = /* @__PURE__ */ jsx12(
2882
+ "div",
2883
+ {
2884
+ "data-annotation": wash.kind,
2885
+ className: "border-s-2 border-s-success bg-success/10 py-1.5 pe-2 ps-3",
2886
+ children: content
2887
+ }
2888
+ );
2889
+ }
2890
+ if (activeSearch) {
2891
+ content = /* @__PURE__ */ jsx12("div", { "data-search-active": "", className: "-mx-2 rounded-md bg-primary/10 px-2 py-1", children: content });
2892
+ }
2893
+ return /* @__PURE__ */ jsxs10(Fragment2, { children: [
2894
+ removed ? /* @__PURE__ */ jsx12(RemovedMarker, { count: removed.removedCount ?? 1 }) : null,
2895
+ content
2896
+ ] });
2897
+ };
2898
+ }
2899
+ function heading(level) {
2900
+ return function HeadingMd({ node: _n, children, ...rest }) {
2901
+ const headingActions = useContext4(HeadingActionsContext);
2902
+ const start = rest["data-sourcepos"]?.split(":")[0];
2903
+ const line = start ? Number(start) : void 0;
2904
+ const headingId = useHeadingId(line);
2905
+ const slot = headingActions?.({
2906
+ level,
2907
+ text: flattenNodeText(children),
2908
+ line
2909
+ });
2910
+ return /* @__PURE__ */ jsxs10(
2911
+ ProseHeading,
2912
+ {
2913
+ level,
2914
+ id: headingId,
2915
+ ...rest,
2916
+ className: cn11(
2917
+ slot ? "group/heading" : void 0,
2918
+ headingId ? "scroll-mt-4" : void 0,
2919
+ rest.className
2920
+ ),
2921
+ children: [
2922
+ children,
2923
+ slot ? /* @__PURE__ */ jsx12(
2924
+ "span",
2925
+ {
2926
+ className: "ms-1.5 inline-flex align-middle opacity-0 transition-opacity duration-fast ease-standard focus-within:opacity-100 group-hover/heading:opacity-100 has-[[aria-pressed=true]]:opacity-100 motion-reduce:transition-none",
2927
+ children: slot
2928
+ }
2929
+ ) : null
2930
+ ]
2931
+ }
2932
+ );
2933
+ };
2934
+ }
2935
+ var CALLOUT_VARIANT = {
2936
+ info: "info",
2937
+ note: "info",
2938
+ tip: "success",
2939
+ success: "success",
2940
+ warning: "warning",
2941
+ caution: "warning",
2942
+ danger: "destructive",
2943
+ error: "destructive",
2944
+ destructive: "destructive"
2945
+ };
2946
+ var TIMELINE_STATUS = {
2947
+ done: "done",
2948
+ complete: "done",
2949
+ completed: "done",
2950
+ active: "active",
2951
+ current: "active",
2952
+ pending: "pending",
2953
+ todo: "pending"
2954
+ };
2955
+ function UnknownBlock({ name }) {
2956
+ return /* @__PURE__ */ jsxs10(Alert, { variant: "destructive", children: [
2957
+ /* @__PURE__ */ jsxs10(AlertTitle, { children: [
2958
+ "Unknown block: ",
2959
+ name
2960
+ ] }),
2961
+ /* @__PURE__ */ jsxs10(AlertDescription, { children: [
2962
+ "No renderer is mapped for ",
2963
+ /* @__PURE__ */ jsxs10("code", { children: [
2964
+ ":::",
2965
+ name
2966
+ ] }),
2967
+ ". Add it to the brand directive registry, or fix the directive name."
2968
+ ] })
2969
+ ] });
2970
+ }
2971
+ function readDirectivePayload(rest) {
2972
+ const raw = rest[BRAND_DIRECTIVE_ATTR] ?? rest.dataBrand;
2973
+ if (!raw) return null;
2974
+ try {
2975
+ return JSON.parse(raw);
2976
+ } catch {
2977
+ return "malformed";
2978
+ }
2979
+ }
2980
+ function BrandDirective({ node: _n, children, ...rest }) {
2981
+ const registry = useContext4(RegistryContext);
2982
+ const payload = readDirectivePayload(rest);
2983
+ if (payload === null) return null;
2984
+ if (payload === "malformed") return /* @__PURE__ */ jsx12(UnknownBlock, { name: "malformed" });
2985
+ if (!payload.known) return /* @__PURE__ */ jsx12(UnknownBlock, { name: payload.name });
2986
+ const attrs = payload.attributes ?? {};
2987
+ switch (payload.name) {
2988
+ case "card":
2989
+ return /* @__PURE__ */ jsxs10(Card, { children: [
2990
+ attrs.title ? /* @__PURE__ */ jsx12(CardHeader, { children: /* @__PURE__ */ jsx12(CardTitle, { children: attrs.title }) }) : null,
2991
+ /* @__PURE__ */ jsx12(CardContent, { className: cn11(!attrs.title && "pt-6"), children })
2992
+ ] });
2993
+ case "callout":
2994
+ return /* @__PURE__ */ jsxs10(Alert, { variant: CALLOUT_VARIANT[attrs.type ?? ""] ?? "default", children: [
2995
+ attrs.title ? /* @__PURE__ */ jsx12("div", { className: "mb-1 font-medium leading-none tracking-tight", children: attrs.title }) : null,
2996
+ /* @__PURE__ */ jsx12(AlertDescription, { children })
2997
+ ] });
2998
+ case "metric":
2999
+ return /* @__PURE__ */ jsx12(
3000
+ MetricBlock,
3001
+ {
3002
+ label: attrs.label ?? "",
3003
+ value: attrs.value ?? "",
3004
+ description: attrs.description,
3005
+ delta: attrs.delta,
3006
+ deltaDirection: attrs.delta?.startsWith("+") ? "up" : attrs.delta?.startsWith("-") ? "down" : "neutral"
3007
+ }
3008
+ );
3009
+ case "timeline":
3010
+ return /* @__PURE__ */ jsx12(
3011
+ Timeline,
3012
+ {
3013
+ items: (payload.items ?? []).map((it) => ({
3014
+ title: it.title,
3015
+ status: TIMELINE_STATUS[it.status] ?? "pending"
3016
+ }))
3017
+ }
3018
+ );
3019
+ default: {
3020
+ const renderer = registry.directives.get(payload.name);
3021
+ if (renderer && (!renderer.kinds || renderer.kinds.includes(payload.kind))) {
3022
+ return /* @__PURE__ */ jsx12(Fragment2, { children: renderer.render({
3023
+ name: payload.name,
3024
+ kind: payload.kind,
3025
+ attributes: attrs,
3026
+ children,
3027
+ textValue: payload.label,
3028
+ rawBody: payload.body
3029
+ }) });
3030
+ }
3031
+ return /* @__PURE__ */ jsx12(UnknownBlock, { name: payload.name });
3032
+ }
3033
+ }
3034
+ }
3035
+ function BrandInlineDirective({ node: _n, children, ...rest }) {
3036
+ const registry = useContext4(RegistryContext);
3037
+ const payload = readDirectivePayload(rest);
3038
+ if (payload === null || payload === "malformed") return /* @__PURE__ */ jsx12(Fragment2, { children });
3039
+ const renderer = registry.directives.get(payload.name);
3040
+ if (!renderer || renderer.kinds && !renderer.kinds.includes("inline")) {
3041
+ return /* @__PURE__ */ jsx12(Fragment2, { children });
3042
+ }
3043
+ return /* @__PURE__ */ jsx12(Fragment2, { children: renderer.render({
3044
+ name: payload.name,
3045
+ kind: "inline",
3046
+ attributes: payload.attributes ?? {},
3047
+ children,
3048
+ textValue: payload.label
3049
+ }) });
3050
+ }
3051
+ function fenceText(children) {
3052
+ if (typeof children === "string") return children;
3053
+ if (Array.isArray(children)) return children.map((c) => fenceText(c)).join("");
3054
+ return "";
3055
+ }
3056
+ function isMermaidCodeElement(child) {
3057
+ return isValidElement(child) && /\blanguage-mermaid\b/.test(child.props?.className ?? "");
3058
+ }
3059
+ function PreBlock({ node, children, ...rest }) {
3060
+ const search = useContext4(SearchContext);
3061
+ const registry = useContext4(RegistryContext);
3062
+ const pos = getSourcePos(node);
3063
+ const activeInBlock = pos != null && search.activeLine != null && search.activeLine >= pos.start && search.activeLine <= pos.end;
3064
+ const list = Array.isArray(children) ? children : [children];
3065
+ const mermaidChild = list.find(isMermaidCodeElement);
3066
+ if (mermaidChild) {
3067
+ const chart = fenceText(mermaidChild.props.children).replace(
3068
+ /\n$/,
3069
+ ""
3070
+ );
3071
+ return /* @__PURE__ */ jsx12(
3072
+ MermaidDiagram,
3073
+ {
3074
+ chart,
3075
+ "data-sourcepos": pos ? `${pos.start}:${pos.end}` : void 0,
3076
+ highlightTerm: search.term,
3077
+ activeText: activeInBlock && search.activeLine != null ? search.lines[search.activeLine - 1] : void 0
3078
+ }
3079
+ );
3080
+ }
3081
+ const codeEl = list.find(isValidElement);
3082
+ const fenceLang = fenceLanguage(codeEl?.props.className);
3083
+ const fenceRenderer = fenceLang ? registry.fences.get(fenceLang) : void 0;
3084
+ if (fenceLang && fenceRenderer && codeEl) {
3085
+ const source = fenceText(codeEl.props.children).replace(/\n$/, "");
3086
+ const rendered = fenceRenderer.render({ source, lang: fenceLang });
3087
+ return pos ? /* @__PURE__ */ jsx12("div", { "data-sourcepos": `${pos.start}:${pos.end}`, children: rendered }) : /* @__PURE__ */ jsx12(Fragment2, { children: rendered });
3088
+ }
3089
+ const codeText = fenceText(codeEl ? codeEl.props.children : children).replace(
3090
+ /\n$/,
3091
+ ""
3092
+ );
3093
+ return /* @__PURE__ */ jsx12(
3094
+ CodeFence,
3095
+ {
3096
+ ...rest,
3097
+ codeText,
3098
+ language: fenceLang,
3099
+ searchActive: activeInBlock,
3100
+ children
3101
+ }
3102
+ );
3103
+ }
3104
+ function ImageMd({ node: _n, src, alt, ...rest }) {
3105
+ return /* @__PURE__ */ jsx12(
3106
+ "img",
3107
+ {
3108
+ src,
3109
+ alt: alt ?? "",
3110
+ loading: "lazy",
3111
+ className: "max-w-full rounded-md border border-border",
3112
+ ...rest
3113
+ }
3114
+ );
3115
+ }
3116
+ function LinkMd({ node: _n, href, children, ...rest }) {
3117
+ const renderLinkPreview = useContext4(LinkPreviewContext);
3118
+ const anchor = /* @__PURE__ */ jsx12(ProseLink, { href, ...rest, children });
3119
+ if (renderLinkPreview && typeof href === "string") {
3120
+ return /* @__PURE__ */ jsx12(Fragment2, { children: renderLinkPreview(href, anchor) });
3121
+ }
3122
+ return anchor;
3123
+ }
3124
+ function TransclusionBlock({ node: _n, ...rest }) {
3125
+ const resolveTransclusion = useContext4(TransclusionResolverContext);
3126
+ const depth = useContext4(TransclusionDepthContext);
3127
+ const linkPreview = useContext4(LinkPreviewContext);
3128
+ const rawAttr = rest[BRAND_TRANSCLUSION_ATTR] ?? rest.dataTransclusion;
3129
+ if (!rawAttr || !resolveTransclusion) {
3130
+ return /* @__PURE__ */ jsx12("span", { children: rawAttr ? `![[${JSON.parse(rawAttr).target}]]` : null });
3131
+ }
3132
+ let payload;
3133
+ try {
3134
+ payload = JSON.parse(rawAttr);
3135
+ } catch {
3136
+ return null;
3137
+ }
3138
+ const { target, section } = payload;
3139
+ const label = section ? `${target}#${section}` : target;
3140
+ if (depth >= TRANSCLUSION_MAX_DEPTH) {
3141
+ return /* @__PURE__ */ jsxs10(
3142
+ "figure",
3143
+ {
3144
+ "aria-label": `Embedded: ${label}`,
3145
+ className: "my-3 rounded-md border-s-2 border-s-muted bg-muted/40 px-4 py-3",
3146
+ "data-testid": "transclusion-block",
3147
+ "data-transclusion-depth": depth,
3148
+ children: [
3149
+ /* @__PURE__ */ jsx12("figcaption", { className: "mb-1 text-meta text-muted-foreground", children: label }),
3150
+ /* @__PURE__ */ jsx12("p", { className: "text-meta text-muted-foreground italic", children: "Transclusion too deep \u2014 embed skipped." })
3151
+ ]
3152
+ }
3153
+ );
3154
+ }
3155
+ const content = resolveTransclusion(target, section ? { section } : {});
3156
+ if (content === null) {
3157
+ return /* @__PURE__ */ jsx12("span", { children: `![[${label}]]` });
3158
+ }
3159
+ return /* @__PURE__ */ jsx12(TransclusionDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsx12(LinkPreviewContext.Provider, { value: linkPreview, children: /* @__PURE__ */ jsx12(RecursiveTransclusionContent, { target, label, content }) }) });
3160
+ }
3161
+ function RecursiveTransclusionContent({
3162
+ target: _target,
3163
+ label,
3164
+ content
3165
+ }) {
3166
+ const plugins = useMemo5(() => {
3167
+ return [...baseRemarkPlugins, ...buildMarkdownPlugins()];
3168
+ }, []);
3169
+ return /* @__PURE__ */ jsxs10(
3170
+ "figure",
3171
+ {
3172
+ "aria-label": `Embedded: ${label}`,
3173
+ className: "my-3 rounded-md border-s-2 border-s-muted bg-muted/40 px-4 py-2",
3174
+ "data-testid": "transclusion-block",
3175
+ children: [
3176
+ /* @__PURE__ */ jsx12("figcaption", { className: "mb-1.5 text-meta text-muted-foreground", children: label }),
3177
+ /* @__PURE__ */ jsx12("div", { className: "text-body text-foreground", children: /* @__PURE__ */ jsx12(
3178
+ Streamdown,
3179
+ {
3180
+ parseMarkdownIntoBlocksFn: singleBlock,
3181
+ remarkPlugins: plugins,
3182
+ rehypePlugins,
3183
+ allowedTags,
3184
+ components,
3185
+ children: content
3186
+ }
3187
+ ) })
3188
+ ]
3189
+ }
3190
+ );
3191
+ }
3192
+ var components = {
3193
+ h1: annotated(heading(1)),
3194
+ h2: annotated(heading(2)),
3195
+ h3: annotated(heading(3)),
3196
+ h4: annotated(heading(4)),
3197
+ h5: annotated(heading(5)),
3198
+ h6: annotated(heading(6)),
3199
+ p: annotated(({ node: _n, ...p }) => /* @__PURE__ */ jsx12(ProseText, { ...p })),
3200
+ a: LinkMd,
3201
+ img: ImageMd,
3202
+ // Lists wash at ITEM granularity (a whole-list wash drowns the page), so the
3203
+ // ul/ol wrappers opt out of the search wash and the li carries it inline
3204
+ // (no wrapper div — that would break list semantics).
3205
+ ul: annotated(
3206
+ ({ node: _n, ...p }) => /* @__PURE__ */ jsx12(ProseList, { ...p }),
3207
+ false
3208
+ ),
3209
+ ol: annotated(
3210
+ ({ node: _n, ...p }) => /* @__PURE__ */ jsx12(ProseList, { ordered: true, ...p }),
3211
+ false
3212
+ ),
3213
+ li: function ListItemMd({ node, ...p }) {
3214
+ const search = useContext4(SearchContext);
3215
+ const pos = getSourcePos(node);
3216
+ const active = pos != null && search.activeLine != null && search.activeLine >= pos.start && search.activeLine <= pos.end && !nestedItemContains(node, search.activeLine);
3217
+ return /* @__PURE__ */ jsx12(
3218
+ ProseListItem,
3219
+ {
3220
+ "data-sourcepos": pos ? `${pos.start}:${pos.end}` : void 0,
3221
+ "data-search-active": active ? "" : void 0,
3222
+ ...p,
3223
+ className: cn11(active && "-mx-1 rounded-sm bg-primary/10 px-1", p.className)
3224
+ }
3225
+ );
3226
+ },
3227
+ blockquote: annotated(({ node: _n, ...p }) => /* @__PURE__ */ jsx12(ProseBlockquote, { ...p })),
3228
+ hr: annotated(() => /* @__PURE__ */ jsx12(Separator3, { className: "my-4" })),
3229
+ pre: annotated(PreBlock, false),
3230
+ table: annotated(({ node: _n, ...p }) => /* @__PURE__ */ jsx12(Table, { ...p })),
3231
+ thead: ({ node: _n, ...p }) => /* @__PURE__ */ jsx12(TableHeader, { ...p }),
3232
+ tbody: ({ node: _n, ...p }) => /* @__PURE__ */ jsx12(TableBody, { ...p }),
3233
+ tr: ({ node: _n, ...p }) => /* @__PURE__ */ jsx12(TableRow, { ...p }),
3234
+ th: ({ node: _n, ...p }) => /* @__PURE__ */ jsx12(TableHead, { ...p }),
3235
+ td: ({ node: _n, ...p }) => /* @__PURE__ */ jsx12(TableCell, { ...p }),
3236
+ [BRAND_DIRECTIVE_TAG]: annotated(BrandDirective),
3237
+ // Inline directives render un-`annotated` (no block wrapper) to stay in the text flow.
3238
+ [BRAND_DIRECTIVE_INLINE_TAG]: BrandInlineDirective,
3239
+ // Transclusion embeds (`![[target]]`) — resolved + recursively rendered by TransclusionBlock.
3240
+ [BRAND_TRANSCLUSION_TAG]: TransclusionBlock,
3241
+ // Academic layer — footnotes, math, citations (inline tags stay in the text flow;
3242
+ // the footnote section is a generated block).
3243
+ [FOOTNOTE_REF_TAG]: FootnoteRef,
3244
+ [FOOTNOTE_ITEM_TAG]: FootnoteItem,
3245
+ [FOOTNOTE_LIST_TAG]: ({ node: _n, children, ...rest }) => /* @__PURE__ */ jsx12(FootnoteList, { ...rest, children }),
3246
+ [MATH_INLINE_TAG]: MathInlineTag,
3247
+ [MATH_BLOCK_TAG]: MathBlockTag,
3248
+ [CITE_TAG]: InlineCite
3249
+ };
3250
+ var MarkdownPreview = forwardRef7(
3251
+ function MarkdownPreview2({
3252
+ children,
3253
+ stripFrontmatter = true,
3254
+ annotations,
3255
+ resolveUrl,
3256
+ searchTerm,
3257
+ activeSearchLine,
3258
+ headingActions,
3259
+ evaluate,
3260
+ extensions,
3261
+ resolveWikilink,
3262
+ resolveTransclusion,
3263
+ renderLinkPreview,
3264
+ footnotes,
3265
+ math,
3266
+ resolveCitation,
3267
+ citationStyle = "numeric",
3268
+ toc,
3269
+ evaluateIteration,
3270
+ interpolate,
3271
+ className,
3272
+ ...props
3273
+ }, ref) {
3274
+ const markdown = stripFrontmatter ? parseFrontmatter(children).content : children;
3275
+ const fmOffset = stripFrontmatter ? children.split("\n").length - markdown.split("\n").length : 0;
3276
+ const shifted = useMemo5(() => {
3277
+ if (!annotations?.length) return [];
3278
+ return fmOffset ? shiftAnnotations(annotations, fmOffset) : annotations;
3279
+ }, [annotations, fmOffset]);
3280
+ const search = useMemo5(() => {
3281
+ const term = searchTerm?.trim();
3282
+ const activeLine = activeSearchLine != null && activeSearchLine - fmOffset >= 1 ? activeSearchLine - fmOffset : void 0;
3283
+ return {
3284
+ term: term && term.length >= 2 ? term : void 0,
3285
+ activeLine,
3286
+ lines: markdown.split("\n")
3287
+ };
3288
+ }, [searchTerm, activeSearchLine, fmOffset, markdown]);
3289
+ const citations = useMemo5(() => {
3290
+ if (!resolveCitation) return null;
3291
+ return collectCitations(markdown, resolveCitation);
3292
+ }, [markdown, resolveCitation]);
3293
+ const outline = useMemo5(() => toc ? parseMarkdownOutline(markdown) : null, [toc, markdown]);
3294
+ const registry = useMemo5(() => {
3295
+ const directives = /* @__PURE__ */ new Map();
3296
+ for (const d of extensions?.directives ?? []) directives.set(d.name, d);
3297
+ const fences = /* @__PURE__ */ new Map();
3298
+ for (const f of extensions?.fences ?? []) fences.set(f.lang, f);
3299
+ if (toc) {
3300
+ directives.set("toc", {
3301
+ name: "toc",
3302
+ kinds: ["leaf", "container"],
3303
+ render: ({ attributes }) => /* @__PURE__ */ jsx12(TableOfContents, { title: attributes.title || void 0 })
3304
+ });
3305
+ }
3306
+ if (resolveCitation) {
3307
+ const renderBibliography = ({ attributes }) => /* @__PURE__ */ jsx12(Bibliography, { title: attributes.title || void 0 });
3308
+ directives.set("bibliography", {
3309
+ name: "bibliography",
3310
+ kinds: ["leaf", "container"],
3311
+ render: renderBibliography
3312
+ });
3313
+ directives.set("references", {
3314
+ name: "references",
3315
+ kinds: ["leaf", "container"],
3316
+ render: renderBibliography
3317
+ });
3318
+ }
3319
+ if (evaluateIteration) {
3320
+ const iterationDirective = (name) => ({
3321
+ name,
3322
+ kinds: ["container"],
3323
+ render: ({ attributes, rawBody }) => /* @__PURE__ */ jsx12(
3324
+ IterationDirective,
3325
+ {
3326
+ spec: specFromDirective(name, attributes, rawBody),
3327
+ evaluate: evaluateIteration,
3328
+ interpolate,
3329
+ renderCell: (md) => /* @__PURE__ */ jsx12(
3330
+ IterationCell,
3331
+ {
3332
+ markdown: md,
3333
+ config: {
3334
+ evaluateIteration,
3335
+ interpolate,
3336
+ evaluate,
3337
+ extensions,
3338
+ footnotes,
3339
+ math,
3340
+ resolveCitation,
3341
+ citationStyle
3342
+ }
3343
+ }
3344
+ )
3345
+ }
3346
+ )
3347
+ });
3348
+ directives.set("iterate", iterationDirective("iterate"));
3349
+ directives.set("pivot", iterationDirective("pivot"));
3350
+ }
3351
+ if (evaluate && !fences.has("calc")) {
3352
+ fences.set("calc", {
3353
+ lang: "calc",
3354
+ render: ({ source }) => /* @__PURE__ */ jsx12(CalcBlock, { source, evaluate })
3355
+ });
3356
+ }
3357
+ if (evaluate && !directives.has("calc")) {
3358
+ directives.set("calc", {
3359
+ name: "calc",
3360
+ kinds: ["inline"],
3361
+ // `textValue` is the verbatim expression (markdown chars preserved);
3362
+ // fall back to the rendered label only if positions were unavailable.
3363
+ render: ({ textValue, children: children2 }) => /* @__PURE__ */ jsx12(CalcInline, { source: textValue ?? flattenNodeText(children2), evaluate })
3364
+ });
3365
+ }
3366
+ return { directives, fences };
3367
+ }, [
3368
+ extensions,
3369
+ evaluate,
3370
+ toc,
3371
+ resolveCitation,
3372
+ citationStyle,
3373
+ evaluateIteration,
3374
+ interpolate,
3375
+ footnotes,
3376
+ math
3377
+ ]);
3378
+ const directiveNamesKey = [
3379
+ ...(extensions?.directives ?? []).map((d) => d.name),
3380
+ ...evaluate ? ["calc"] : [],
3381
+ ...toc ? ["toc"] : [],
3382
+ ...resolveCitation ? ["bibliography", "references"] : [],
3383
+ ...evaluateIteration ? ["iterate", "pivot"] : []
3384
+ ].join(" ");
3385
+ const plugins = useMemo5(() => {
3386
+ const directiveNames = directiveNamesKey ? directiveNamesKey.split(" ") : [];
3387
+ const rawBodyNames = evaluateIteration ? ["iterate", "pivot"] : [];
3388
+ let list = [
3389
+ ...baseRemarkPlugins,
3390
+ ...buildMarkdownPlugins({ directiveNames, rawBodyNames })
3391
+ ];
3392
+ if (math) list = [...list, remarkMath, remarkBrandMath];
3393
+ if (footnotes) list = [...list, remarkBrandFootnotes];
3394
+ if (resolveCitation) list = [...list, remarkBrandCitations];
3395
+ if (resolveWikilink) list = [...list, remarkResolveWikilinks(resolveWikilink)];
3396
+ if (resolveTransclusion) list = [...list, remarkResolveTransclusions()];
3397
+ if (resolveUrl) list = [...list, remarkResolveUrls(resolveUrl)];
3398
+ return list;
3399
+ }, [
3400
+ resolveUrl,
3401
+ resolveWikilink,
3402
+ resolveTransclusion,
3403
+ directiveNamesKey,
3404
+ math,
3405
+ footnotes,
3406
+ resolveCitation,
3407
+ evaluateIteration
3408
+ ]);
3409
+ const streamdown = /* @__PURE__ */ jsx12(
3410
+ Streamdown,
3411
+ {
3412
+ parseMarkdownIntoBlocksFn: singleBlock,
3413
+ remarkPlugins: plugins,
3414
+ rehypePlugins,
3415
+ allowedTags,
3416
+ components,
3417
+ children: markdown
3418
+ }
3419
+ );
3420
+ const withCitations = citations ? /* @__PURE__ */ jsx12(CitationProvider, { order: citations.order, byKey: citations.byKey, style: citationStyle, children: streamdown }) : streamdown;
3421
+ const body = outline ? /* @__PURE__ */ jsx12(TocProvider, { items: outline, children: withCitations }) : withCitations;
3422
+ return /* @__PURE__ */ jsx12(
3423
+ "div",
3424
+ {
3425
+ ref,
3426
+ "data-testid": "markdown-preview",
3427
+ className: cn11(
3428
+ "text-body text-foreground [&_pre]:my-3",
3429
+ "[&_h1]:!mt-10 [&_h2]:!mt-9 [&_h3]:!mt-7 [&_h4]:!mt-6",
3430
+ "[&_:is(h1,h2,h3,h4)+*]:!mt-3 [&_:is(h1,h2,h3,h4):first-child]:!mt-0",
3431
+ className
3432
+ ),
3433
+ ...props,
3434
+ children: /* @__PURE__ */ jsx12(AnnotationsContext.Provider, { value: shifted, children: /* @__PURE__ */ jsx12(SearchContext.Provider, { value: search, children: /* @__PURE__ */ jsx12(HeadingActionsContext.Provider, { value: headingActions ?? null, children: /* @__PURE__ */ jsx12(RegistryContext.Provider, { value: registry, children: /* @__PURE__ */ jsx12(LinkPreviewContext.Provider, { value: renderLinkPreview ?? null, children: /* @__PURE__ */ jsx12(TransclusionResolverContext.Provider, { value: resolveTransclusion ?? null, children: /* @__PURE__ */ jsx12(TransclusionDepthContext.Provider, { value: 0, children: body }) }) }) }) }) }) })
3435
+ }
3436
+ );
3437
+ }
3438
+ );
3439
+ function IterationCell({ markdown, config }) {
3440
+ return /* @__PURE__ */ jsx12(MarkdownPreview, { stripFrontmatter: false, ...config, children: markdown });
3441
+ }
3442
+
3443
+ // src/markdown-toolbar/markdown-toolbar.tsx
3444
+ import {
3445
+ Button as Button3,
3446
+ DropdownMenu,
3447
+ DropdownMenuContent,
3448
+ DropdownMenuItem,
3449
+ DropdownMenuLabel,
3450
+ DropdownMenuSeparator,
3451
+ DropdownMenuTrigger,
3452
+ Separator as Separator4,
3453
+ Tooltip,
3454
+ TooltipContent,
3455
+ TooltipProvider,
3456
+ TooltipTrigger
3457
+ } from "@elabs-ai/components-ui";
3458
+ import { cn as cn12 } from "@elabs-ai/components-ui/lib/cn";
3459
+ import {
3460
+ Bold,
3461
+ ChevronDown,
3462
+ Code2,
3463
+ Heading,
3464
+ Italic,
3465
+ Link2,
3466
+ List,
3467
+ ListOrdered,
3468
+ Minus as Minus2,
3469
+ Quote,
3470
+ SquarePlus
3471
+ } from "lucide-react";
3472
+ import { forwardRef as forwardRef8, Fragment as Fragment3 } from "react";
3473
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3474
+ var DIRECTIVE_SNIPPETS = [
3475
+ { label: "Card", snippet: `:::card{title="Title"}
3476
+ Content
3477
+ :::` },
3478
+ { label: "Callout", snippet: `:::callout{type="info" title="Note"}
3479
+ Message
3480
+ :::` },
3481
+ { label: "Metric", snippet: `::metric{label="Label" value="0" description="detail"}` },
3482
+ {
3483
+ label: "Timeline",
3484
+ snippet: `:::timeline
3485
+ - (done) Step one
3486
+ - (active) Step two
3487
+ - (pending) Step three
3488
+ :::`
3489
+ }
3490
+ ];
3491
+ var MarkdownToolbar = forwardRef8(
3492
+ function MarkdownToolbar2({ editor: editor2, actions, insertCommands, className, ...props }, ref) {
3493
+ const disabled = !editor2;
3494
+ const run = (fn) => () => {
3495
+ if (editor2) fn(editor2);
3496
+ };
3497
+ const insertGroups = insertCommands ? groupSlashCommands(
3498
+ insertCommands.filter((c) => typeof c.snippet === "string")
3499
+ ) : null;
3500
+ const IconButton = ({
3501
+ label,
3502
+ icon,
3503
+ onClick
3504
+ }) => /* @__PURE__ */ jsxs11(Tooltip, { children: [
3505
+ /* @__PURE__ */ jsx13(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx13(
3506
+ Button3,
3507
+ {
3508
+ type: "button",
3509
+ variant: "ghost",
3510
+ size: "icon-sm",
3511
+ disabled,
3512
+ onClick,
3513
+ "aria-label": label,
3514
+ children: icon
3515
+ }
3516
+ ) }),
3517
+ /* @__PURE__ */ jsx13(TooltipContent, { children: label })
3518
+ ] });
3519
+ return /* @__PURE__ */ jsx13(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs11(
3520
+ "div",
3521
+ {
3522
+ ref,
3523
+ role: "toolbar",
3524
+ "aria-label": "Markdown formatting",
3525
+ className: cn12(
3526
+ "flex h-10 shrink-0 items-center gap-0.5 border-b border-border bg-surface px-2",
3527
+ className
3528
+ ),
3529
+ ...props,
3530
+ children: [
3531
+ /* @__PURE__ */ jsx13(
3532
+ IconButton,
3533
+ {
3534
+ label: "Bold",
3535
+ icon: /* @__PURE__ */ jsx13(Bold, { className: "size-4" }),
3536
+ onClick: run((e) => wrapSelection(e, "**"))
3537
+ }
3538
+ ),
3539
+ /* @__PURE__ */ jsx13(
3540
+ IconButton,
3541
+ {
3542
+ label: "Italic",
3543
+ icon: /* @__PURE__ */ jsx13(Italic, { className: "size-4" }),
3544
+ onClick: run((e) => wrapSelection(e, "*"))
3545
+ }
3546
+ ),
3547
+ /* @__PURE__ */ jsx13(
3548
+ IconButton,
3549
+ {
3550
+ label: "Inline code",
3551
+ icon: /* @__PURE__ */ jsx13(Code2, { className: "size-4" }),
3552
+ onClick: run((e) => wrapSelection(e, "`"))
3553
+ }
3554
+ ),
3555
+ /* @__PURE__ */ jsx13(IconButton, { label: "Link", icon: /* @__PURE__ */ jsx13(Link2, { className: "size-4" }), onClick: run(insertLink) }),
3556
+ /* @__PURE__ */ jsx13(Separator4, { orientation: "vertical", className: "mx-1 h-5" }),
3557
+ /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
3558
+ /* @__PURE__ */ jsxs11(Tooltip, { children: [
3559
+ /* @__PURE__ */ jsx13(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx13(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs11(
3560
+ Button3,
3561
+ {
3562
+ type: "button",
3563
+ variant: "ghost",
3564
+ size: "sm",
3565
+ disabled,
3566
+ className: "gap-1",
3567
+ "aria-label": "Heading level",
3568
+ children: [
3569
+ /* @__PURE__ */ jsx13(Heading, { className: "size-4" }),
3570
+ /* @__PURE__ */ jsx13(ChevronDown, { className: "size-3" })
3571
+ ]
3572
+ }
3573
+ ) }) }),
3574
+ /* @__PURE__ */ jsx13(TooltipContent, { children: "Heading" })
3575
+ ] }),
3576
+ /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", children: [1, 2, 3].map((level) => /* @__PURE__ */ jsxs11(
3577
+ DropdownMenuItem,
3578
+ {
3579
+ onSelect: run((e) => toggleLinePrefix(e, `${"#".repeat(level)} `)),
3580
+ children: [
3581
+ "Heading ",
3582
+ level
3583
+ ]
3584
+ },
3585
+ level
3586
+ )) })
3587
+ ] }),
3588
+ /* @__PURE__ */ jsx13(
3589
+ IconButton,
3590
+ {
3591
+ label: "Quote",
3592
+ icon: /* @__PURE__ */ jsx13(Quote, { className: "size-4" }),
3593
+ onClick: run((e) => toggleLinePrefix(e, "> "))
3594
+ }
3595
+ ),
3596
+ /* @__PURE__ */ jsx13(
3597
+ IconButton,
3598
+ {
3599
+ label: "Bullet list",
3600
+ icon: /* @__PURE__ */ jsx13(List, { className: "size-4" }),
3601
+ onClick: run((e) => toggleLinePrefix(e, "- "))
3602
+ }
3603
+ ),
3604
+ /* @__PURE__ */ jsx13(
3605
+ IconButton,
3606
+ {
3607
+ label: "Numbered list",
3608
+ icon: /* @__PURE__ */ jsx13(ListOrdered, { className: "size-4" }),
3609
+ onClick: run((e) => toggleLinePrefix(e, "1. "))
3610
+ }
3611
+ ),
3612
+ /* @__PURE__ */ jsx13(
3613
+ IconButton,
3614
+ {
3615
+ label: "Divider",
3616
+ icon: /* @__PURE__ */ jsx13(Minus2, { className: "size-4" }),
3617
+ onClick: run(insertHorizontalRule)
3618
+ }
3619
+ ),
3620
+ /* @__PURE__ */ jsx13(Separator4, { orientation: "vertical", className: "mx-1 h-5" }),
3621
+ /* @__PURE__ */ jsxs11(DropdownMenu, { children: [
3622
+ /* @__PURE__ */ jsxs11(Tooltip, { children: [
3623
+ /* @__PURE__ */ jsx13(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx13(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs11(
3624
+ Button3,
3625
+ {
3626
+ type: "button",
3627
+ variant: "ghost",
3628
+ size: "sm",
3629
+ disabled,
3630
+ className: "gap-1",
3631
+ "aria-label": "Insert block",
3632
+ children: [
3633
+ /* @__PURE__ */ jsx13(SquarePlus, { className: "size-4" }),
3634
+ /* @__PURE__ */ jsx13("span", { className: "text-xs", children: "Insert" })
3635
+ ]
3636
+ }
3637
+ ) }) }),
3638
+ /* @__PURE__ */ jsx13(TooltipContent, { children: "Insert brand block" })
3639
+ ] }),
3640
+ /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", children: insertGroups && insertGroups.length > 0 ? insertGroups.map(({ group, commands }, gi) => /* @__PURE__ */ jsxs11(Fragment3, { children: [
3641
+ gi > 0 ? /* @__PURE__ */ jsx13(DropdownMenuSeparator, {}) : null,
3642
+ /* @__PURE__ */ jsx13(DropdownMenuLabel, { className: "text-meta font-medium text-muted-foreground", children: group }),
3643
+ commands.map((cmd) => /* @__PURE__ */ jsxs11(
3644
+ DropdownMenuItem,
3645
+ {
3646
+ className: "gap-2",
3647
+ onSelect: run((e) => insertDirective(e, cmd.snippet)),
3648
+ children: [
3649
+ cmd.icon ? /* @__PURE__ */ jsx13("span", { className: "flex size-4 shrink-0 items-center justify-center text-muted-foreground [&_svg]:size-4", children: cmd.icon }) : null,
3650
+ cmd.label
3651
+ ]
3652
+ },
3653
+ cmd.id
3654
+ ))
3655
+ ] }, group)) : DIRECTIVE_SNIPPETS.map(({ label, snippet }) => /* @__PURE__ */ jsx13(
3656
+ DropdownMenuItem,
3657
+ {
3658
+ onSelect: run((e) => insertDirective(e, snippet)),
3659
+ children: label
3660
+ },
3661
+ label
3662
+ )) })
3663
+ ] }),
3664
+ actions ? /* @__PURE__ */ jsx13("div", { className: "ml-auto flex items-center gap-1.5", children: actions }) : null
3665
+ ]
3666
+ }
3667
+ ) });
3668
+ }
3669
+ );
3670
+
3671
+ // src/markdown-workspace/focus-writing.ts
3672
+ function topLevelBlockOf(editorRoot, node) {
3673
+ let current = node;
3674
+ while (current && current.parentNode !== editorRoot) {
3675
+ current = current.parentNode;
3676
+ }
3677
+ return current instanceof Element ? current : null;
3678
+ }
3679
+ function typewriterDelta(caretTop, caretHeight, hostTop, hostHeight, band = 0.22) {
3680
+ if (hostHeight <= 0) return 0;
3681
+ const center = hostTop + hostHeight / 2;
3682
+ const caretMid = caretTop + caretHeight / 2;
3683
+ const tolerance = hostHeight * band / 2;
3684
+ const off = caretMid - center;
3685
+ return Math.abs(off) <= tolerance ? 0 : off;
3686
+ }
3687
+
3688
+ // src/markdown-workspace/markdown-workspace.tsx
3689
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
3690
+ var MODES = [
3691
+ { value: "source", label: "Source", icon: SquareCode },
3692
+ { value: "split", label: "Split", icon: Columns2 },
3693
+ { value: "wysiwyg", label: "Preview-edit", icon: Eye }
3694
+ ];
3695
+ var MarkdownWorkspace = forwardRef9(
3696
+ function MarkdownWorkspace2({
3697
+ value,
3698
+ defaultValue,
3699
+ onChange,
3700
+ mode,
3701
+ defaultMode = "split",
3702
+ onModeChange,
3703
+ defaultFocusWriting = false,
3704
+ focusWriting,
3705
+ modeSwitch = true,
3706
+ toolbarActions,
3707
+ slashMenu = true,
3708
+ insertCommands,
3709
+ calc,
3710
+ completions,
3711
+ onEmbedAsset,
3712
+ className,
3713
+ ...props
3714
+ }, ref) {
3715
+ const slashCommandList = typeof slashMenu === "object" && slashMenu.commands ? slashMenu.commands : BRAND_SLASH_COMMANDS;
3716
+ const toolbarInsertCommands = insertCommands ?? slashCommandList;
3717
+ const isControlled = value !== void 0;
3718
+ const [internalValue, setInternalValue] = useState5(value ?? defaultValue ?? "");
3719
+ const markdown = isControlled ? value : internalValue;
3720
+ const [internalMode, setInternalMode] = useState5(defaultMode);
3721
+ const activeMode = mode ?? internalMode;
3722
+ const [monaco5, setMonaco] = useState5(null);
3723
+ const [selectionListeners] = useState5(() => /* @__PURE__ */ new Set());
3724
+ const calcRef = useRef5(calc);
3725
+ calcRef.current = calc;
3726
+ const calcEnabled = calc != null;
3727
+ useEffect5(() => {
3728
+ if (!monaco5 || !calcEnabled) return;
3729
+ monaco5.updateOptions({
3730
+ quickSuggestions: { other: true, comments: false, strings: false }
3731
+ });
3732
+ return attachCalcMonaco(monaco5, () => calcRef.current);
3733
+ }, [monaco5, calcEnabled]);
3734
+ const completionsRef = useRef5(completions);
3735
+ completionsRef.current = completions;
3736
+ const completionsEnabled = completions != null;
3737
+ useEffect5(() => {
3738
+ if (!monaco5 || !completionsEnabled) return;
3739
+ return attachCompletionsMonaco(monaco5, () => completionsRef.current);
3740
+ }, [monaco5, completionsEnabled]);
3741
+ useEffect5(() => {
3742
+ if (activeMode === "wysiwyg") setMonaco(null);
3743
+ }, [activeMode]);
3744
+ const slashEnabled = slashMenu !== false;
3745
+ const shortcut = typeof slashMenu === "object" && "shortcut" in slashMenu ? slashMenu.shortcut : DEFAULT_SLASH_SHORTCUT;
3746
+ const [sourceSlashOpen, setSourceSlashOpen] = useState5(false);
3747
+ const [typedTrigger, setTypedTrigger] = useState5(null);
3748
+ const handleSourceSlashOpenChange = (next) => {
3749
+ setSourceSlashOpen(next);
3750
+ if (!next) setTypedTrigger(null);
3751
+ };
3752
+ useEffect5(() => {
3753
+ if (!monaco5 || !slashEnabled) return;
3754
+ const sub = monaco5.onDidChangeModelContent((e) => {
3755
+ if (sourceSlashOpen || e.changes.length !== 1) return;
3756
+ const change = e.changes[0];
3757
+ if (!change || change.text !== "/") return;
3758
+ const model = monaco5.getModel();
3759
+ if (!model) return;
3760
+ const line = change.range.startLineNumber;
3761
+ const range = slashTriggerRange(line, model.getLineContent(line), change.range.startColumn);
3762
+ if (!range) return;
3763
+ setTypedTrigger(range);
3764
+ setSourceSlashOpen(true);
3765
+ });
3766
+ return () => sub.dispose();
3767
+ }, [monaco5, slashEnabled, sourceSlashOpen]);
3768
+ const sourceCommands = useMemo6(
3769
+ () => slashCommandList.filter((c) => c.snippet != null || typeof c.runInSource === "function"),
3770
+ [slashCommandList]
3771
+ );
3772
+ const sourceActions = useMemo6(
3773
+ () => slashEnabled && shortcut ? [
3774
+ {
3775
+ id: "brand.openSlashMenu",
3776
+ label: "Insert block\u2026",
3777
+ keybindings: [parseShortcut(shortcut)],
3778
+ // Hotkey open inserts at the caret (no typed `/` to replace).
3779
+ run: () => {
3780
+ setTypedTrigger(null);
3781
+ setSourceSlashOpen(true);
3782
+ }
3783
+ }
3784
+ ] : [],
3785
+ [slashEnabled, shortcut]
3786
+ );
3787
+ const previewPaneRef = useRef5(null);
3788
+ const scrollLock = useRef5(null);
3789
+ const lockTimer = useRef5(null);
3790
+ const fmOffset = useMemo6(() => {
3791
+ try {
3792
+ const body = parseFrontmatter(markdown).content;
3793
+ return markdown.split("\n").length - body.split("\n").length;
3794
+ } catch {
3795
+ return 0;
3796
+ }
3797
+ }, [markdown]);
3798
+ const lock = (owner) => {
3799
+ scrollLock.current = owner;
3800
+ if (lockTimer.current) clearTimeout(lockTimer.current);
3801
+ lockTimer.current = setTimeout(() => {
3802
+ scrollLock.current = null;
3803
+ }, 150);
3804
+ };
3805
+ useEffect5(() => {
3806
+ if (!monaco5 || activeMode !== "split") return;
3807
+ const disposable = monaco5.onDidScrollChange(() => {
3808
+ if (scrollLock.current === "preview") return;
3809
+ const range = monaco5.getVisibleRanges()[0];
3810
+ const host = previewPaneRef.current;
3811
+ if (!range || !host) return;
3812
+ const line = range.startLineNumber - fmOffset;
3813
+ let target = null;
3814
+ for (const el of host.querySelectorAll("[data-sourcepos]")) {
3815
+ const end = Number(el.dataset.sourcepos?.split(":")[1]);
3816
+ if (end >= line) {
3817
+ target = el;
3818
+ break;
3819
+ }
3820
+ }
3821
+ if (!target) return;
3822
+ lock("editor");
3823
+ host.scrollTop = target.getBoundingClientRect().top - host.getBoundingClientRect().top + host.scrollTop - 12;
3824
+ });
3825
+ return () => disposable.dispose();
3826
+ }, [monaco5, activeMode, fmOffset]);
3827
+ const onPreviewScroll = () => {
3828
+ if (scrollLock.current === "editor" || !monaco5 || activeMode !== "split") return;
3829
+ const host = previewPaneRef.current;
3830
+ if (!host) return;
3831
+ const hostTop = host.getBoundingClientRect().top;
3832
+ for (const el of host.querySelectorAll("[data-sourcepos]")) {
3833
+ if (el.getBoundingClientRect().bottom >= hostTop) {
3834
+ const start = Number(el.dataset.sourcepos?.split(":")[0]);
3835
+ if (!Number.isNaN(start)) {
3836
+ lock("preview");
3837
+ monaco5.setScrollTop(monaco5.getTopForLineNumber(Math.max(1, start + fmOffset)));
3838
+ }
3839
+ return;
3840
+ }
3841
+ }
3842
+ };
3843
+ const setMarkdown = (next) => {
3844
+ if (!isControlled) setInternalValue(next);
3845
+ onChange?.(next);
3846
+ };
3847
+ const wysiwygRef = useRef5(null);
3848
+ const wysiwygBase = useRef5(null);
3849
+ useEffect5(() => {
3850
+ if (activeMode !== "wysiwyg") {
3851
+ wysiwygBase.current = null;
3852
+ return;
3853
+ }
3854
+ const original = markdown;
3855
+ wysiwygBase.current = { original, baseline: null };
3856
+ const poll = setInterval(() => {
3857
+ const base = wysiwygBase.current;
3858
+ if (!base || base.baseline !== null) {
3859
+ clearInterval(poll);
3860
+ return;
3861
+ }
3862
+ const s = wysiwygRef.current?.serialized();
3863
+ if (s != null) {
3864
+ base.baseline = s;
3865
+ clearInterval(poll);
3866
+ }
3867
+ }, 50);
3868
+ const stop = setTimeout(() => clearInterval(poll), 5e3);
3869
+ return () => {
3870
+ clearInterval(poll);
3871
+ clearTimeout(stop);
3872
+ };
3873
+ }, [activeMode]);
3874
+ const onWysiwygChange = (emitted) => {
3875
+ const base = wysiwygBase.current;
3876
+ const next = base && base.baseline !== null ? mergeNormalizedEdit(base.original, base.baseline, emitted) : emitted;
3877
+ setMarkdown(next);
3878
+ };
3879
+ const focusWritingEnabled = focusWriting !== false;
3880
+ const [focusWritingOn, setFocusWritingOn] = useState5(
3881
+ focusWritingEnabled ? defaultFocusWriting : false
3882
+ );
3883
+ const wysiwygPaneRef = useRef5(null);
3884
+ const lastInputAt = useRef5(0);
3885
+ useEffect5(() => {
3886
+ if (!(focusWritingEnabled && focusWritingOn && activeMode === "wysiwyg")) return;
3887
+ const pane = wysiwygPaneRef.current;
3888
+ if (!pane) return;
3889
+ let active = null;
3890
+ const onSelectionChange = () => {
3891
+ const root = pane.querySelector(".ProseMirror");
3892
+ if (!root) return;
3893
+ const sel = document.getSelection();
3894
+ const node = sel?.anchorNode ?? null;
3895
+ if (!node || !root.contains(node)) return;
3896
+ const block = topLevelBlockOf(root, node);
3897
+ if (block !== active) {
3898
+ active?.classList.remove("wb-fw-active");
3899
+ block?.classList.add("wb-fw-active");
3900
+ active = block;
3901
+ }
3902
+ if (Date.now() - lastInputAt.current < 200 && sel && sel.rangeCount > 0) {
3903
+ const range = sel.getRangeAt(0).getBoundingClientRect();
3904
+ const caret = range.height > 0 ? range : active?.getBoundingClientRect() ?? range;
3905
+ const host = pane.getBoundingClientRect();
3906
+ const delta = typewriterDelta(caret.top, caret.height, host.top, host.height);
3907
+ if (delta !== 0) pane.scrollTop += delta;
3908
+ }
3909
+ };
3910
+ const onInput = () => {
3911
+ lastInputAt.current = Date.now();
3912
+ };
3913
+ document.addEventListener("selectionchange", onSelectionChange);
3914
+ pane.addEventListener("input", onInput, true);
3915
+ onSelectionChange();
3916
+ return () => {
3917
+ document.removeEventListener("selectionchange", onSelectionChange);
3918
+ pane.removeEventListener("input", onInput, true);
3919
+ active?.classList.remove("wb-fw-active");
3920
+ };
3921
+ }, [focusWritingEnabled, focusWritingOn, activeMode]);
3922
+ const rootRef = useRef5(null);
3923
+ useImperativeHandle(
3924
+ ref,
3925
+ () => {
3926
+ const revealLine = (n, opts) => {
3927
+ if (monaco5) {
3928
+ const max = monaco5.getModel()?.getLineCount() ?? 0;
3929
+ if (n < 1 || n > max) return;
3930
+ if (opts?.center === false) {
3931
+ monaco5.revealLine(n);
3932
+ } else {
3933
+ monaco5.revealLineInCenter(n);
3934
+ }
3935
+ } else {
3936
+ const items = parseMarkdownOutline(markdown);
3937
+ const preceding = items.filter((item) => item.line + fmOffset <= n).at(-1);
3938
+ if (!preceding) return;
3939
+ wysiwygRef.current?.scrollToHeading(preceding.id);
3940
+ }
3941
+ };
3942
+ const scrollToHeading = (slug) => {
3943
+ if (monaco5) {
3944
+ const item = parseMarkdownOutline(markdown).find((i) => i.id === slug);
3945
+ if (!item) return;
3946
+ revealLine(item.line + fmOffset, { center: true });
3947
+ } else {
3948
+ wysiwygRef.current?.scrollToHeading(slug);
3949
+ }
3950
+ };
3951
+ const getAccess = () => {
3952
+ if (monaco5) return monacoContentAccess(monaco5);
3953
+ if (wysiwygRef.current) return wysiwygRef.current;
3954
+ return null;
3955
+ };
3956
+ return {
3957
+ revealLine,
3958
+ scrollToHeading,
3959
+ getEditor: () => monaco5,
3960
+ getElement: () => rootRef.current,
3961
+ // EditorContentAccess — read/write delegate to the active engine at call
3962
+ // time (the AI acts after mount, so a call-time snapshot is correct here).
3963
+ getText: () => getAccess()?.getText() ?? "",
3964
+ getSelection: () => getAccess()?.getSelection() ?? { text: "", empty: true },
3965
+ replaceSelection: (text) => getAccess()?.replaceSelection(text),
3966
+ insertAtCursor: (text) => getAccess()?.insertAtCursor(text),
3967
+ focus: () => getAccess()?.focus(),
3968
+ // onSelectionChange uses the STABLE listener set (not getAccess()) so a
3969
+ // subscribe-in-mount-effect survives the editor's async mount + mode
3970
+ // switches; the binding effect below forwards the active engine's events.
3971
+ onSelectionChange: (listener) => {
3972
+ selectionListeners.add(listener);
3973
+ return () => selectionListeners.delete(listener);
3974
+ }
3975
+ };
3976
+ },
3977
+ // Re-create when the things the methods close over change.
3978
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3979
+ [monaco5, markdown, fmOffset, activeMode, selectionListeners]
3980
+ );
3981
+ useEffect5(() => {
3982
+ let unsub;
3983
+ if (monaco5) {
3984
+ unsub = monacoContentAccess(monaco5).onSelectionChange(
3985
+ (sel) => selectionListeners.forEach((l) => l(sel))
3986
+ );
3987
+ } else if (activeMode === "wysiwyg" && wysiwygRef.current) {
3988
+ unsub = wysiwygRef.current.onSelectionChange(
3989
+ (sel) => selectionListeners.forEach((l) => l(sel))
3990
+ );
3991
+ }
3992
+ return () => unsub?.();
3993
+ }, [monaco5, activeMode, selectionListeners]);
3994
+ const setMode = (next) => {
3995
+ if (next !== "source" && next !== "split" && next !== "wysiwyg") return;
3996
+ if (!mode) setInternalMode(next);
3997
+ onModeChange?.(next);
3998
+ };
3999
+ const modeToggle = /* @__PURE__ */ jsx14(TooltipProvider2, { delayDuration: 300, children: /* @__PURE__ */ jsx14(
4000
+ ToggleGroup,
4001
+ {
4002
+ type: "single",
4003
+ value: activeMode,
4004
+ onValueChange: setMode,
4005
+ variant: "segmented",
4006
+ size: "sm",
4007
+ className: "rounded-md p-0.5",
4008
+ children: MODES.map(({ value: m, label, icon: Icon }) => /* @__PURE__ */ jsxs12(Tooltip2, { children: [
4009
+ /* @__PURE__ */ jsx14(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx14(
4010
+ ToggleGroupItem,
4011
+ {
4012
+ value: m,
4013
+ "aria-label": label,
4014
+ className: "h-6 min-w-7 rounded-[5px] px-2",
4015
+ children: /* @__PURE__ */ jsx14(Icon, { className: "size-4" })
4016
+ }
4017
+ ) }),
4018
+ /* @__PURE__ */ jsx14(TooltipContent2, { children: label })
4019
+ ] }, m))
4020
+ }
4021
+ ) });
4022
+ const trailing = /* @__PURE__ */ jsxs12(Fragment4, { children: [
4023
+ modeSwitch ? modeToggle : null,
4024
+ toolbarActions
4025
+ ] });
4026
+ const sourcePane = /* @__PURE__ */ jsx14(
4027
+ CodeEditor,
4028
+ {
4029
+ language: "markdown",
4030
+ value: markdown,
4031
+ onChange: setMarkdown,
4032
+ actions: sourceActions,
4033
+ onMount: (editor2) => {
4034
+ editor2.updateOptions({ wordWrap: "on" });
4035
+ setMonaco(editor2);
4036
+ }
4037
+ }
4038
+ );
4039
+ return /* @__PURE__ */ jsxs12(
4040
+ "div",
4041
+ {
4042
+ ref: rootRef,
4043
+ "data-testid": "markdown-workspace",
4044
+ className: cn13("flex h-full min-h-0 flex-col overflow-hidden", className),
4045
+ ...props,
4046
+ children: [
4047
+ activeMode === "wysiwyg" ? /* @__PURE__ */ jsxs12("div", { className: "flex h-10 shrink-0 items-center justify-end gap-2 border-b border-border bg-surface px-2", children: [
4048
+ focusWritingEnabled ? /* @__PURE__ */ jsx14(TooltipProvider2, { delayDuration: 300, children: /* @__PURE__ */ jsxs12(Tooltip2, { children: [
4049
+ /* @__PURE__ */ jsx14(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsxs12(
4050
+ Toggle,
4051
+ {
4052
+ size: "sm",
4053
+ pressed: focusWritingOn,
4054
+ onPressedChange: setFocusWritingOn,
4055
+ "aria-label": "Focus writing",
4056
+ className: "h-6 gap-1.5 px-2 text-caption",
4057
+ children: [
4058
+ /* @__PURE__ */ jsx14(Focus, { className: "size-3.5", "aria-hidden": "true" }),
4059
+ " Focus"
4060
+ ]
4061
+ }
4062
+ ) }),
4063
+ /* @__PURE__ */ jsx14(TooltipContent2, { children: "Typewriter scrolling \xB7 inactive paragraphs dim" })
4064
+ ] }) }) : null,
4065
+ trailing
4066
+ ] }) : /* @__PURE__ */ jsx14(
4067
+ MarkdownToolbar,
4068
+ {
4069
+ editor: monaco5,
4070
+ actions: trailing,
4071
+ insertCommands: toolbarInsertCommands
4072
+ }
4073
+ ),
4074
+ /* @__PURE__ */ jsxs12("div", { className: "min-h-0 flex-1", children: [
4075
+ activeMode === "source" ? sourcePane : null,
4076
+ activeMode === "wysiwyg" ? /* @__PURE__ */ jsx14(
4077
+ "div",
4078
+ {
4079
+ ref: wysiwygPaneRef,
4080
+ "data-focus-writing": focusWritingEnabled && focusWritingOn ? "" : void 0,
4081
+ className: "h-full overflow-auto p-4",
4082
+ children: /* @__PURE__ */ jsx14(
4083
+ MarkdownEditor,
4084
+ {
4085
+ ref: wysiwygRef,
4086
+ defaultValue: markdown,
4087
+ onChange: onWysiwygChange,
4088
+ slashMenu,
4089
+ calc,
4090
+ completions,
4091
+ onEmbedAsset,
4092
+ className: "border-0"
4093
+ }
4094
+ )
4095
+ }
4096
+ ) : null,
4097
+ activeMode === "split" ? /* @__PURE__ */ jsxs12(ResizablePanelGroup, { direction: "horizontal", children: [
4098
+ /* @__PURE__ */ jsx14(ResizablePanel, { defaultSize: 50, minSize: 25, children: sourcePane }),
4099
+ /* @__PURE__ */ jsx14(ResizableHandle, { withHandle: true }),
4100
+ /* @__PURE__ */ jsx14(ResizablePanel, { defaultSize: 50, minSize: 25, children: /* @__PURE__ */ jsx14(
4101
+ "div",
4102
+ {
4103
+ ref: previewPaneRef,
4104
+ onScroll: onPreviewScroll,
4105
+ className: "h-full overflow-auto p-5",
4106
+ children: /* @__PURE__ */ jsx14(MarkdownPreview, { children: markdown })
4107
+ }
4108
+ ) })
4109
+ ] }) : null
4110
+ ] }),
4111
+ monaco5 && (activeMode === "source" || activeMode === "split") && slashEnabled ? /* @__PURE__ */ jsx14(
4112
+ MonacoSlashMenu,
4113
+ {
4114
+ editor: monaco5,
4115
+ commands: sourceCommands,
4116
+ open: sourceSlashOpen,
4117
+ onOpenChange: handleSourceSlashOpenChange,
4118
+ triggerRange: typedTrigger
4119
+ }
4120
+ ) : null
4121
+ ]
4122
+ }
4123
+ );
4124
+ }
4125
+ );
4126
+
4127
+ // src/markdown/parse.ts
4128
+ import remarkDirective2 from "remark-directive";
4129
+ import remarkFrontmatter from "remark-frontmatter";
4130
+ import remarkGfm from "remark-gfm";
4131
+ import remarkParse from "remark-parse";
4132
+ import { unified } from "unified";
4133
+ var processor = unified().use(remarkParse).use(remarkGfm).use(remarkFrontmatter, ["yaml"]).use(remarkDirective2).freeze();
4134
+ function parseMarkdown(md) {
4135
+ return processor.parse(md);
4136
+ }
4137
+
4138
+ // src/mermaid-workspace/mermaid-workspace.tsx
4139
+ import { ResizableHandle as ResizableHandle2, ResizablePanel as ResizablePanel2, ResizablePanelGroup as ResizablePanelGroup2 } from "@elabs-ai/components-ui";
4140
+ import { cn as cn14 } from "@elabs-ai/components-ui/lib/cn";
4141
+ import { forwardRef as forwardRef10, useEffect as useEffect6, useState as useState6 } from "react";
4142
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
4143
+ var MermaidWorkspace = forwardRef10(
4144
+ function MermaidWorkspace2({ value, defaultValue, onChange, debounceMs = 350, className, ...props }, ref) {
4145
+ const isControlled = value !== void 0;
4146
+ const [internal, setInternal] = useState6(value ?? defaultValue ?? "");
4147
+ const source = isControlled ? value : internal;
4148
+ const [debounced, setDebounced] = useState6(source);
4149
+ useEffect6(() => {
4150
+ const t = setTimeout(() => setDebounced(source), debounceMs);
4151
+ return () => clearTimeout(t);
4152
+ }, [source, debounceMs]);
4153
+ const setSource = (next) => {
4154
+ if (!isControlled) setInternal(next);
4155
+ onChange?.(next);
4156
+ };
4157
+ return /* @__PURE__ */ jsx15(
4158
+ "div",
4159
+ {
4160
+ ref,
4161
+ "data-testid": "mermaid-workspace",
4162
+ className: cn14("h-full min-h-0 overflow-hidden", className),
4163
+ ...props,
4164
+ children: /* @__PURE__ */ jsxs13(ResizablePanelGroup2, { direction: "horizontal", children: [
4165
+ /* @__PURE__ */ jsx15(ResizablePanel2, { defaultSize: 45, minSize: 25, children: /* @__PURE__ */ jsx15(CodeEditor, { language: "plaintext", value: source, onChange: setSource }) }),
4166
+ /* @__PURE__ */ jsx15(ResizableHandle2, { withHandle: true }),
4167
+ /* @__PURE__ */ jsx15(ResizablePanel2, { defaultSize: 55, minSize: 25, children: /* @__PURE__ */ jsx15("div", { className: "h-full overflow-auto p-4", children: /* @__PURE__ */ jsx15(MermaidDiagram, { chart: debounced, label: "Diagram preview" }) }) })
4168
+ ] })
4169
+ }
4170
+ );
4171
+ }
4172
+ );
4173
+
4174
+ // src/ai-objects/decision-card.tsx
4175
+ import {
4176
+ Badge,
4177
+ Card as Card2,
4178
+ CardContent as CardContent2,
4179
+ CardHeader as CardHeader2,
4180
+ Separator as Separator5
4181
+ } from "@elabs-ai/components-ui";
4182
+ import { cn as cn15 } from "@elabs-ai/components-ui/lib/cn";
4183
+ import { cva } from "class-variance-authority";
4184
+ import { CheckCircle2, CircleDashed, Clock, RefreshCw } from "lucide-react";
4185
+ import {
4186
+ forwardRef as forwardRef11
4187
+ } from "react";
4188
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
4189
+ var DECISION_STATUSES = ["accepted", "rejected", "proposed", "superseded"];
4190
+ var STATUS_BADGE_VARIANT = {
4191
+ accepted: "success",
4192
+ rejected: "destructive",
4193
+ proposed: "info",
4194
+ superseded: "secondary"
4195
+ };
4196
+ var STATUS_LABELS = {
4197
+ accepted: "Accepted",
4198
+ rejected: "Rejected",
4199
+ proposed: "Proposed",
4200
+ superseded: "Superseded"
4201
+ };
4202
+ var STATUS_ICONS = {
4203
+ accepted: CheckCircle2,
4204
+ rejected: CircleDashed,
4205
+ proposed: Clock,
4206
+ superseded: RefreshCw
4207
+ };
4208
+ function isDecisionStatus(s) {
4209
+ return DECISION_STATUSES.includes(s);
4210
+ }
4211
+ var decisionCardVariants = cva("border-s-4", {
4212
+ variants: {
4213
+ status: {
4214
+ accepted: "border-s-success",
4215
+ rejected: "border-s-destructive",
4216
+ proposed: "border-s-info",
4217
+ superseded: "border-s-border"
4218
+ }
4219
+ },
4220
+ defaultVariants: { status: "proposed" }
4221
+ });
4222
+ var DecisionCard = forwardRef11(function DecisionCard2({ status: rawStatus, date, alternatives, children, className, ...props }, ref) {
4223
+ const status = isDecisionStatus(rawStatus ?? "") ? rawStatus : "proposed";
4224
+ const badgeVariant = STATUS_BADGE_VARIANT[status];
4225
+ const label = STATUS_LABELS[status];
4226
+ const Icon = STATUS_ICONS[status];
4227
+ const altItems = alternatives ? alternatives.split(",").map((s) => s.trim()).filter(Boolean) : [];
4228
+ return /* @__PURE__ */ jsx16(
4229
+ "section",
4230
+ {
4231
+ ref,
4232
+ "aria-label": `Decision: ${label}`,
4233
+ className: cn15("not-prose", className),
4234
+ ...props,
4235
+ children: /* @__PURE__ */ jsxs14(Card2, { className: cn15(decisionCardVariants({ status })), children: [
4236
+ /* @__PURE__ */ jsx16(CardHeader2, { className: "pb-3", children: /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-2", children: [
4237
+ /* @__PURE__ */ jsxs14(Badge, { variant: badgeVariant, className: "gap-1.5", children: [
4238
+ /* @__PURE__ */ jsx16(Icon, { className: "size-3", "aria-hidden": "true" }),
4239
+ label
4240
+ ] }),
4241
+ date ? /* @__PURE__ */ jsx16("time", { dateTime: date, className: "text-meta text-muted-foreground tabular-nums", children: date }) : null
4242
+ ] }) }),
4243
+ children ? /* @__PURE__ */ jsx16(CardContent2, { className: "text-body text-foreground", children }) : null,
4244
+ altItems.length > 0 ? /* @__PURE__ */ jsxs14(Fragment5, { children: [
4245
+ /* @__PURE__ */ jsx16(Separator5, {}),
4246
+ /* @__PURE__ */ jsxs14("div", { className: "px-6 py-4", children: [
4247
+ /* @__PURE__ */ jsx16("p", { className: "mb-2 text-meta font-medium text-muted-foreground", children: "Alternatives considered" }),
4248
+ /* @__PURE__ */ jsx16("ul", { className: "flex flex-wrap gap-1.5", "aria-label": "Alternatives considered", children: altItems.map((alt) => /* @__PURE__ */ jsx16("li", { children: /* @__PURE__ */ jsx16(Badge, { variant: "outline", className: "text-meta", children: alt }) }, alt)) })
4249
+ ] })
4250
+ ] }) : null
4251
+ ] })
4252
+ }
4253
+ );
4254
+ });
4255
+
4256
+ // src/ai-objects/entity.tsx
4257
+ import { Card as Card3, CardContent as CardContent3, CardHeader as CardHeader3, CardTitle as CardTitle2 } from "@elabs-ai/components-ui";
4258
+ import { cn as cn16 } from "@elabs-ai/components-ui/lib/cn";
4259
+ import { cva as cva2 } from "class-variance-authority";
4260
+ import { Box, Building2, Lightbulb, MapPin, Tag, User } from "lucide-react";
4261
+ import {
4262
+ forwardRef as forwardRef12
4263
+ } from "react";
4264
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
4265
+ var ENTITY_KINDS = ["org", "person", "place", "product", "concept"];
4266
+ var KIND_META = {
4267
+ org: { Icon: Building2, label: "Organisation" },
4268
+ person: { Icon: User, label: "Person" },
4269
+ place: { Icon: MapPin, label: "Place" },
4270
+ product: { Icon: Box, label: "Product" },
4271
+ concept: { Icon: Lightbulb, label: "Concept" }
4272
+ };
4273
+ var DEFAULT_KIND_META = { Icon: Tag, label: "Entity" };
4274
+ function kindMeta(kind) {
4275
+ return KIND_META[kind ?? ""] ?? DEFAULT_KIND_META;
4276
+ }
4277
+ var entityChipVariants = cva2(
4278
+ // inline-flex + align-middle keeps the chip in the text baseline;
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",
4281
+ {
4282
+ variants: {
4283
+ kind: {
4284
+ org: "border-border bg-secondary/60 text-secondary-foreground",
4285
+ // #399 — same reasoning as `concept` below: a 10% WASH is not a plate
4286
+ // and not a mark, so the LABEL takes the on-surface `-text` rung. This
4287
+ // is the row that had no `-text` rung to reach for until #399 minted it.
4288
+ person: "border-primary/30 bg-primary/10 text-primary-text",
4289
+ place: "border-info/30 bg-info/10 text-info-text",
4290
+ product: "border-success/30 bg-success/10 text-success-text",
4291
+ // `-text`, not `-foreground`: the chip is a 10% WASH on the page surface,
4292
+ // not a solid `--warning` plate, so it needs the on-surface rung its
4293
+ // place/product siblings use (#381 flipped `--warning-foreground` to
4294
+ // light ink for the now-deep fill).
4295
+ concept: "border-warning/30 bg-warning/10 text-warning-text",
4296
+ default: "border-border text-foreground"
4297
+ }
4298
+ },
4299
+ defaultVariants: { kind: "default" }
4300
+ }
4301
+ );
4302
+ function chipKind(kind) {
4303
+ if (!kind) return "default";
4304
+ const known = [
4305
+ "org",
4306
+ "person",
4307
+ "place",
4308
+ "product",
4309
+ "concept",
4310
+ "default"
4311
+ ];
4312
+ return known.includes(kind) ? kind : "default";
4313
+ }
4314
+ var EntityChip = forwardRef12(function EntityChip2({ kind: rawKind, children, className, ...props }, ref) {
4315
+ const { Icon, label } = kindMeta(rawKind);
4316
+ const resolvedKind = chipKind(rawKind);
4317
+ return /* @__PURE__ */ jsxs15(
4318
+ "span",
4319
+ {
4320
+ ref,
4321
+ role: "mark",
4322
+ "aria-label": children ? `${String(children)} (${label})` : label,
4323
+ className: cn16(entityChipVariants({ kind: resolvedKind }), className),
4324
+ ...props,
4325
+ children: [
4326
+ /* @__PURE__ */ jsx17(Icon, { className: "size-3 shrink-0", "aria-hidden": "true" }),
4327
+ /* @__PURE__ */ jsx17("span", { children })
4328
+ ]
4329
+ }
4330
+ );
4331
+ });
4332
+ var EntityCard = forwardRef12(function EntityCard2({ kind: rawKind, name, children, className, ...props }, ref) {
4333
+ const { Icon, label } = kindMeta(rawKind);
4334
+ return /* @__PURE__ */ jsx17(
4335
+ "section",
4336
+ {
4337
+ ref,
4338
+ "aria-label": name ? `${label}: ${name}` : label,
4339
+ className: cn16("not-prose", className),
4340
+ ...props,
4341
+ children: /* @__PURE__ */ jsxs15(Card3, { children: [
4342
+ /* @__PURE__ */ jsx17(CardHeader3, { className: "pb-3", children: /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
4343
+ /* @__PURE__ */ jsx17(
4344
+ "span",
4345
+ {
4346
+ className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-muted",
4347
+ "aria-hidden": "true",
4348
+ children: /* @__PURE__ */ jsx17(Icon, { className: "size-4 text-muted-foreground" })
4349
+ }
4350
+ ),
4351
+ /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
4352
+ name ? /* @__PURE__ */ jsx17(CardTitle2, { className: "truncate", children: name }) : null,
4353
+ /* @__PURE__ */ jsx17("p", { className: "text-meta text-muted-foreground", children: label })
4354
+ ] })
4355
+ ] }) }),
4356
+ children ? /* @__PURE__ */ jsx17(CardContent3, { className: "text-body text-foreground", children }) : null
4357
+ ] })
4358
+ }
4359
+ );
4360
+ });
4361
+
4362
+ // src/ai-objects/knowledge-card.tsx
4363
+ import { Card as Card4, CardContent as CardContent4, CardFooter } from "@elabs-ai/components-ui";
4364
+ import { cn as cn17 } from "@elabs-ai/components-ui/lib/cn";
4365
+ import { BookOpen, FileText } from "lucide-react";
4366
+ import { forwardRef as forwardRef13 } from "react";
4367
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
4368
+ function SourceRow({ path, resolve }) {
4369
+ const resolved = resolve ? resolve(path) : null;
4370
+ const display = resolved?.title ?? path;
4371
+ if (resolved) {
4372
+ return /* @__PURE__ */ jsxs16(
4373
+ "a",
4374
+ {
4375
+ href: resolved.href,
4376
+ rel: "noopener noreferrer",
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",
4379
+ "aria-label": `Source: ${display}`,
4380
+ children: [
4381
+ /* @__PURE__ */ jsx18(FileText, { className: "size-3 shrink-0", "aria-hidden": "true" }),
4382
+ /* @__PURE__ */ jsx18("span", { className: "truncate", children: display })
4383
+ ]
4384
+ }
4385
+ );
4386
+ }
4387
+ return /* @__PURE__ */ jsxs16(
4388
+ "span",
4389
+ {
4390
+ className: "flex min-w-0 items-center gap-1.5 text-meta text-muted-foreground",
4391
+ "aria-label": `Source (unresolved): ${display}`,
4392
+ children: [
4393
+ /* @__PURE__ */ jsx18(FileText, { className: "size-3 shrink-0", "aria-hidden": "true" }),
4394
+ /* @__PURE__ */ jsx18("span", { className: "truncate", children: display })
4395
+ ]
4396
+ }
4397
+ );
4398
+ }
4399
+ var KnowledgeCard = forwardRef13(function KnowledgeCard2({ sources, resolve, children, className, ...props }, ref) {
4400
+ const sourcePaths = sources ? sources.split(",").map((s) => s.trim()).filter(Boolean) : [];
4401
+ return /* @__PURE__ */ jsx18(
4402
+ "section",
4403
+ {
4404
+ ref,
4405
+ "aria-label": "Knowledge fact",
4406
+ className: cn17("not-prose", className),
4407
+ ...props,
4408
+ children: /* @__PURE__ */ jsxs16(Card4, { className: "border-s-4 border-s-info", children: [
4409
+ /* @__PURE__ */ jsxs16(CardContent4, { className: "pt-4", children: [
4410
+ /* @__PURE__ */ jsxs16("div", { className: "mb-2 flex items-center gap-1.5", children: [
4411
+ /* @__PURE__ */ jsx18(BookOpen, { className: "size-3.5 shrink-0 text-info-text", "aria-hidden": "true" }),
4412
+ /* @__PURE__ */ jsx18("span", { className: "text-meta font-medium text-info-text", children: "Knowledge" })
4413
+ ] }),
4414
+ /* @__PURE__ */ jsx18("div", { className: "text-body text-foreground", children })
4415
+ ] }),
4416
+ sourcePaths.length > 0 ? /* @__PURE__ */ jsxs16(CardFooter, { className: "flex-col items-start gap-1 border-t border-border pt-3", children: [
4417
+ /* @__PURE__ */ jsx18("p", { className: "text-meta font-medium text-muted-foreground", children: "Sources" }),
4418
+ /* @__PURE__ */ jsx18("ul", { className: "flex w-full flex-col gap-1", "aria-label": "Sources", children: sourcePaths.map((path) => /* @__PURE__ */ jsx18("li", { className: "min-w-0", children: /* @__PURE__ */ jsx18(SourceRow, { path, resolve }) }, path)) })
4419
+ ] }) : null
4420
+ ] })
4421
+ }
4422
+ );
4423
+ });
4424
+
4425
+ // src/ai-objects/directives.ts
4426
+ import { createElement } from "react";
4427
+ function decisionDirective() {
4428
+ return {
4429
+ name: "decision",
4430
+ kinds: ["container"],
4431
+ render({ attributes, children }) {
4432
+ return createElement(DecisionCard, {
4433
+ status: attributes.status,
4434
+ date: attributes.date,
4435
+ alternatives: attributes.alternatives,
4436
+ children
4437
+ });
4438
+ }
4439
+ };
4440
+ }
4441
+ function entityDirective() {
4442
+ return {
4443
+ name: "entity",
4444
+ kinds: ["container", "inline"],
4445
+ render({ kind, attributes, children, textValue }) {
4446
+ if (kind === "inline") {
4447
+ const label = textValue ?? (typeof children === "string" ? children : void 0);
4448
+ return createElement(EntityChip, { kind: attributes.kind, children: label ?? children });
4449
+ }
4450
+ return createElement(EntityCard, {
4451
+ kind: attributes.kind,
4452
+ name: attributes.name,
4453
+ children
4454
+ });
4455
+ }
4456
+ };
4457
+ }
4458
+ function knowledgeDirective(options) {
4459
+ return {
4460
+ name: "knowledge",
4461
+ kinds: ["container"],
4462
+ render({ attributes, children }) {
4463
+ return createElement(KnowledgeCard, {
4464
+ sources: attributes.sources,
4465
+ resolve: options?.resolve,
4466
+ children
4467
+ });
4468
+ }
4469
+ };
4470
+ }
4471
+ function aiObjectDirectives(options = {}) {
4472
+ return [
4473
+ decisionDirective(),
4474
+ entityDirective(),
4475
+ knowledgeDirective({ resolve: options.resolveKnowledge })
4476
+ ];
4477
+ }
4478
+
4479
+ // src/markdown-iteration/template-dialog.tsx
4480
+ import {
4481
+ Button as Button4,
4482
+ Dialog as Dialog2,
4483
+ DialogContent as DialogContent2,
4484
+ DialogDescription,
4485
+ DialogFooter,
4486
+ DialogHeader,
4487
+ DialogTitle as DialogTitle2
4488
+ } from "@elabs-ai/components-ui";
4489
+ import { useEffect as useEffect7, useState as useState7 } from "react";
4490
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
4491
+ function IterationTemplateDialog({
4492
+ open,
4493
+ onOpenChange,
4494
+ template,
4495
+ onSave,
4496
+ kind = "iterate",
4497
+ mode = "split"
4498
+ }) {
4499
+ const [draft, setDraft] = useState7(template);
4500
+ useEffect7(() => {
4501
+ if (open) setDraft(template);
4502
+ }, [open, template]);
4503
+ const unit = kind === "pivot" ? "cell" : "row";
4504
+ const save = () => {
4505
+ onSave(draft);
4506
+ onOpenChange(false);
4507
+ };
4508
+ return /* @__PURE__ */ jsx19(Dialog2, { open, onOpenChange, children: /* @__PURE__ */ jsxs17(DialogContent2, { className: "flex max-h-[85vh] w-[min(48rem,92vw)] max-w-none flex-col", children: [
4509
+ /* @__PURE__ */ jsxs17(DialogHeader, { children: [
4510
+ /* @__PURE__ */ jsxs17(DialogTitle2, { children: [
4511
+ "Edit ",
4512
+ kind === "pivot" ? "pivot" : "iteration",
4513
+ " template"
4514
+ ] }),
4515
+ /* @__PURE__ */ jsxs17(DialogDescription, { children: [
4516
+ "The per-",
4517
+ unit,
4518
+ " template. Use ",
4519
+ /* @__PURE__ */ jsx19("code", { children: "{{token}}" }),
4520
+ " placeholders (e.g.",
4521
+ " ",
4522
+ /* @__PURE__ */ jsx19("code", { children: "{{item.name}}" }),
4523
+ ") \u2014 each is filled per ",
4524
+ unit,
4525
+ " when the block renders."
4526
+ ] })
4527
+ ] }),
4528
+ /* @__PURE__ */ jsx19("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx19(
4529
+ MarkdownWorkspace,
4530
+ {
4531
+ value: draft,
4532
+ onChange: setDraft,
4533
+ defaultMode: mode,
4534
+ className: "h-full",
4535
+ "aria-label": "Iteration template editor"
4536
+ }
4537
+ ) }),
4538
+ /* @__PURE__ */ jsxs17(DialogFooter, { children: [
4539
+ /* @__PURE__ */ jsx19(Button4, { variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
4540
+ /* @__PURE__ */ jsx19(Button4, { onClick: save, children: "Save template" })
4541
+ ] })
4542
+ ] }) });
4543
+ }
4544
+ function IterationTemplateProvider({ children }) {
4545
+ const [request, setRequest] = useState7(null);
4546
+ return /* @__PURE__ */ jsxs17(IterationEditContext.Provider, { value: setRequest, children: [
4547
+ children,
4548
+ /* @__PURE__ */ jsx19(
4549
+ IterationTemplateDialog,
4550
+ {
4551
+ open: request != null,
4552
+ onOpenChange: (next) => {
4553
+ if (!next) setRequest(null);
4554
+ },
4555
+ template: request?.template ?? "",
4556
+ kind: request?.kind ?? "iterate",
4557
+ onSave: (template) => request?.onSave(template)
4558
+ }
4559
+ )
4560
+ ] });
4561
+ }
4562
+
4563
+ // src/markdown-iteration/iteration-builder-dialog.tsx
4564
+ import {
4565
+ Button as Button5,
4566
+ Dialog as Dialog3,
4567
+ DialogBody,
4568
+ DialogContent as DialogContent3,
4569
+ DialogDescription as DialogDescription2,
4570
+ DialogFooter as DialogFooter2,
4571
+ DialogHeader as DialogHeader2,
4572
+ DialogTitle as DialogTitle3,
4573
+ Input as Input2,
4574
+ Label,
4575
+ TagInput,
4576
+ ToggleGroup as ToggleGroup2,
4577
+ ToggleGroupItem as ToggleGroupItem2
4578
+ } from "@elabs-ai/components-ui";
4579
+ import { useEffect as useEffect8, useId as useId4, useMemo as useMemo7, useState as useState8 } from "react";
4580
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
4581
+ function IterationBuilderDialog({
4582
+ open,
4583
+ onOpenChange,
4584
+ kind: kindProp = "iterate",
4585
+ value,
4586
+ initialValues,
4587
+ onSave,
4588
+ evaluate,
4589
+ interpolate
4590
+ }) {
4591
+ const kind = value?.kind ?? kindProp;
4592
+ const isPivot = kind === "pivot";
4593
+ const ids = useId4();
4594
+ const [asName, setAsName] = useState8("item");
4595
+ const [layout, setLayout] = useState8(ITERATION_LAYOUTS[kind][0] ?? "stacked");
4596
+ const [values, setValues] = useState8([]);
4597
+ const [cols, setCols] = useState8([]);
4598
+ const [template, setTemplate] = useState8("");
4599
+ useEffect8(() => {
4600
+ if (!open) return;
4601
+ const seed = value ?? {
4602
+ ...emptyBuilderValue(kind),
4603
+ values: initialValues ?? []
4604
+ };
4605
+ setAsName(seed.as);
4606
+ setLayout(seed.layout);
4607
+ setValues(seed.values);
4608
+ setCols(seed.cols ?? []);
4609
+ setTemplate(seed.template);
4610
+ }, [open]);
4611
+ const draft = useMemo7(
4612
+ () => ({
4613
+ kind,
4614
+ as: asName.trim() || "item",
4615
+ layout,
4616
+ values,
4617
+ cols: isPivot ? cols : void 0,
4618
+ template
4619
+ }),
4620
+ [kind, asName, layout, values, cols, template, isPivot]
4621
+ );
4622
+ const previewMarkdown = useMemo7(() => serializeIterationDirective(draft), [draft]);
4623
+ const save = () => {
4624
+ onSave(serializeIterationDirective(draft));
4625
+ onOpenChange(false);
4626
+ };
4627
+ const noun = isPivot ? "pivot" : "iteration";
4628
+ return /* @__PURE__ */ jsx20(Dialog3, { open, onOpenChange, children: /* @__PURE__ */ jsxs18(DialogContent3, { className: "flex max-h-[88vh] w-[min(56rem,94vw)] max-w-none flex-col", children: [
4629
+ /* @__PURE__ */ jsxs18(DialogHeader2, { children: [
4630
+ /* @__PURE__ */ jsxs18(DialogTitle3, { children: [
4631
+ value ? "Edit" : "Insert",
4632
+ " ",
4633
+ noun
4634
+ ] }),
4635
+ /* @__PURE__ */ jsx20(DialogDescription2, { children: isPivot ? "Pick the row and column values, then write the per-cell template. The matrix below fills in live." : "Add the list values, then write the per-row template. The result below fills in live." })
4636
+ ] }),
4637
+ /* @__PURE__ */ jsxs18(DialogBody, { className: "grid gap-4 md:grid-cols-2", children: [
4638
+ /* @__PURE__ */ jsxs18("div", { className: "flex min-w-0 flex-col gap-4", children: [
4639
+ !isPivot ? /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-1.5", children: [
4640
+ /* @__PURE__ */ jsx20(Label, { htmlFor: `${ids}-as`, children: "Bind name" }),
4641
+ /* @__PURE__ */ jsx20(
4642
+ Input2,
4643
+ {
4644
+ id: `${ids}-as`,
4645
+ value: asName,
4646
+ spellCheck: false,
4647
+ autoComplete: "off",
4648
+ placeholder: "item",
4649
+ onChange: (e) => setAsName(e.target.value)
4650
+ }
4651
+ ),
4652
+ /* @__PURE__ */ jsxs18("p", { className: "text-meta text-muted-foreground", children: [
4653
+ "Use ",
4654
+ /* @__PURE__ */ jsx20("code", { children: `{{${asName.trim() || "item"}.name}}` }),
4655
+ " in the template."
4656
+ ] })
4657
+ ] }) : null,
4658
+ /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-1.5", children: [
4659
+ /* @__PURE__ */ jsx20(Label, { htmlFor: `${ids}-values`, children: isPivot ? "Row values" : "Values" }),
4660
+ /* @__PURE__ */ jsx20(
4661
+ TagInput,
4662
+ {
4663
+ id: `${ids}-values`,
4664
+ value: values,
4665
+ onValueChange: setValues,
4666
+ delimiter: [",", "\n"],
4667
+ placeholder: "Type a value, press Enter\u2026"
4668
+ }
4669
+ )
4670
+ ] }),
4671
+ isPivot ? /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-1.5", children: [
4672
+ /* @__PURE__ */ jsx20(Label, { htmlFor: `${ids}-cols`, children: "Column values" }),
4673
+ /* @__PURE__ */ jsx20(
4674
+ TagInput,
4675
+ {
4676
+ id: `${ids}-cols`,
4677
+ value: cols,
4678
+ onValueChange: setCols,
4679
+ delimiter: [",", "\n"],
4680
+ placeholder: "Type a value, press Enter\u2026"
4681
+ }
4682
+ )
4683
+ ] }) : null,
4684
+ /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-1.5", children: [
4685
+ /* @__PURE__ */ jsx20(Label, { id: `${ids}-layout`, children: "Layout" }),
4686
+ /* @__PURE__ */ jsx20(
4687
+ ToggleGroup2,
4688
+ {
4689
+ type: "single",
4690
+ variant: "segmented",
4691
+ value: layout,
4692
+ onValueChange: (next) => {
4693
+ if (next) setLayout(next);
4694
+ },
4695
+ "aria-labelledby": `${ids}-layout`,
4696
+ className: "w-fit",
4697
+ children: ITERATION_LAYOUTS[kind].map((l) => /* @__PURE__ */ jsx20(ToggleGroupItem2, { value: l, className: "capitalize", children: l }, l))
4698
+ }
4699
+ )
4700
+ ] })
4701
+ ] }),
4702
+ /* @__PURE__ */ jsxs18("div", { className: "flex min-h-0 min-w-0 flex-col gap-4", children: [
4703
+ /* @__PURE__ */ jsxs18("div", { className: "flex min-h-0 flex-col gap-1.5", children: [
4704
+ /* @__PURE__ */ jsxs18(Label, { children: [
4705
+ "Per-",
4706
+ isPivot ? "cell" : "row",
4707
+ " template"
4708
+ ] }),
4709
+ /* @__PURE__ */ jsx20("div", { className: "h-44 min-h-0 overflow-hidden rounded-md border border-border", children: /* @__PURE__ */ jsx20(
4710
+ MarkdownWorkspace,
4711
+ {
4712
+ value: template,
4713
+ onChange: setTemplate,
4714
+ defaultMode: "source",
4715
+ className: "h-full",
4716
+ "aria-label": "Per-cell template"
4717
+ }
4718
+ ) })
4719
+ ] }),
4720
+ /* @__PURE__ */ jsxs18("div", { className: "flex min-h-0 flex-col gap-1.5", children: [
4721
+ /* @__PURE__ */ jsx20(Label, { children: "Live preview" }),
4722
+ /* @__PURE__ */ jsx20(
4723
+ "div",
4724
+ {
4725
+ role: "region",
4726
+ "aria-label": "Live preview",
4727
+ className: "min-h-0 flex-1 overflow-auto rounded-md border border-border bg-card p-3",
4728
+ children: /* @__PURE__ */ jsx20(
4729
+ MarkdownPreview,
4730
+ {
4731
+ evaluateIteration: evaluateEmbedded,
4732
+ evaluate,
4733
+ interpolate,
4734
+ children: previewMarkdown
4735
+ }
4736
+ )
4737
+ }
4738
+ )
4739
+ ] })
4740
+ ] })
4741
+ ] }),
4742
+ /* @__PURE__ */ jsxs18(DialogFooter2, { children: [
4743
+ /* @__PURE__ */ jsx20(Button5, { variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
4744
+ /* @__PURE__ */ jsx20(Button5, { onClick: save, children: value ? "Save" : "Insert" })
4745
+ ] })
4746
+ ] }) });
4747
+ }
4748
+ function IterationBuilderProvider({
4749
+ children,
4750
+ evaluate,
4751
+ interpolate
4752
+ }) {
4753
+ const [request, setRequest] = useState8(null);
4754
+ const seed = request ? builderValueFromParts(request.kind, request.attributes ?? {}, request.template) : void 0;
4755
+ return /* @__PURE__ */ jsxs18(IterationEditContext.Provider, { value: setRequest, children: [
4756
+ children,
4757
+ /* @__PURE__ */ jsx20(
4758
+ IterationBuilderDialog,
4759
+ {
4760
+ open: request != null,
4761
+ onOpenChange: (next) => {
4762
+ if (!next) setRequest(null);
4763
+ },
4764
+ kind: request?.kind ?? "iterate",
4765
+ value: seed,
4766
+ evaluate,
4767
+ interpolate,
4768
+ onSave: (directiveMarkdown) => {
4769
+ const parsed = parseIterationDirective(directiveMarkdown);
4770
+ if (!parsed) return;
4771
+ const { attributes, template } = directivePartsFromValue(parsed);
4772
+ if (request?.onSaveData) request.onSaveData({ attributes, template });
4773
+ else request?.onSave(template);
4774
+ }
4775
+ }
4776
+ )
4777
+ ] });
4778
+ }
4779
+ export {
4780
+ BRAND_DIRECTIVES,
4781
+ BRAND_SLASH_COMMANDS,
4782
+ Bibliography,
4783
+ ProseBlockquote as Blockquote,
4784
+ CALC_FENCE_SEED,
4785
+ CalcBlock,
4786
+ CalcInline,
4787
+ DECISION_STATUSES,
4788
+ DEFAULT_TEMPLATE,
4789
+ DecisionCard,
4790
+ DocumentOutline,
4791
+ ENTITY_KINDS,
4792
+ EntityCard,
4793
+ EntityChip,
4794
+ FootnoteList,
4795
+ ProseHeading as Heading,
4796
+ ProseInlineCode as InlineCode,
4797
+ IterationBlock,
4798
+ IterationBuilderDialog,
4799
+ IterationBuilderProvider,
4800
+ IterationEditContext,
4801
+ IterationTemplateDialog,
4802
+ IterationTemplateProvider,
4803
+ KnowledgeCard,
4804
+ ProseLink as Link,
4805
+ ProseList as List,
4806
+ ProseListItem as ListItem,
4807
+ MARKDOWN_HEADING_REM,
4808
+ MARKDOWN_HEADING_TRACKING,
4809
+ MARKDOWN_HEADING_WEIGHT,
4810
+ MARKDOWN_MEASURE,
4811
+ MarkdownEditor,
4812
+ MarkdownPreview,
4813
+ MarkdownToolbar,
4814
+ MarkdownWorkspace,
4815
+ MathBlock,
4816
+ MathInline,
4817
+ MermaidDiagram,
4818
+ MermaidWorkspace,
4819
+ MetricBlock,
4820
+ MonacoSlashMenu,
4821
+ SlashMenu,
4822
+ TableOfContents,
4823
+ ProseText as Text,
4824
+ Timeline,
4825
+ aiObjectDirectives,
4826
+ annotationForRange,
4827
+ attachCalcMonaco,
4828
+ attachCompletionsMonaco,
4829
+ brandSlashViewPlugins,
4830
+ buildMarkdownPlugins,
4831
+ builderValueFromParts,
4832
+ calcProsePlugins,
4833
+ calcTokenClassName,
4834
+ collectCitations,
4835
+ collectCompletions,
4836
+ computeMarkdownAnnotations,
4837
+ decisionCardVariants,
4838
+ decisionDirective,
4839
+ defaultInterpolate,
4840
+ directivePartsFromValue,
4841
+ emptyBuilderValue,
4842
+ entityChipVariants,
4843
+ entityDirective,
4844
+ evaluateEmbedded,
4845
+ filterSlashCommands,
4846
+ findCalcFences,
4847
+ groupSlashCommands,
4848
+ insertBasicBlock,
4849
+ insertBrandDirective,
4850
+ insertCalcFence,
4851
+ insertDirective,
4852
+ insertHorizontalRule,
4853
+ insertLink,
4854
+ knowledgeDirective,
4855
+ markdownScaleVars,
4856
+ mergeNormalizedEdit,
4857
+ monacoContentAccess,
4858
+ parseAttributes,
4859
+ parseFrontmatter,
4860
+ parseIterationDirective,
4861
+ parseMarkdown,
4862
+ parseMarkdownOutline,
4863
+ proseMirrorContentAccess,
4864
+ remarkBrandDirectives,
4865
+ removedMarkerAt,
4866
+ resolveCalcInsert,
4867
+ resolveReplaceRange,
4868
+ selectionWatchPlugin,
4869
+ serializeFrontmatter,
4870
+ serializeIterationDirective,
4871
+ shiftAnnotations,
4872
+ specFromDirective,
4873
+ splitList,
4874
+ staticMarkdownFromValue,
4875
+ summarizeAnnotations,
4876
+ toggleLinePrefix,
4877
+ transposeIterationValue,
4878
+ triggerQueryStart,
4879
+ useMarkdownOutline,
4880
+ wrapSelection
4881
+ };
4882
+ //# sourceMappingURL=index.js.map