@orchestra-mcp/editor 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2069 @@
1
+ import {
2
+ exportToImage
3
+ } from "./chunk-BUUTSAAO.js";
4
+
5
+ // src/CodeEditor/CodeEditor.tsx
6
+ import { useRef as useRef4, useCallback as useCallback3, useState as useState4 } from "react";
7
+
8
+ // src/CodeEditor/MonacoLoader.tsx
9
+ import { lazy, Suspense } from "react";
10
+ import { jsx } from "react/jsx-runtime";
11
+ var MonacoEditor = lazy(
12
+ () => import("@monaco-editor/react").then((mod) => ({ default: mod.Editor }))
13
+ );
14
+ var MonacoDiffEditor = lazy(
15
+ () => import("@monaco-editor/react").then((mod) => ({ default: mod.DiffEditor }))
16
+ );
17
+ function DefaultLoading() {
18
+ return /* @__PURE__ */ jsx("div", { className: "code-editor__loading", children: /* @__PURE__ */ jsx("div", { className: "code-editor__loading-shimmer" }) });
19
+ }
20
+ function MonacoSuspense({ children, loading }) {
21
+ return /* @__PURE__ */ jsx(Suspense, { fallback: loading ?? /* @__PURE__ */ jsx(DefaultLoading, {}), children });
22
+ }
23
+
24
+ // src/CodeEditor/LegacyCodeEditor.tsx
25
+ import { useRef, useEffect, useCallback, useState } from "react";
26
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
27
+ var LegacyCodeEditor = ({
28
+ value,
29
+ language,
30
+ onChange,
31
+ readOnly = false,
32
+ lineNumbers = true,
33
+ height = 400,
34
+ tabSize = 2,
35
+ fontSize = 14,
36
+ placeholder,
37
+ fileName,
38
+ className,
39
+ onMount
40
+ }) => {
41
+ const textareaRef = useRef(null);
42
+ const lineCountRef = useRef(null);
43
+ const [lineCount, setLineCount] = useState(1);
44
+ const [cursorInfo, setCursorInfo] = useState({ line: 1, col: 1 });
45
+ useEffect(() => {
46
+ const count = (value || "").split("\n").length;
47
+ setLineCount(count);
48
+ }, [value]);
49
+ useEffect(() => {
50
+ if (textareaRef.current && onMount) {
51
+ onMount(textareaRef.current);
52
+ }
53
+ }, [onMount]);
54
+ const handleChange = useCallback(
55
+ (e) => {
56
+ onChange?.(e.target.value);
57
+ },
58
+ [onChange]
59
+ );
60
+ const handleKeyDown = useCallback(
61
+ (e) => {
62
+ if (e.key === "Tab") {
63
+ e.preventDefault();
64
+ const textarea = e.currentTarget;
65
+ const start = textarea.selectionStart;
66
+ const end = textarea.selectionEnd;
67
+ const spaces = " ".repeat(tabSize);
68
+ const newValue = value.substring(0, start) + spaces + value.substring(end);
69
+ onChange?.(newValue);
70
+ requestAnimationFrame(() => {
71
+ textarea.selectionStart = textarea.selectionEnd = start + tabSize;
72
+ });
73
+ }
74
+ },
75
+ [value, onChange, tabSize]
76
+ );
77
+ const handleSelect = useCallback(() => {
78
+ if (!textareaRef.current) return;
79
+ const pos = textareaRef.current.selectionStart;
80
+ const textBefore = value.substring(0, pos);
81
+ const line = textBefore.split("\n").length;
82
+ const col = pos - textBefore.lastIndexOf("\n");
83
+ setCursorInfo({ line, col });
84
+ }, [value]);
85
+ const handleScroll = useCallback(() => {
86
+ if (textareaRef.current && lineCountRef.current) {
87
+ lineCountRef.current.scrollTop = textareaRef.current.scrollTop;
88
+ }
89
+ }, []);
90
+ const heightStyle = typeof height === "number" ? `${height}px` : height;
91
+ const cls = ["code-editor", "code-editor--legacy", className].filter(Boolean).join(" ");
92
+ return /* @__PURE__ */ jsxs("div", { className: cls, style: { "--editor-height": heightStyle, "--editor-font-size": `${fontSize}px` }, children: [
93
+ fileName && /* @__PURE__ */ jsxs("div", { className: "code-editor__header", children: [
94
+ /* @__PURE__ */ jsx2("span", { className: "code-editor__filename", children: fileName }),
95
+ language && /* @__PURE__ */ jsx2("span", { className: "code-editor__badge", children: language })
96
+ ] }),
97
+ /* @__PURE__ */ jsxs("div", { className: "code-editor__body", children: [
98
+ lineNumbers && /* @__PURE__ */ jsx2("div", { className: "code-editor__gutter", ref: lineCountRef, "aria-hidden": "true", children: Array.from({ length: lineCount }, (_, i) => /* @__PURE__ */ jsx2("div", { className: "code-editor__line-number", children: i + 1 }, i)) }),
99
+ /* @__PURE__ */ jsx2(
100
+ "textarea",
101
+ {
102
+ ref: textareaRef,
103
+ className: "code-editor__textarea",
104
+ value,
105
+ onChange: handleChange,
106
+ onKeyDown: handleKeyDown,
107
+ onSelect: handleSelect,
108
+ onScroll: handleScroll,
109
+ readOnly,
110
+ placeholder,
111
+ spellCheck: false,
112
+ autoCapitalize: "off",
113
+ autoComplete: "off",
114
+ autoCorrect: "off",
115
+ "data-language": language
116
+ }
117
+ )
118
+ ] }),
119
+ /* @__PURE__ */ jsxs("div", { className: "code-editor__footer", children: [
120
+ /* @__PURE__ */ jsxs("span", { className: "code-editor__cursor-info", children: [
121
+ "Ln ",
122
+ cursorInfo.line,
123
+ ", Col ",
124
+ cursorInfo.col
125
+ ] }),
126
+ language && /* @__PURE__ */ jsx2("span", { className: "code-editor__language", children: language }),
127
+ readOnly && /* @__PURE__ */ jsx2("span", { className: "code-editor__readonly", children: "Read Only" })
128
+ ] })
129
+ ] });
130
+ };
131
+
132
+ // src/CodeEditor/useMonacoTheme.ts
133
+ import { useEffect as useEffect2, useState as useState2, useRef as useRef2 } from "react";
134
+ import { useMonaco } from "@monaco-editor/react";
135
+ import { THEMES, getColorTheme, onColorThemeChange } from "@orchestra-mcp/theme";
136
+
137
+ // src/CodeEditor/theme-bridge.ts
138
+ function orchestraToMonacoTheme(theme) {
139
+ return {
140
+ base: theme.isLight ? "vs" : "vs-dark",
141
+ inherit: true,
142
+ rules: buildTokenRules(theme.syntax),
143
+ colors: buildEditorColors(theme.colors)
144
+ };
145
+ }
146
+ function buildTokenRules(s) {
147
+ return [
148
+ { token: "keyword", foreground: strip(s.purple) },
149
+ { token: "keyword.control", foreground: strip(s.purple) },
150
+ { token: "storage.type", foreground: strip(s.purple) },
151
+ { token: "string", foreground: strip(s.green) },
152
+ { token: "string.quoted", foreground: strip(s.green) },
153
+ { token: "string.escape", foreground: strip(s.orange) },
154
+ { token: "constant.numeric", foreground: strip(s.orange) },
155
+ { token: "constant.language", foreground: strip(s.orange) },
156
+ { token: "number", foreground: strip(s.orange) },
157
+ { token: "entity.name.function", foreground: strip(s.blue) },
158
+ { token: "support.function", foreground: strip(s.blue) },
159
+ { token: "variable.parameter", foreground: strip(s.cyan) },
160
+ { token: "punctuation", foreground: strip(s.cyan) },
161
+ { token: "delimiter", foreground: strip(s.cyan) },
162
+ { token: "entity.name.type", foreground: strip(s.yellow) },
163
+ { token: "entity.name.class", foreground: strip(s.yellow) },
164
+ { token: "support.type", foreground: strip(s.yellow) },
165
+ { token: "type", foreground: strip(s.yellow) },
166
+ { token: "attribute.name", foreground: strip(s.yellow) },
167
+ { token: "attribute.value", foreground: strip(s.green) },
168
+ { token: "tag", foreground: strip(s.red) },
169
+ { token: "invalid", foreground: strip(s.red) },
170
+ { token: "invalid.illegal", foreground: strip(s.red) },
171
+ { token: "variable.language", foreground: strip(s.red) },
172
+ { token: "regexp", foreground: strip(s.red) },
173
+ { token: "comment", foreground: strip(s.teal), fontStyle: "italic" },
174
+ { token: "entity.other.attribute-name", foreground: strip(s.teal) },
175
+ { token: "invalid.deprecated", foreground: strip(s.error) }
176
+ ];
177
+ }
178
+ function buildEditorColors(c) {
179
+ return {
180
+ "editor.background": c.bg,
181
+ "editor.foreground": c.fg,
182
+ "editor.lineHighlightBackground": c.bgActive,
183
+ "editor.selectionBackground": c.bgSelection,
184
+ "editorCursor.foreground": c.accent,
185
+ "editorLineNumber.foreground": c.fgMuted,
186
+ "editorLineNumber.activeForeground": c.fgBright,
187
+ "editorGutter.background": c.bgAlt,
188
+ "editorWidget.background": c.bgAlt,
189
+ "editorWidget.border": c.border,
190
+ "editorGroup.border": c.border,
191
+ "focusBorder": c.accent,
192
+ "input.background": c.bgContrast,
193
+ "input.foreground": c.fg,
194
+ "input.border": c.border,
195
+ "dropdown.background": c.bgAlt,
196
+ "dropdown.border": c.border,
197
+ "list.hoverBackground": c.bgActive,
198
+ "list.activeSelectionBackground": c.bgSelection,
199
+ "minimap.background": c.bgAlt,
200
+ "scrollbarSlider.background": c.bgActive + "80",
201
+ "scrollbarSlider.hoverBackground": c.bgActive
202
+ };
203
+ }
204
+ function strip(hex) {
205
+ return hex.replace(/^#/, "");
206
+ }
207
+ function getMonacoThemeId(orchestraId) {
208
+ return `orchestra-${orchestraId}`;
209
+ }
210
+
211
+ // src/CodeEditor/useMonacoTheme.ts
212
+ function useMonacoTheme(overrideTheme) {
213
+ const monaco = useMonaco();
214
+ const registered = useRef2(false);
215
+ const [themeId, setThemeId] = useState2(
216
+ () => overrideTheme ?? getMonacoThemeId(getColorTheme())
217
+ );
218
+ useEffect2(() => {
219
+ if (!monaco || registered.current) return;
220
+ registered.current = true;
221
+ for (const theme of THEMES) {
222
+ monaco.editor.defineTheme(
223
+ getMonacoThemeId(theme.id),
224
+ orchestraToMonacoTheme(theme)
225
+ );
226
+ }
227
+ const currentId = overrideTheme ?? getMonacoThemeId(getColorTheme());
228
+ monaco.editor.setTheme(currentId);
229
+ setThemeId(currentId);
230
+ }, [monaco, overrideTheme]);
231
+ useEffect2(() => {
232
+ if (overrideTheme || !monaco) return;
233
+ const unsubscribe = onColorThemeChange((newId) => {
234
+ const monacoId = getMonacoThemeId(newId);
235
+ monaco.editor.setTheme(monacoId);
236
+ setThemeId(monacoId);
237
+ });
238
+ return unsubscribe;
239
+ }, [monaco, overrideTheme]);
240
+ return themeId;
241
+ }
242
+
243
+ // src/CodeEditor/useMonacoKeybindings.ts
244
+ import { useEffect as useEffect3 } from "react";
245
+
246
+ // src/CodeEditor/jetbrains-keymap.ts
247
+ var JETBRAINS_KEYMAP = [
248
+ {
249
+ id: "jb-duplicate-line",
250
+ label: "Duplicate Line",
251
+ keybinding: (KM, KC) => KM.CtrlCmd | KC.KeyD,
252
+ run: (ed) => {
253
+ ed.getAction("editor.action.copyLinesDownAction")?.run();
254
+ }
255
+ },
256
+ {
257
+ id: "jb-delete-line",
258
+ label: "Delete Line",
259
+ keybinding: (KM, KC) => KM.CtrlCmd | KC.KeyY,
260
+ run: (ed) => {
261
+ ed.getAction("editor.action.deleteLines")?.run();
262
+ }
263
+ },
264
+ {
265
+ id: "jb-move-line-up",
266
+ label: "Move Line Up",
267
+ keybinding: (KM, KC) => KM.CtrlCmd | KM.Shift | KC.UpArrow,
268
+ run: (ed) => {
269
+ ed.getAction("editor.action.moveLinesUpAction")?.run();
270
+ }
271
+ },
272
+ {
273
+ id: "jb-move-line-down",
274
+ label: "Move Line Down",
275
+ keybinding: (KM, KC) => KM.CtrlCmd | KM.Shift | KC.DownArrow,
276
+ run: (ed) => {
277
+ ed.getAction("editor.action.moveLinesDownAction")?.run();
278
+ }
279
+ },
280
+ {
281
+ id: "jb-toggle-comment",
282
+ label: "Toggle Line Comment",
283
+ keybinding: (KM, KC) => KM.CtrlCmd | KC.Slash,
284
+ run: (ed) => {
285
+ ed.getAction("editor.action.commentLine")?.run();
286
+ }
287
+ },
288
+ {
289
+ id: "jb-block-comment",
290
+ label: "Toggle Block Comment",
291
+ keybinding: (KM, KC) => KM.CtrlCmd | KM.Shift | KC.Slash,
292
+ run: (ed) => {
293
+ ed.getAction("editor.action.blockComment")?.run();
294
+ }
295
+ },
296
+ {
297
+ id: "jb-format",
298
+ label: "Reformat Code",
299
+ keybinding: (KM, KC) => KM.CtrlCmd | KM.Alt | KC.KeyL,
300
+ run: (ed) => {
301
+ ed.getAction("editor.action.formatDocument")?.run();
302
+ }
303
+ },
304
+ {
305
+ id: "jb-quick-fix",
306
+ label: "Show Quick Fixes",
307
+ keybinding: (KM, KC) => KM.Alt | KC.Enter,
308
+ run: (ed) => {
309
+ ed.getAction("editor.action.quickFix")?.run();
310
+ }
311
+ },
312
+ {
313
+ id: "jb-go-to-declaration",
314
+ label: "Go to Declaration",
315
+ keybinding: (KM, KC) => KM.CtrlCmd | KC.KeyB,
316
+ run: (ed) => {
317
+ ed.getAction("editor.action.revealDefinition")?.run();
318
+ }
319
+ },
320
+ {
321
+ id: "jb-complete-statement",
322
+ label: "Complete Statement",
323
+ keybinding: (KM, KC) => KM.CtrlCmd | KM.Shift | KC.Enter,
324
+ run: (ed) => {
325
+ ed.getAction("editor.action.insertLineAfter")?.run();
326
+ }
327
+ },
328
+ {
329
+ id: "jb-select-word",
330
+ label: "Extend Selection",
331
+ keybinding: (KM, KC) => KM.CtrlCmd | KC.KeyW,
332
+ run: (ed) => {
333
+ ed.getAction("editor.action.smartSelect.expand")?.run();
334
+ }
335
+ },
336
+ {
337
+ id: "jb-shrink-selection",
338
+ label: "Shrink Selection",
339
+ keybinding: (KM, KC) => KM.CtrlCmd | KM.Shift | KC.KeyW,
340
+ run: (ed) => {
341
+ ed.getAction("editor.action.smartSelect.shrink")?.run();
342
+ }
343
+ }
344
+ ];
345
+ function applyJetBrainsKeymap(editor, monaco) {
346
+ for (const kb of JETBRAINS_KEYMAP) {
347
+ editor.addAction({
348
+ id: kb.id,
349
+ label: kb.label,
350
+ keybindings: [kb.keybinding(monaco.KeyMod, monaco.KeyCode)],
351
+ run: kb.run
352
+ });
353
+ }
354
+ }
355
+
356
+ // src/CodeEditor/useMonacoKeybindings.ts
357
+ function useMonacoKeybindings(editor, monaco, keymap) {
358
+ useEffect3(() => {
359
+ if (!editor || !monaco || !keymap || keymap === "default") return;
360
+ if (keymap === "jetbrains") {
361
+ applyJetBrainsKeymap(editor, monaco);
362
+ return;
363
+ }
364
+ if (Array.isArray(keymap)) {
365
+ for (const entry of keymap) {
366
+ editor.addAction({
367
+ id: entry.id,
368
+ label: entry.label,
369
+ keybindings: [entry.keybinding],
370
+ run: entry.handler
371
+ });
372
+ }
373
+ }
374
+ }, [editor, monaco, keymap]);
375
+ }
376
+
377
+ // src/CodeEditor/useLsp.ts
378
+ import { useEffect as useEffect4, useRef as useRef3, useState as useState3, useCallback as useCallback2 } from "react";
379
+ var RECONNECT_DELAY = 3e3;
380
+ var MAX_RETRIES = 3;
381
+ function useLsp({
382
+ monaco,
383
+ editor,
384
+ languageId,
385
+ lspUrl,
386
+ onStatusChange
387
+ }) {
388
+ const [status, setStatus] = useState3("disconnected");
389
+ const connectionRef = useRef3(null);
390
+ const retriesRef = useRef3(0);
391
+ const timerRef = useRef3(null);
392
+ const updateStatus = useCallback2(
393
+ (next) => {
394
+ setStatus(next);
395
+ onStatusChange?.(next);
396
+ },
397
+ [onStatusChange]
398
+ );
399
+ const disconnect = useCallback2(() => {
400
+ if (timerRef.current) {
401
+ clearTimeout(timerRef.current);
402
+ timerRef.current = null;
403
+ }
404
+ if (connectionRef.current) {
405
+ connectionRef.current.dispose();
406
+ connectionRef.current = null;
407
+ }
408
+ }, []);
409
+ const connect = useCallback2(async () => {
410
+ if (!monaco || !editor || !languageId || !lspUrl) return;
411
+ disconnect();
412
+ updateStatus("connecting");
413
+ try {
414
+ const { connectLanguageServer } = await import("./lsp-bridge-OWU2SHS5.js");
415
+ const conn = await connectLanguageServer(monaco, editor, languageId, lspUrl);
416
+ connectionRef.current = conn;
417
+ retriesRef.current = 0;
418
+ updateStatus("connected");
419
+ } catch {
420
+ updateStatus("error");
421
+ if (retriesRef.current < MAX_RETRIES) {
422
+ retriesRef.current += 1;
423
+ timerRef.current = setTimeout(() => connect(), RECONNECT_DELAY);
424
+ }
425
+ }
426
+ }, [monaco, editor, languageId, lspUrl, disconnect, updateStatus]);
427
+ const reconnect = useCallback2(() => {
428
+ retriesRef.current = 0;
429
+ connect();
430
+ }, [connect]);
431
+ useEffect4(() => {
432
+ if (monaco && editor && languageId && lspUrl) {
433
+ connect();
434
+ }
435
+ return () => disconnect();
436
+ }, [monaco, editor, languageId, lspUrl, connect, disconnect]);
437
+ return { status, reconnect };
438
+ }
439
+
440
+ // src/CodeEditor/language-map.ts
441
+ var LANGUAGE_MAP = {
442
+ // Monaco built-in language IDs
443
+ javascript: "javascript",
444
+ typescript: "typescript",
445
+ html: "html",
446
+ css: "css",
447
+ json: "json",
448
+ python: "python",
449
+ go: "go",
450
+ rust: "rust",
451
+ java: "java",
452
+ csharp: "csharp",
453
+ cpp: "cpp",
454
+ c: "c",
455
+ php: "php",
456
+ ruby: "ruby",
457
+ swift: "swift",
458
+ kotlin: "kotlin",
459
+ sql: "sql",
460
+ yaml: "yaml",
461
+ xml: "xml",
462
+ markdown: "markdown",
463
+ shell: "shell",
464
+ scss: "scss",
465
+ less: "less",
466
+ graphql: "graphql",
467
+ dockerfile: "dockerfile",
468
+ lua: "lua",
469
+ perl: "perl",
470
+ r: "r",
471
+ // Common aliases
472
+ ts: "typescript",
473
+ tsx: "typescript",
474
+ js: "javascript",
475
+ jsx: "javascript",
476
+ py: "python",
477
+ rs: "rust",
478
+ rb: "ruby",
479
+ cs: "csharp",
480
+ kt: "kotlin",
481
+ md: "markdown",
482
+ yml: "yaml",
483
+ bash: "shell",
484
+ sh: "shell",
485
+ zsh: "shell",
486
+ toml: "ini",
487
+ proto: "protobuf",
488
+ tf: "hcl"
489
+ };
490
+ function resolveLanguage(lang) {
491
+ if (!lang) return void 0;
492
+ return LANGUAGE_MAP[lang.toLowerCase()] ?? lang.toLowerCase();
493
+ }
494
+ function languageFromFilename(filename) {
495
+ const ext = filename.split(".").pop()?.toLowerCase();
496
+ if (!ext) return void 0;
497
+ return LANGUAGE_MAP[ext];
498
+ }
499
+
500
+ // src/CodeEditor/CodeEditor.tsx
501
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
502
+ function MonacoCodeEditor(props) {
503
+ const {
504
+ value,
505
+ language,
506
+ onChange,
507
+ readOnly = false,
508
+ lineNumbers = true,
509
+ minimap = false,
510
+ wordWrap = "off",
511
+ height = 400,
512
+ tabSize = 2,
513
+ fontSize = 14,
514
+ fileName,
515
+ className,
516
+ onMount,
517
+ beforeMount,
518
+ options,
519
+ keymap,
520
+ lspUrl,
521
+ onLspStatusChange,
522
+ theme: themeOverride,
523
+ loading
524
+ } = props;
525
+ const editorRef = useRef4(null);
526
+ const monacoRef = useRef4(null);
527
+ const [cursorInfo, setCursorInfo] = useState4({ line: 1, col: 1 });
528
+ const monacoTheme = useMonacoTheme(themeOverride);
529
+ useMonacoKeybindings(editorRef.current, monacoRef.current, keymap);
530
+ const resolvedLang = language ? resolveLanguage(language) : fileName ? languageFromFilename(fileName) : void 0;
531
+ const { status: lspStatus } = useLsp({
532
+ monaco: monacoRef.current,
533
+ editor: editorRef.current,
534
+ languageId: resolvedLang,
535
+ lspUrl,
536
+ onStatusChange: onLspStatusChange
537
+ });
538
+ const handleMount = useCallback3(
539
+ (editor, monaco) => {
540
+ editorRef.current = editor;
541
+ monacoRef.current = monaco;
542
+ editor.onDidChangeCursorPosition((e) => {
543
+ setCursorInfo({ line: e.position.lineNumber, col: e.position.column });
544
+ });
545
+ onMount?.(editor, monaco);
546
+ },
547
+ [onMount]
548
+ );
549
+ const handleChange = useCallback3(
550
+ (val) => {
551
+ onChange?.(val ?? "");
552
+ },
553
+ [onChange]
554
+ );
555
+ const heightStyle = typeof height === "number" ? `${height}px` : height;
556
+ const cls = ["code-editor", className].filter(Boolean).join(" ");
557
+ const mergedOptions = {
558
+ readOnly,
559
+ lineNumbers: lineNumbers ? "on" : "off",
560
+ minimap: { enabled: minimap },
561
+ wordWrap,
562
+ tabSize,
563
+ fontSize,
564
+ scrollBeyondLastLine: false,
565
+ automaticLayout: true,
566
+ padding: { top: 8, bottom: 8 },
567
+ contextmenu: true,
568
+ find: { addExtraSpaceOnTop: false, autoFindInSelection: "multiline" },
569
+ suggest: { showMethods: true, showFunctions: true, showSnippets: true },
570
+ ...options
571
+ };
572
+ return /* @__PURE__ */ jsxs2("div", { className: cls, children: [
573
+ fileName && /* @__PURE__ */ jsxs2("div", { className: "code-editor__header", children: [
574
+ /* @__PURE__ */ jsx3("span", { className: "code-editor__filename", children: fileName }),
575
+ resolvedLang && /* @__PURE__ */ jsx3("span", { className: "code-editor__badge", children: resolvedLang })
576
+ ] }),
577
+ /* @__PURE__ */ jsx3(MonacoSuspense, { loading, children: /* @__PURE__ */ jsx3(
578
+ MonacoEditor,
579
+ {
580
+ height: heightStyle,
581
+ language: resolvedLang,
582
+ value,
583
+ theme: monacoTheme,
584
+ options: mergedOptions,
585
+ onChange: handleChange,
586
+ onMount: handleMount,
587
+ beforeMount
588
+ }
589
+ ) }),
590
+ /* @__PURE__ */ jsxs2("div", { className: "code-editor__footer", children: [
591
+ /* @__PURE__ */ jsxs2("span", { className: "code-editor__cursor-info", children: [
592
+ "Ln ",
593
+ cursorInfo.line,
594
+ ", Col ",
595
+ cursorInfo.col
596
+ ] }),
597
+ resolvedLang && /* @__PURE__ */ jsx3("span", { className: "code-editor__language", children: resolvedLang }),
598
+ readOnly && /* @__PURE__ */ jsx3("span", { className: "code-editor__readonly", children: "Read Only" }),
599
+ lspUrl && /* @__PURE__ */ jsx3("span", { className: `code-editor__lsp code-editor__lsp--${lspStatus}`, children: lspStatus === "connected" ? "LSP" : lspStatus === "connecting" ? "LSP..." : lspStatus === "error" ? "LSP \u2717" : "" })
600
+ ] })
601
+ ] });
602
+ }
603
+ var CodeEditor = (props) => {
604
+ if (props.useLegacy) {
605
+ const {
606
+ onMount: _onMount,
607
+ beforeMount: _beforeMount,
608
+ options: _options,
609
+ keymap: _keymap,
610
+ lspUrl: _lspUrl,
611
+ onLspStatusChange: _onLspStatusChange,
612
+ theme: _theme,
613
+ loading: _loading,
614
+ useLegacy: _,
615
+ ...legacyProps
616
+ } = props;
617
+ return /* @__PURE__ */ jsx3(LegacyCodeEditor, { ...legacyProps });
618
+ }
619
+ return /* @__PURE__ */ jsx3(MonacoCodeEditor, { ...props });
620
+ };
621
+
622
+ // src/CodeEditor/CodeDiffEditor.tsx
623
+ import { jsx as jsx4 } from "react/jsx-runtime";
624
+ var CodeDiffEditor = ({
625
+ original,
626
+ modified,
627
+ language,
628
+ fileName,
629
+ height = 300,
630
+ readOnly = true,
631
+ renderSideBySide = false,
632
+ className,
633
+ loading
634
+ }) => {
635
+ const monacoTheme = useMonacoTheme();
636
+ const resolvedLang = language ? resolveLanguage(language) : fileName ? languageFromFilename(fileName) : void 0;
637
+ const heightStyle = typeof height === "number" ? `${height}px` : height;
638
+ const cls = ["code-editor", className].filter(Boolean).join(" ");
639
+ return /* @__PURE__ */ jsx4("div", { className: cls, children: /* @__PURE__ */ jsx4(MonacoSuspense, { loading, children: /* @__PURE__ */ jsx4(
640
+ MonacoDiffEditor,
641
+ {
642
+ height: heightStyle,
643
+ language: resolvedLang,
644
+ original,
645
+ modified,
646
+ theme: monacoTheme,
647
+ options: {
648
+ readOnly,
649
+ renderSideBySide,
650
+ minimap: { enabled: false },
651
+ scrollBeyondLastLine: false,
652
+ lineNumbers: "on",
653
+ automaticLayout: true,
654
+ padding: { top: 8, bottom: 8 }
655
+ }
656
+ }
657
+ ) }) });
658
+ };
659
+
660
+ // src/CodeBlock/CodeBlock.tsx
661
+ import { useState as useState5, useCallback as useCallback4, useRef as useRef5 } from "react";
662
+ import { saveFile, uuidFilename } from "@orchestra-mcp/widgets";
663
+
664
+ // src/CodeBlock/highlighter.ts
665
+ var COMMON_KEYWORDS = [
666
+ "if",
667
+ "else",
668
+ "for",
669
+ "while",
670
+ "return",
671
+ "function",
672
+ "class",
673
+ "const",
674
+ "let",
675
+ "var",
676
+ "import",
677
+ "export",
678
+ "from",
679
+ "default",
680
+ "new",
681
+ "this",
682
+ "throw",
683
+ "try",
684
+ "catch",
685
+ "finally",
686
+ "switch",
687
+ "case",
688
+ "break",
689
+ "continue",
690
+ "do",
691
+ "in",
692
+ "of",
693
+ "typeof",
694
+ "instanceof",
695
+ "void",
696
+ "delete",
697
+ "async",
698
+ "await",
699
+ "yield",
700
+ "static",
701
+ "extends",
702
+ "super",
703
+ "implements",
704
+ "interface",
705
+ "type",
706
+ "enum",
707
+ "abstract",
708
+ "readonly"
709
+ ];
710
+ var PHP_KEYWORDS = [
711
+ "namespace",
712
+ "use",
713
+ "class",
714
+ "function",
715
+ "public",
716
+ "private",
717
+ "protected",
718
+ "static",
719
+ "return",
720
+ "if",
721
+ "else",
722
+ "foreach",
723
+ "as",
724
+ "new",
725
+ "extends",
726
+ "implements",
727
+ "interface",
728
+ "abstract",
729
+ "final",
730
+ "const",
731
+ "echo",
732
+ "throw",
733
+ "try",
734
+ "catch",
735
+ "finally",
736
+ "match",
737
+ "fn",
738
+ "yield",
739
+ "readonly"
740
+ ];
741
+ var GO_KEYWORDS = [
742
+ "func",
743
+ "package",
744
+ "import",
745
+ "type",
746
+ "struct",
747
+ "interface",
748
+ "map",
749
+ "chan",
750
+ "go",
751
+ "defer",
752
+ "return",
753
+ "if",
754
+ "else",
755
+ "for",
756
+ "range",
757
+ "switch",
758
+ "case",
759
+ "default",
760
+ "break",
761
+ "continue",
762
+ "select",
763
+ "var",
764
+ "const",
765
+ "fallthrough",
766
+ "goto"
767
+ ];
768
+ var RUST_KEYWORDS = [
769
+ "fn",
770
+ "let",
771
+ "mut",
772
+ "pub",
773
+ "struct",
774
+ "enum",
775
+ "impl",
776
+ "trait",
777
+ "use",
778
+ "mod",
779
+ "crate",
780
+ "self",
781
+ "super",
782
+ "match",
783
+ "if",
784
+ "else",
785
+ "for",
786
+ "while",
787
+ "loop",
788
+ "return",
789
+ "where",
790
+ "async",
791
+ "await",
792
+ "move",
793
+ "ref",
794
+ "type",
795
+ "const",
796
+ "static",
797
+ "unsafe",
798
+ "extern",
799
+ "dyn",
800
+ "as",
801
+ "in"
802
+ ];
803
+ var PYTHON_KEYWORDS = [
804
+ "def",
805
+ "class",
806
+ "import",
807
+ "from",
808
+ "return",
809
+ "if",
810
+ "elif",
811
+ "else",
812
+ "for",
813
+ "while",
814
+ "in",
815
+ "not",
816
+ "and",
817
+ "or",
818
+ "is",
819
+ "with",
820
+ "as",
821
+ "try",
822
+ "except",
823
+ "finally",
824
+ "raise",
825
+ "pass",
826
+ "break",
827
+ "continue",
828
+ "lambda",
829
+ "yield",
830
+ "global",
831
+ "nonlocal",
832
+ "assert",
833
+ "del",
834
+ "async",
835
+ "await"
836
+ ];
837
+ var JAVA_KEYWORDS = [
838
+ "class",
839
+ "interface",
840
+ "extends",
841
+ "implements",
842
+ "public",
843
+ "private",
844
+ "protected",
845
+ "static",
846
+ "final",
847
+ "abstract",
848
+ "void",
849
+ "return",
850
+ "if",
851
+ "else",
852
+ "for",
853
+ "while",
854
+ "do",
855
+ "switch",
856
+ "case",
857
+ "break",
858
+ "continue",
859
+ "new",
860
+ "throw",
861
+ "try",
862
+ "catch",
863
+ "finally",
864
+ "import",
865
+ "package",
866
+ "this",
867
+ "super",
868
+ "synchronized",
869
+ "volatile"
870
+ ];
871
+ var C_KEYWORDS = [
872
+ "if",
873
+ "else",
874
+ "for",
875
+ "while",
876
+ "do",
877
+ "switch",
878
+ "case",
879
+ "break",
880
+ "continue",
881
+ "return",
882
+ "struct",
883
+ "typedef",
884
+ "enum",
885
+ "union",
886
+ "void",
887
+ "int",
888
+ "char",
889
+ "float",
890
+ "double",
891
+ "long",
892
+ "short",
893
+ "unsigned",
894
+ "signed",
895
+ "const",
896
+ "static",
897
+ "extern",
898
+ "sizeof",
899
+ "include",
900
+ "define",
901
+ "ifdef",
902
+ "ifndef",
903
+ "endif",
904
+ "NULL"
905
+ ];
906
+ var RUBY_KEYWORDS = [
907
+ "def",
908
+ "class",
909
+ "module",
910
+ "end",
911
+ "if",
912
+ "elsif",
913
+ "else",
914
+ "unless",
915
+ "while",
916
+ "until",
917
+ "for",
918
+ "do",
919
+ "begin",
920
+ "rescue",
921
+ "ensure",
922
+ "raise",
923
+ "return",
924
+ "yield",
925
+ "block_given",
926
+ "require",
927
+ "include",
928
+ "extend",
929
+ "attr_accessor",
930
+ "attr_reader",
931
+ "nil",
932
+ "true",
933
+ "false",
934
+ "self",
935
+ "super",
936
+ "puts",
937
+ "print"
938
+ ];
939
+ var BASH_KEYWORDS = [
940
+ "if",
941
+ "then",
942
+ "else",
943
+ "elif",
944
+ "fi",
945
+ "for",
946
+ "while",
947
+ "do",
948
+ "done",
949
+ "case",
950
+ "esac",
951
+ "function",
952
+ "return",
953
+ "exit",
954
+ "echo",
955
+ "read",
956
+ "export",
957
+ "source",
958
+ "local",
959
+ "readonly",
960
+ "shift",
961
+ "set",
962
+ "unset",
963
+ "trap",
964
+ "eval",
965
+ "exec",
966
+ "cd",
967
+ "pwd",
968
+ "test",
969
+ "true",
970
+ "false"
971
+ ];
972
+ var SQL_KEYWORDS = [
973
+ "SELECT",
974
+ "FROM",
975
+ "WHERE",
976
+ "INSERT",
977
+ "INTO",
978
+ "VALUES",
979
+ "UPDATE",
980
+ "SET",
981
+ "DELETE",
982
+ "CREATE",
983
+ "TABLE",
984
+ "ALTER",
985
+ "DROP",
986
+ "INDEX",
987
+ "JOIN",
988
+ "LEFT",
989
+ "RIGHT",
990
+ "INNER",
991
+ "OUTER",
992
+ "ON",
993
+ "AND",
994
+ "OR",
995
+ "NOT",
996
+ "NULL",
997
+ "AS",
998
+ "ORDER",
999
+ "BY",
1000
+ "GROUP",
1001
+ "HAVING",
1002
+ "LIMIT",
1003
+ "OFFSET",
1004
+ "DISTINCT",
1005
+ "UNION",
1006
+ "EXISTS",
1007
+ "IN",
1008
+ "BETWEEN",
1009
+ "LIKE",
1010
+ "IS",
1011
+ "PRIMARY",
1012
+ "KEY",
1013
+ "FOREIGN",
1014
+ "REFERENCES",
1015
+ "CASCADE",
1016
+ "DEFAULT",
1017
+ "CONSTRAINT",
1018
+ "BEGIN",
1019
+ "COMMIT",
1020
+ "ROLLBACK"
1021
+ ];
1022
+ var SWIFT_KEYWORDS = [
1023
+ "func",
1024
+ "var",
1025
+ "let",
1026
+ "class",
1027
+ "struct",
1028
+ "enum",
1029
+ "protocol",
1030
+ "extension",
1031
+ "import",
1032
+ "return",
1033
+ "if",
1034
+ "else",
1035
+ "guard",
1036
+ "switch",
1037
+ "case",
1038
+ "default",
1039
+ "for",
1040
+ "while",
1041
+ "repeat",
1042
+ "break",
1043
+ "continue",
1044
+ "throw",
1045
+ "try",
1046
+ "catch",
1047
+ "self",
1048
+ "super",
1049
+ "init",
1050
+ "deinit",
1051
+ "nil",
1052
+ "true",
1053
+ "false",
1054
+ "override",
1055
+ "private",
1056
+ "public",
1057
+ "internal",
1058
+ "open",
1059
+ "static",
1060
+ "async",
1061
+ "await"
1062
+ ];
1063
+ var KOTLIN_KEYWORDS = [
1064
+ "fun",
1065
+ "val",
1066
+ "var",
1067
+ "class",
1068
+ "object",
1069
+ "interface",
1070
+ "abstract",
1071
+ "override",
1072
+ "open",
1073
+ "data",
1074
+ "sealed",
1075
+ "import",
1076
+ "package",
1077
+ "return",
1078
+ "if",
1079
+ "else",
1080
+ "when",
1081
+ "for",
1082
+ "while",
1083
+ "do",
1084
+ "break",
1085
+ "continue",
1086
+ "throw",
1087
+ "try",
1088
+ "catch",
1089
+ "finally",
1090
+ "this",
1091
+ "super",
1092
+ "null",
1093
+ "true",
1094
+ "false",
1095
+ "is",
1096
+ "as",
1097
+ "in",
1098
+ "suspend",
1099
+ "companion",
1100
+ "init",
1101
+ "private",
1102
+ "public",
1103
+ "internal",
1104
+ "protected"
1105
+ ];
1106
+ var CSHARP_KEYWORDS = [
1107
+ "class",
1108
+ "interface",
1109
+ "struct",
1110
+ "enum",
1111
+ "namespace",
1112
+ "using",
1113
+ "public",
1114
+ "private",
1115
+ "protected",
1116
+ "internal",
1117
+ "static",
1118
+ "void",
1119
+ "return",
1120
+ "if",
1121
+ "else",
1122
+ "for",
1123
+ "foreach",
1124
+ "while",
1125
+ "do",
1126
+ "switch",
1127
+ "case",
1128
+ "break",
1129
+ "continue",
1130
+ "new",
1131
+ "throw",
1132
+ "try",
1133
+ "catch",
1134
+ "finally",
1135
+ "async",
1136
+ "await",
1137
+ "var",
1138
+ "const",
1139
+ "readonly",
1140
+ "override",
1141
+ "virtual",
1142
+ "abstract",
1143
+ "sealed",
1144
+ "partial",
1145
+ "this",
1146
+ "base",
1147
+ "null",
1148
+ "true",
1149
+ "false",
1150
+ "out",
1151
+ "ref",
1152
+ "in",
1153
+ "yield"
1154
+ ];
1155
+ function getKeywords(lang) {
1156
+ switch (lang) {
1157
+ case "php":
1158
+ return PHP_KEYWORDS;
1159
+ case "go":
1160
+ case "golang":
1161
+ return GO_KEYWORDS;
1162
+ case "rust":
1163
+ case "rs":
1164
+ return RUST_KEYWORDS;
1165
+ case "python":
1166
+ case "py":
1167
+ return PYTHON_KEYWORDS;
1168
+ case "java":
1169
+ return JAVA_KEYWORDS;
1170
+ case "c":
1171
+ case "cpp":
1172
+ case "c++":
1173
+ case "h":
1174
+ return C_KEYWORDS;
1175
+ case "ruby":
1176
+ case "rb":
1177
+ return RUBY_KEYWORDS;
1178
+ case "bash":
1179
+ case "sh":
1180
+ case "shell":
1181
+ case "zsh":
1182
+ return BASH_KEYWORDS;
1183
+ case "sql":
1184
+ case "mysql":
1185
+ case "postgresql":
1186
+ case "postgres":
1187
+ case "sqlite":
1188
+ return SQL_KEYWORDS;
1189
+ case "swift":
1190
+ return SWIFT_KEYWORDS;
1191
+ case "kotlin":
1192
+ case "kt":
1193
+ return KOTLIN_KEYWORDS;
1194
+ case "csharp":
1195
+ case "cs":
1196
+ case "c#":
1197
+ return CSHARP_KEYWORDS;
1198
+ default:
1199
+ return COMMON_KEYWORDS;
1200
+ }
1201
+ }
1202
+ function buildRules(lang) {
1203
+ const keywords = getKeywords(lang);
1204
+ const isSql = lang === "sql" || lang === "mysql" || lang === "postgresql" || lang === "postgres" || lang === "sqlite";
1205
+ const kwFlags = isSql ? "gi" : "g";
1206
+ const kwPattern = new RegExp(`\\b(${keywords.join("|")})\\b`, kwFlags);
1207
+ const hasDollarVars = lang === "php" || lang === "bash" || lang === "sh" || lang === "shell" || lang === "zsh";
1208
+ return [
1209
+ // Comments: // and # single-line
1210
+ { pattern: /\/\/.*$/gm, className: "syn-comment" },
1211
+ { pattern: /#.*$/gm, className: "syn-comment" },
1212
+ // Multi-line comments
1213
+ { pattern: /\/\*[\s\S]*?\*\//g, className: "syn-comment" },
1214
+ // SQL single-line comments (--)
1215
+ ...isSql ? [{ pattern: /--.*$/gm, className: "syn-comment" }] : [],
1216
+ // Strings: double-quoted
1217
+ { pattern: /"(?:[^"\\]|\\.)*"/g, className: "syn-string" },
1218
+ // Strings: single-quoted
1219
+ { pattern: /'(?:[^'\\]|\\.)*'/g, className: "syn-string" },
1220
+ // Strings: backtick
1221
+ { pattern: /`(?:[^`\\]|\\.)*`/g, className: "syn-string" },
1222
+ // Numbers
1223
+ { pattern: /\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/gi, className: "syn-number" },
1224
+ // PHP/Bash variables
1225
+ ...hasDollarVars ? [{ pattern: /\$[a-zA-Z_]\w*/g, className: "syn-variable" }] : [],
1226
+ // Type names (PascalCase)
1227
+ { pattern: /\b[A-Z][a-zA-Z0-9_]*\b/g, className: "syn-type" },
1228
+ // Keywords
1229
+ { pattern: kwPattern, className: "syn-keyword" },
1230
+ // Punctuation: arrows, operators
1231
+ { pattern: /=>|->|::|\.\.\.|\.\.|[{}()[\];,.:?]/g, className: "syn-punctuation" }
1232
+ ];
1233
+ }
1234
+ function tokenizeLine(line, lang) {
1235
+ const rules = buildRules(lang);
1236
+ const markers = [];
1237
+ for (const rule of rules) {
1238
+ rule.pattern.lastIndex = 0;
1239
+ let match;
1240
+ while ((match = rule.pattern.exec(line)) !== null) {
1241
+ markers.push({
1242
+ start: match.index,
1243
+ end: match.index + match[0].length,
1244
+ className: rule.className
1245
+ });
1246
+ }
1247
+ }
1248
+ markers.sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
1249
+ const resolved = [];
1250
+ let cursor = 0;
1251
+ for (const m of markers) {
1252
+ if (m.start >= cursor) {
1253
+ resolved.push(m);
1254
+ cursor = m.end;
1255
+ }
1256
+ }
1257
+ const tokens = [];
1258
+ let pos = 0;
1259
+ for (const m of resolved) {
1260
+ if (m.start > pos) {
1261
+ tokens.push({ text: line.slice(pos, m.start), start: pos, end: m.start });
1262
+ }
1263
+ tokens.push({
1264
+ text: line.slice(m.start, m.end),
1265
+ className: m.className,
1266
+ start: m.start,
1267
+ end: m.end
1268
+ });
1269
+ pos = m.end;
1270
+ }
1271
+ if (pos < line.length) {
1272
+ tokens.push({ text: line.slice(pos), start: pos, end: line.length });
1273
+ }
1274
+ return tokens.length > 0 ? tokens : [{ text: line || "\n", start: 0, end: line.length }];
1275
+ }
1276
+
1277
+ // src/CodeBlock/CodeBlock.tsx
1278
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1279
+ var LANGUAGE_EXTENSIONS = {
1280
+ javascript: "js",
1281
+ typescript: "ts",
1282
+ python: "py",
1283
+ ruby: "rb",
1284
+ rust: "rs",
1285
+ go: "go",
1286
+ java: "java",
1287
+ css: "css",
1288
+ html: "html",
1289
+ json: "json",
1290
+ yaml: "yml",
1291
+ bash: "sh",
1292
+ shell: "sh",
1293
+ sql: "sql",
1294
+ markdown: "md",
1295
+ php: "php",
1296
+ csharp: "cs",
1297
+ swift: "swift",
1298
+ kotlin: "kt"
1299
+ };
1300
+ var WrapIcon = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx5("path", { d: "M2 4h12M2 8h8a2 2 0 0 1 0 4H8l1.5-1.5M9.5 12L8 10.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1301
+ var CopyIcon = () => /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
1302
+ /* @__PURE__ */ jsx5("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
1303
+ /* @__PURE__ */ jsx5("path", { d: "M11 3H4.5A1.5 1.5 0 0 0 3 4.5V11", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
1304
+ ] });
1305
+ var CheckIcon = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx5("path", { d: "M3.5 8.5L6.5 11.5L12.5 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1306
+ var DownloadIcon = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx5("path", { d: "M8 2v8m0 0L5 7m3 3l3-3M3 12h10", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1307
+ var ImageIcon = () => /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
1308
+ /* @__PURE__ */ jsx5("rect", { x: "2", y: "2", width: "12", height: "12", rx: "2", stroke: "currentColor", strokeWidth: "1.5" }),
1309
+ /* @__PURE__ */ jsx5("circle", { cx: "6", cy: "6", r: "1.5", fill: "currentColor" }),
1310
+ /* @__PURE__ */ jsx5("path", { d: "M2 11l3-3 2 2 3-3 4 4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1311
+ ] });
1312
+ var MermaidIcon = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx5("path", { d: "M8 2v4M8 6L4 10M8 6l4 4M4 10v2M8 10v2M12 10v2", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
1313
+ var CodeBlock = ({
1314
+ code,
1315
+ language,
1316
+ showLineNumbers = false,
1317
+ highlightLines = [],
1318
+ copyable = true,
1319
+ exportable = false,
1320
+ exportImage = false,
1321
+ filename,
1322
+ maxHeight,
1323
+ wrapLines = false,
1324
+ showWindowDots = true,
1325
+ onConvertToMermaid,
1326
+ className
1327
+ }) => {
1328
+ const [copied, setCopied] = useState5(false);
1329
+ const [wrapped, setWrapped] = useState5(wrapLines);
1330
+ const timerRef = useRef5(null);
1331
+ const blockRef = useRef5(null);
1332
+ const lines = code.split("\n");
1333
+ const highlightSet = new Set(highlightLines);
1334
+ const handleCopy = useCallback4(() => {
1335
+ navigator.clipboard.writeText(code).then(() => {
1336
+ setCopied(true);
1337
+ if (timerRef.current) clearTimeout(timerRef.current);
1338
+ timerRef.current = setTimeout(() => setCopied(false), 2e3);
1339
+ });
1340
+ }, [code]);
1341
+ const handleExport = useCallback4(async () => {
1342
+ const ext = language ? LANGUAGE_EXTENSIONS[language] ?? language : "txt";
1343
+ const base = filename ?? "code";
1344
+ const name = uuidFilename(base, ext);
1345
+ await saveFile(code, name, "text/plain");
1346
+ }, [code, language, filename]);
1347
+ const handleExportImage = useCallback4(async () => {
1348
+ if (!blockRef.current) return;
1349
+ const { exportToImage: exportToImage2 } = await import("./exportToImage-BFNJIYE2.js");
1350
+ const name = filename ?? `code-${language ?? "snippet"}`;
1351
+ await exportToImage2(blockRef.current, name);
1352
+ }, [filename, language]);
1353
+ const handleToggleWrap = useCallback4(() => {
1354
+ setWrapped((prev) => !prev);
1355
+ }, []);
1356
+ const showHeader = language || copyable || exportable || exportImage;
1357
+ const bodyStyle = maxHeight ? { maxHeight: `${maxHeight}px` } : void 0;
1358
+ const wrapperClass = ["code-block", className].filter(Boolean).join(" ");
1359
+ return /* @__PURE__ */ jsxs3("div", { className: wrapperClass, ref: blockRef, children: [
1360
+ showHeader && /* @__PURE__ */ jsxs3("div", { className: "code-block__header", children: [
1361
+ /* @__PURE__ */ jsxs3("div", { className: "code-block__header-left", children: [
1362
+ showWindowDots && /* @__PURE__ */ jsxs3("div", { className: "code-block__dots", "aria-hidden": "true", children: [
1363
+ /* @__PURE__ */ jsx5("span", { className: "code-block__dot code-block__dot--red" }),
1364
+ /* @__PURE__ */ jsx5("span", { className: "code-block__dot code-block__dot--yellow" }),
1365
+ /* @__PURE__ */ jsx5("span", { className: "code-block__dot code-block__dot--green" })
1366
+ ] }),
1367
+ language && /* @__PURE__ */ jsx5("span", { className: "code-block__badge", children: language })
1368
+ ] }),
1369
+ /* @__PURE__ */ jsxs3("div", { className: "code-block__actions", children: [
1370
+ /* @__PURE__ */ jsx5(
1371
+ "button",
1372
+ {
1373
+ type: "button",
1374
+ className: "code-block__btn",
1375
+ onClick: handleToggleWrap,
1376
+ title: wrapped ? "Disable word wrap" : "Enable word wrap",
1377
+ "aria-label": "Toggle word wrap",
1378
+ children: /* @__PURE__ */ jsx5(WrapIcon, {})
1379
+ }
1380
+ ),
1381
+ copyable && /* @__PURE__ */ jsx5(
1382
+ "button",
1383
+ {
1384
+ type: "button",
1385
+ className: `code-block__btn ${copied ? "code-block__btn--copied" : ""}`,
1386
+ onClick: handleCopy,
1387
+ "aria-label": "Copy code",
1388
+ children: copied ? /* @__PURE__ */ jsx5(CheckIcon, {}) : /* @__PURE__ */ jsx5(CopyIcon, {})
1389
+ }
1390
+ ),
1391
+ exportable && /* @__PURE__ */ jsx5(
1392
+ "button",
1393
+ {
1394
+ type: "button",
1395
+ className: "code-block__btn",
1396
+ onClick: handleExport,
1397
+ "aria-label": "Download code",
1398
+ children: /* @__PURE__ */ jsx5(DownloadIcon, {})
1399
+ }
1400
+ ),
1401
+ exportImage && /* @__PURE__ */ jsx5(
1402
+ "button",
1403
+ {
1404
+ type: "button",
1405
+ className: "code-block__btn",
1406
+ onClick: handleExportImage,
1407
+ "aria-label": "Export as image",
1408
+ children: /* @__PURE__ */ jsx5(ImageIcon, {})
1409
+ }
1410
+ ),
1411
+ onConvertToMermaid && language !== "mermaid" && /* @__PURE__ */ jsx5(
1412
+ "button",
1413
+ {
1414
+ type: "button",
1415
+ className: "code-block__btn",
1416
+ onClick: () => onConvertToMermaid(code),
1417
+ title: "Convert to Mermaid diagram",
1418
+ "aria-label": "Convert to Mermaid diagram",
1419
+ children: /* @__PURE__ */ jsx5(MermaidIcon, {})
1420
+ }
1421
+ )
1422
+ ] })
1423
+ ] }),
1424
+ /* @__PURE__ */ jsxs3(
1425
+ "div",
1426
+ {
1427
+ className: [
1428
+ "code-block__body",
1429
+ maxHeight ? "code-block__body--scrollable" : ""
1430
+ ].filter(Boolean).join(" "),
1431
+ style: bodyStyle,
1432
+ children: [
1433
+ showLineNumbers && /* @__PURE__ */ jsx5("div", { className: "code-block__gutter", "aria-hidden": "true", children: lines.map((_, i) => /* @__PURE__ */ jsx5("div", { className: "code-block__line-number", children: i + 1 }, i)) }),
1434
+ /* @__PURE__ */ jsx5("pre", { className: `code-block__code ${wrapped ? "code-block__code--wrapped" : ""}`, children: /* @__PURE__ */ jsx5("code", { children: lines.map((line, i) => /* @__PURE__ */ jsx5(
1435
+ "div",
1436
+ {
1437
+ className: [
1438
+ "code-block__line",
1439
+ highlightSet.has(i + 1) ? "code-block__line--highlighted" : ""
1440
+ ].filter(Boolean).join(" "),
1441
+ children: language ? tokenizeLine(line, language).map(
1442
+ (tok, j) => tok.className ? /* @__PURE__ */ jsx5("span", { className: tok.className, children: tok.text }, j) : /* @__PURE__ */ jsx5("span", { children: tok.text }, j)
1443
+ ) : line || "\n"
1444
+ },
1445
+ i
1446
+ )) }) })
1447
+ ]
1448
+ }
1449
+ )
1450
+ ] });
1451
+ };
1452
+
1453
+ // src/GitDiffView/GitDiffView.tsx
1454
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1455
+ function computeLCS(a, b) {
1456
+ const m = a.length;
1457
+ const n = b.length;
1458
+ const dp = Array.from(
1459
+ { length: m + 1 },
1460
+ () => Array(n + 1).fill(0)
1461
+ );
1462
+ for (let i2 = 1; i2 <= m; i2++) {
1463
+ for (let j2 = 1; j2 <= n; j2++) {
1464
+ dp[i2][j2] = a[i2 - 1] === b[j2 - 1] ? dp[i2 - 1][j2 - 1] + 1 : Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
1465
+ }
1466
+ }
1467
+ const result = [];
1468
+ let i = m;
1469
+ let j = n;
1470
+ while (i > 0 && j > 0) {
1471
+ if (a[i - 1] === b[j - 1]) {
1472
+ result.unshift(a[i - 1]);
1473
+ i--;
1474
+ j--;
1475
+ } else if (dp[i - 1][j] > dp[i][j - 1]) {
1476
+ i--;
1477
+ } else {
1478
+ j--;
1479
+ }
1480
+ }
1481
+ return result;
1482
+ }
1483
+ function computeDiff(original, modified) {
1484
+ const oldLines = original.split("\n");
1485
+ const newLines = modified.split("\n");
1486
+ const lcs = computeLCS(oldLines, newLines);
1487
+ const result = [];
1488
+ let oldIdx = 0;
1489
+ let newIdx = 0;
1490
+ let lcsIdx = 0;
1491
+ while (oldIdx < oldLines.length || newIdx < newLines.length) {
1492
+ if (lcsIdx < lcs.length && oldIdx < oldLines.length && oldLines[oldIdx] === lcs[lcsIdx] && newIdx < newLines.length && newLines[newIdx] === lcs[lcsIdx]) {
1493
+ result.push({
1494
+ type: "unchanged",
1495
+ content: lcs[lcsIdx],
1496
+ oldLineNumber: oldIdx + 1,
1497
+ newLineNumber: newIdx + 1
1498
+ });
1499
+ oldIdx++;
1500
+ newIdx++;
1501
+ lcsIdx++;
1502
+ } else if (oldIdx < oldLines.length && (lcsIdx >= lcs.length || oldLines[oldIdx] !== lcs[lcsIdx])) {
1503
+ result.push({
1504
+ type: "removed",
1505
+ content: oldLines[oldIdx],
1506
+ oldLineNumber: oldIdx + 1
1507
+ });
1508
+ oldIdx++;
1509
+ } else if (newIdx < newLines.length && (lcsIdx >= lcs.length || newLines[newIdx] !== lcs[lcsIdx])) {
1510
+ result.push({
1511
+ type: "added",
1512
+ content: newLines[newIdx],
1513
+ newLineNumber: newIdx + 1
1514
+ });
1515
+ newIdx++;
1516
+ }
1517
+ }
1518
+ return result;
1519
+ }
1520
+ var GitDiffView = ({
1521
+ original,
1522
+ modified,
1523
+ language,
1524
+ fileName,
1525
+ mode = "inline",
1526
+ lineNumbers = true,
1527
+ className
1528
+ }) => {
1529
+ const diffLines = computeDiff(original, modified);
1530
+ const added = diffLines.filter((l) => l.type === "added").length;
1531
+ const removed = diffLines.filter((l) => l.type === "removed").length;
1532
+ const cls = [
1533
+ "git-diff",
1534
+ mode === "side-by-side" ? "git-diff--sbs" : "git-diff--inline",
1535
+ className
1536
+ ].filter(Boolean).join(" ");
1537
+ return /* @__PURE__ */ jsxs4("div", { className: cls, children: [
1538
+ /* @__PURE__ */ jsxs4("div", { className: "git-diff__header", children: [
1539
+ /* @__PURE__ */ jsxs4("div", { className: "git-diff__header-left", children: [
1540
+ fileName && /* @__PURE__ */ jsx6("span", { className: "git-diff__filename", children: fileName }),
1541
+ language && /* @__PURE__ */ jsx6("span", { className: "git-diff__badge", children: language })
1542
+ ] }),
1543
+ /* @__PURE__ */ jsxs4("div", { className: "git-diff__stats", children: [
1544
+ added > 0 && /* @__PURE__ */ jsxs4("span", { className: "git-diff__stat--added", children: [
1545
+ "+",
1546
+ added
1547
+ ] }),
1548
+ removed > 0 && /* @__PURE__ */ jsxs4("span", { className: "git-diff__stat--removed", children: [
1549
+ "-",
1550
+ removed
1551
+ ] }),
1552
+ added === 0 && removed === 0 && /* @__PURE__ */ jsx6("span", { className: "git-diff__stat--none", children: "No changes" })
1553
+ ] })
1554
+ ] }),
1555
+ mode === "side-by-side" ? /* @__PURE__ */ jsx6(SideBySideView, { lines: diffLines, lineNumbers }) : /* @__PURE__ */ jsx6(InlineView, { lines: diffLines, lineNumbers })
1556
+ ] });
1557
+ };
1558
+ function InlineView({ lines, lineNumbers }) {
1559
+ return /* @__PURE__ */ jsx6("div", { className: "git-diff__body", children: lines.map((line, i) => /* @__PURE__ */ jsxs4("div", { className: `git-diff__line git-diff__line--${line.type}`, children: [
1560
+ lineNumbers && /* @__PURE__ */ jsx6("span", { className: "git-diff__gutter git-diff__gutter--old", children: line.oldLineNumber ?? "" }),
1561
+ lineNumbers && /* @__PURE__ */ jsx6("span", { className: "git-diff__gutter git-diff__gutter--new", children: line.newLineNumber ?? "" }),
1562
+ /* @__PURE__ */ jsx6("span", { className: "git-diff__prefix", children: line.type === "added" ? "+" : line.type === "removed" ? "-" : " " }),
1563
+ /* @__PURE__ */ jsx6("span", { className: "git-diff__content", children: line.content || "\n" })
1564
+ ] }, i)) });
1565
+ }
1566
+ function SideBySideView({ lines, lineNumbers }) {
1567
+ const left = [];
1568
+ const right = [];
1569
+ let idx = 0;
1570
+ while (idx < lines.length) {
1571
+ const line = lines[idx];
1572
+ if (line.type === "unchanged") {
1573
+ left.push(line);
1574
+ right.push(line);
1575
+ idx++;
1576
+ } else {
1577
+ const removedBatch = [];
1578
+ const addedBatch = [];
1579
+ while (idx < lines.length && lines[idx].type === "removed") {
1580
+ removedBatch.push(lines[idx]);
1581
+ idx++;
1582
+ }
1583
+ while (idx < lines.length && lines[idx].type === "added") {
1584
+ addedBatch.push(lines[idx]);
1585
+ idx++;
1586
+ }
1587
+ const max = Math.max(removedBatch.length, addedBatch.length);
1588
+ for (let j = 0; j < max; j++) {
1589
+ left.push(j < removedBatch.length ? removedBatch[j] : null);
1590
+ right.push(j < addedBatch.length ? addedBatch[j] : null);
1591
+ }
1592
+ }
1593
+ }
1594
+ return /* @__PURE__ */ jsxs4("div", { className: "git-diff__sbs-body", children: [
1595
+ /* @__PURE__ */ jsx6("div", { className: "git-diff__side", children: left.map((line, i) => /* @__PURE__ */ jsxs4(
1596
+ "div",
1597
+ {
1598
+ className: `git-diff__line ${line ? `git-diff__line--${line.type}` : "git-diff__line--empty"}`,
1599
+ children: [
1600
+ lineNumbers && /* @__PURE__ */ jsx6("span", { className: "git-diff__gutter", children: line?.oldLineNumber ?? "" }),
1601
+ /* @__PURE__ */ jsx6("span", { className: "git-diff__content", children: line?.content || "\xA0" })
1602
+ ]
1603
+ },
1604
+ i
1605
+ )) }),
1606
+ /* @__PURE__ */ jsx6("div", { className: "git-diff__side", children: right.map((line, i) => /* @__PURE__ */ jsxs4(
1607
+ "div",
1608
+ {
1609
+ className: `git-diff__line ${line ? `git-diff__line--${line.type}` : "git-diff__line--empty"}`,
1610
+ children: [
1611
+ lineNumbers && /* @__PURE__ */ jsx6("span", { className: "git-diff__gutter", children: line?.newLineNumber ?? "" }),
1612
+ /* @__PURE__ */ jsx6("span", { className: "git-diff__content", children: line?.content || "\xA0" })
1613
+ ]
1614
+ },
1615
+ i
1616
+ )) })
1617
+ ] });
1618
+ }
1619
+
1620
+ // src/MarkdownEditor/MarkdownEditor.tsx
1621
+ import { useRef as useRef6, useCallback as useCallback6, useMemo as useMemo2 } from "react";
1622
+
1623
+ // src/MarkdownRenderer/MarkdownRenderer.tsx
1624
+ import { useMemo, useCallback as useCallback5 } from "react";
1625
+
1626
+ // src/DataTable/index.ts
1627
+ import { DataTable } from "@orchestra-mcp/widgets";
1628
+
1629
+ // src/MarkdownRenderer/MarkdownRenderer.tsx
1630
+ import { Checkbox } from "@orchestra-mcp/ui";
1631
+
1632
+ // src/MarkdownRenderer/parseMarkdown.ts
1633
+ var UL_RE = /^\s*[-*+]\s/;
1634
+ var OL_RE = /^\s*\d+[.)]\s/;
1635
+ var LIST_RE = (line) => UL_RE.test(line) || OL_RE.test(line);
1636
+ function slugify(text) {
1637
+ return text.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
1638
+ }
1639
+ function parseAlignments(separatorLine) {
1640
+ return separatorLine.split("|").map((c) => c.trim()).filter(Boolean).map((cell) => {
1641
+ const left = cell.startsWith(":");
1642
+ const right = cell.endsWith(":");
1643
+ if (left && right) return "center";
1644
+ if (right) return "right";
1645
+ return "left";
1646
+ });
1647
+ }
1648
+ function parseTable(lines) {
1649
+ if (lines.length < 2) return null;
1650
+ const parse = (line) => line.split("|").map((c) => c.trim()).filter(Boolean);
1651
+ const headers = parse(lines[0]);
1652
+ if (!lines[1].match(/^\|?[\s-:|]+\|?$/)) return null;
1653
+ const alignments = parseAlignments(lines[1]);
1654
+ const rows = lines.slice(2).map(parse);
1655
+ return { type: "table", headers, rows, alignments };
1656
+ }
1657
+ function parseListBlock(lines) {
1658
+ const stripped = lines.map((l) => l.replace(/^\s+/, ""));
1659
+ const taskMatch = stripped[0].match(/^[-*+]\s+\[([ xX])\]/);
1660
+ if (taskMatch) {
1661
+ const items = stripped.map((l) => {
1662
+ const m = l.match(/^[-*+]\s+\[([ xX])\]\s*(.*)/);
1663
+ return { checked: m ? m[1] !== " " : false, text: m ? m[2] : l };
1664
+ });
1665
+ return { type: "task-list", items };
1666
+ }
1667
+ if (OL_RE.test(stripped[0])) {
1668
+ return { type: "ordered-list", items: stripped.map((l) => l.replace(/^\d+[.)]\s*/, "")) };
1669
+ }
1670
+ return { type: "unordered-list", items: stripped.map((l) => l.replace(/^[-*+]\s*/, "")) };
1671
+ }
1672
+ function parseYaml(lines) {
1673
+ const data = {};
1674
+ for (const line of lines) {
1675
+ const match = line.match(/^(\w[\w\s-]*):\s*(.*)/);
1676
+ if (match) {
1677
+ const key = match[1].trim();
1678
+ let value = match[2].trim();
1679
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
1680
+ value = value.slice(1, -1);
1681
+ }
1682
+ data[key] = value;
1683
+ }
1684
+ }
1685
+ return data;
1686
+ }
1687
+ function parseMarkdown(content) {
1688
+ const blocks = [];
1689
+ const rawLines = content.split("\n");
1690
+ let i = 0;
1691
+ if (rawLines[0]?.trim() === "---") {
1692
+ i = 1;
1693
+ const fmLines = [];
1694
+ while (i < rawLines.length && rawLines[i].trim() !== "---") {
1695
+ fmLines.push(rawLines[i]);
1696
+ i++;
1697
+ }
1698
+ if (i < rawLines.length) {
1699
+ i++;
1700
+ const data = parseYaml(fmLines);
1701
+ if (Object.keys(data).length > 0) {
1702
+ blocks.push({ type: "frontmatter", data });
1703
+ }
1704
+ } else {
1705
+ i = 0;
1706
+ }
1707
+ }
1708
+ while (i < rawLines.length) {
1709
+ const line = rawLines[i];
1710
+ if (line.trim() === "") {
1711
+ i++;
1712
+ continue;
1713
+ }
1714
+ if (/^[-*_]{3,}$/.test(line.trim())) {
1715
+ blocks.push({ type: "hr" });
1716
+ i++;
1717
+ continue;
1718
+ }
1719
+ const headingMatch = line.match(/^(#{1,6})\s+(.*)/);
1720
+ if (headingMatch) {
1721
+ const text = headingMatch[2];
1722
+ blocks.push({ type: "heading", level: headingMatch[1].length, text, id: slugify(text) });
1723
+ i++;
1724
+ continue;
1725
+ }
1726
+ const codeMatch = line.match(/^```(\w*)/);
1727
+ if (codeMatch) {
1728
+ const language = codeMatch[1] || "";
1729
+ const codeLines = [];
1730
+ i++;
1731
+ while (i < rawLines.length && !rawLines[i].startsWith("```")) {
1732
+ codeLines.push(rawLines[i]);
1733
+ i++;
1734
+ }
1735
+ blocks.push({ type: "code", language, code: codeLines.join("\n") });
1736
+ i++;
1737
+ continue;
1738
+ }
1739
+ if (line.includes("|") && i + 1 < rawLines.length && rawLines[i + 1].match(/^\|?[\s-:|]+\|?$/)) {
1740
+ const tableLines = [];
1741
+ while (i < rawLines.length && rawLines[i].includes("|")) {
1742
+ tableLines.push(rawLines[i]);
1743
+ i++;
1744
+ }
1745
+ const table = parseTable(tableLines);
1746
+ if (table) {
1747
+ blocks.push(table);
1748
+ continue;
1749
+ }
1750
+ }
1751
+ if (line.startsWith(">")) {
1752
+ const quoteLines = [];
1753
+ while (i < rawLines.length && rawLines[i].startsWith(">")) {
1754
+ quoteLines.push(rawLines[i].replace(/^>\s?/, ""));
1755
+ i++;
1756
+ }
1757
+ blocks.push({ type: "blockquote", text: quoteLines.join("\n") });
1758
+ continue;
1759
+ }
1760
+ if (LIST_RE(line)) {
1761
+ const listLines = [];
1762
+ while (i < rawLines.length) {
1763
+ const cur = rawLines[i];
1764
+ if (cur.trim() === "") break;
1765
+ if (LIST_RE(cur)) {
1766
+ listLines.push(cur);
1767
+ i++;
1768
+ } else if (listLines.length > 0 && (cur.startsWith(" ") || cur.startsWith(" "))) {
1769
+ listLines[listLines.length - 1] += " " + cur.trim();
1770
+ i++;
1771
+ } else {
1772
+ break;
1773
+ }
1774
+ }
1775
+ blocks.push(parseListBlock(listLines));
1776
+ continue;
1777
+ }
1778
+ const paraLines = [];
1779
+ while (i < rawLines.length && rawLines[i].trim() !== "" && !rawLines[i].match(/^(#{1,6}\s|```|>|---+$)/) && !LIST_RE(rawLines[i]) && !(rawLines[i].includes("|") && i + 1 < rawLines.length && rawLines[i + 1]?.match(/^\|?[\s-:|]+\|?$/))) {
1780
+ paraLines.push(rawLines[i]);
1781
+ i++;
1782
+ }
1783
+ if (paraLines.length > 0) {
1784
+ blocks.push({ type: "paragraph", text: paraLines.join(" ") });
1785
+ }
1786
+ }
1787
+ return blocks;
1788
+ }
1789
+
1790
+ // src/MarkdownRenderer/inlineFormat.ts
1791
+ function formatInline(text) {
1792
+ let result = text;
1793
+ result = result.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1794
+ const codeSlots = [];
1795
+ result = result.replace(/`([^`]+)`/g, (_, code) => {
1796
+ const idx = codeSlots.length;
1797
+ codeSlots.push(`<code class="markdown-renderer__inline-code">${code}</code>`);
1798
+ return `\0CODE${idx}\0`;
1799
+ });
1800
+ result = result.replace(
1801
+ /!\[([^\]]*)\]\(([^)]+)\)/g,
1802
+ '<img src="$2" alt="$1" loading="lazy" class="markdown-renderer__img" />'
1803
+ );
1804
+ result = result.replace(
1805
+ /\[([^\]]+)\]\(([^)]+)\)/g,
1806
+ '<a role="link" class="markdown-renderer__link" data-md-link="$2" style="color:var(--color-link,var(--color-primary,var(--primary,#6366f1)));opacity:0.75;cursor:pointer;text-decoration:none">$1</a>'
1807
+ );
1808
+ result = result.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
1809
+ result = result.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
1810
+ result = result.replace(/__(.+?)__/g, "<strong>$1</strong>");
1811
+ result = result.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<em>$1</em>");
1812
+ result = result.replace(/(?<!_)_(?!_)(.+?)(?<!_)_(?!_)/g, "<em>$1</em>");
1813
+ result = result.replace(/~~(.+?)~~/g, "<del>$1</del>");
1814
+ result = result.replace(/\x00CODE(\d+)\x00/g, (_, idx) => codeSlots[parseInt(idx)]);
1815
+ return result;
1816
+ }
1817
+
1818
+ // src/MarkdownRenderer/MarkdownRenderer.tsx
1819
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1820
+ var MarkdownRenderer = ({
1821
+ content,
1822
+ className,
1823
+ onLinkClick,
1824
+ onConvertToMermaid
1825
+ }) => {
1826
+ const blocks = useMemo(() => parseMarkdown(content), [content]);
1827
+ const handleClick = useCallback5(
1828
+ (e) => {
1829
+ const target = e.target;
1830
+ const link = target.closest("[data-md-link]");
1831
+ if (link && onLinkClick) {
1832
+ e.preventDefault();
1833
+ onLinkClick(link.dataset.mdLink);
1834
+ }
1835
+ },
1836
+ [onLinkClick]
1837
+ );
1838
+ const wrapperClass = ["markdown-renderer", className].filter(Boolean).join(" ");
1839
+ return /* @__PURE__ */ jsx7("div", { className: wrapperClass, onClick: handleClick, children: blocks.map((block, i) => /* @__PURE__ */ jsx7(BlockRenderer, { block, onLinkClick, onConvertToMermaid }, i)) });
1840
+ };
1841
+ function renderHeading(level, id, text) {
1842
+ const anchor = /* @__PURE__ */ jsx7("a", { href: `#${id}`, className: "markdown-renderer__heading-anchor", "aria-hidden": "true", children: "#" });
1843
+ const inner = /* @__PURE__ */ jsx7("span", { dangerouslySetInnerHTML: { __html: formatInline(text) } });
1844
+ if (level === 1) return /* @__PURE__ */ jsxs5("h1", { id, children: [
1845
+ anchor,
1846
+ inner
1847
+ ] });
1848
+ if (level === 2) return /* @__PURE__ */ jsxs5("h2", { id, children: [
1849
+ anchor,
1850
+ inner
1851
+ ] });
1852
+ if (level === 3) return /* @__PURE__ */ jsxs5("h3", { id, children: [
1853
+ anchor,
1854
+ inner
1855
+ ] });
1856
+ if (level === 4) return /* @__PURE__ */ jsxs5("h4", { id, children: [
1857
+ anchor,
1858
+ inner
1859
+ ] });
1860
+ if (level === 5) return /* @__PURE__ */ jsxs5("h5", { id, children: [
1861
+ anchor,
1862
+ inner
1863
+ ] });
1864
+ return /* @__PURE__ */ jsxs5("h6", { id, children: [
1865
+ anchor,
1866
+ inner
1867
+ ] });
1868
+ }
1869
+ function BlockRenderer({ block, onLinkClick, onConvertToMermaid }) {
1870
+ switch (block.type) {
1871
+ case "heading":
1872
+ return renderHeading(block.level, block.id, block.text);
1873
+ case "paragraph":
1874
+ return /* @__PURE__ */ jsx7("p", { dangerouslySetInnerHTML: { __html: formatInline(block.text) } });
1875
+ case "code":
1876
+ return /* @__PURE__ */ jsx7(CodeBlock, { code: block.code, language: block.language || void 0, copyable: true, exportable: true, exportImage: true, onConvertToMermaid });
1877
+ case "frontmatter":
1878
+ return /* @__PURE__ */ jsxs5("div", { className: "markdown-renderer__frontmatter", children: [
1879
+ /* @__PURE__ */ jsx7("div", { className: "markdown-renderer__frontmatter-header", children: "Metadata" }),
1880
+ /* @__PURE__ */ jsx7("table", { className: "markdown-renderer__frontmatter-table", children: /* @__PURE__ */ jsx7("tbody", { children: Object.entries(block.data).map(([key, value]) => /* @__PURE__ */ jsxs5("tr", { children: [
1881
+ /* @__PURE__ */ jsx7("td", { className: "markdown-renderer__frontmatter-key", children: key }),
1882
+ /* @__PURE__ */ jsx7("td", { className: "markdown-renderer__frontmatter-value", children: String(value) })
1883
+ ] }, key)) }) })
1884
+ ] });
1885
+ case "table": {
1886
+ const columns = block.headers.map((h, i) => ({
1887
+ key: `col-${i}`,
1888
+ header: h,
1889
+ align: block.alignments[i] ?? "left"
1890
+ }));
1891
+ return /* @__PURE__ */ jsx7(DataTable, { columns, rows: block.rows, showHeader: true, exportable: true, exportImage: true, renderCell: formatInline, onLinkClick });
1892
+ }
1893
+ case "blockquote":
1894
+ return /* @__PURE__ */ jsx7("blockquote", { dangerouslySetInnerHTML: { __html: formatInline(block.text) } });
1895
+ case "unordered-list":
1896
+ return /* @__PURE__ */ jsx7("ul", { children: block.items.map((item, i) => /* @__PURE__ */ jsx7("li", { dangerouslySetInnerHTML: { __html: formatInline(item) } }, i)) });
1897
+ case "ordered-list":
1898
+ return /* @__PURE__ */ jsx7("ol", { children: block.items.map((item, i) => /* @__PURE__ */ jsx7("li", { dangerouslySetInnerHTML: { __html: formatInline(item) } }, i)) });
1899
+ case "task-list":
1900
+ return /* @__PURE__ */ jsx7("ul", { className: "task-list", children: block.items.map((item, i) => /* @__PURE__ */ jsxs5("li", { className: "task-list__item", children: [
1901
+ /* @__PURE__ */ jsx7(
1902
+ Checkbox,
1903
+ {
1904
+ checked: item.checked,
1905
+ color: item.checked ? "success" : void 0,
1906
+ className: "task-list__checkbox"
1907
+ }
1908
+ ),
1909
+ /* @__PURE__ */ jsx7("span", { dangerouslySetInnerHTML: { __html: formatInline(item.text) } })
1910
+ ] }, i)) });
1911
+ case "hr":
1912
+ return /* @__PURE__ */ jsx7("hr", {});
1913
+ }
1914
+ }
1915
+
1916
+ // src/MarkdownEditor/MarkdownEditor.tsx
1917
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1918
+ var MarkdownEditor = ({
1919
+ value,
1920
+ onChange,
1921
+ placeholder,
1922
+ readOnly = false,
1923
+ hidePreview = false,
1924
+ className
1925
+ }) => {
1926
+ const textareaRef = useRef6(null);
1927
+ const wrapSelection = useCallback6(
1928
+ (before, after) => {
1929
+ const ta = textareaRef.current;
1930
+ if (!ta) return;
1931
+ const start = ta.selectionStart;
1932
+ const end = ta.selectionEnd;
1933
+ const selected = value.slice(start, end);
1934
+ const newValue = value.slice(0, start) + before + selected + after + value.slice(end);
1935
+ onChange(newValue);
1936
+ requestAnimationFrame(() => {
1937
+ ta.focus();
1938
+ ta.selectionStart = start + before.length;
1939
+ ta.selectionEnd = end + before.length;
1940
+ });
1941
+ },
1942
+ [value, onChange]
1943
+ );
1944
+ const insertLinePrefix = useCallback6(
1945
+ (prefix) => {
1946
+ const ta = textareaRef.current;
1947
+ if (!ta) return;
1948
+ const pos = ta.selectionStart;
1949
+ const lineStart = value.lastIndexOf("\n", pos - 1) + 1;
1950
+ const newValue = value.slice(0, lineStart) + prefix + value.slice(lineStart);
1951
+ onChange(newValue);
1952
+ requestAnimationFrame(() => {
1953
+ ta.focus();
1954
+ ta.selectionStart = pos + prefix.length;
1955
+ ta.selectionEnd = pos + prefix.length;
1956
+ });
1957
+ },
1958
+ [value, onChange]
1959
+ );
1960
+ const handleBold = useCallback6(() => wrapSelection("**", "**"), [wrapSelection]);
1961
+ const handleItalic = useCallback6(() => wrapSelection("*", "*"), [wrapSelection]);
1962
+ const handleLink = useCallback6(() => wrapSelection("[", "](url)"), [wrapSelection]);
1963
+ const handleHeading = useCallback6(() => insertLinePrefix("## "), [insertLinePrefix]);
1964
+ const handleUl = useCallback6(() => insertLinePrefix("- "), [insertLinePrefix]);
1965
+ const handleOl = useCallback6(() => insertLinePrefix("1. "), [insertLinePrefix]);
1966
+ const handleCode = useCallback6(() => {
1967
+ const ta = textareaRef.current;
1968
+ if (!ta) return;
1969
+ const selected = value.slice(ta.selectionStart, ta.selectionEnd);
1970
+ if (selected.includes("\n")) {
1971
+ wrapSelection("```\n", "\n```");
1972
+ } else {
1973
+ wrapSelection("`", "`");
1974
+ }
1975
+ }, [value, wrapSelection]);
1976
+ const toolbarActions = useMemo2(
1977
+ () => [
1978
+ { label: "B", title: "Bold (Ctrl+B)", action: handleBold },
1979
+ { label: "I", title: "Italic (Ctrl+I)", action: handleItalic },
1980
+ { label: "H", title: "Heading", action: handleHeading },
1981
+ { label: "UL", title: "Unordered list", action: handleUl },
1982
+ { label: "OL", title: "Ordered list", action: handleOl },
1983
+ { label: "<>", title: "Code", action: handleCode },
1984
+ { label: "Link", title: "Link (Ctrl+K)", action: handleLink }
1985
+ ],
1986
+ [handleBold, handleItalic, handleHeading, handleUl, handleOl, handleCode, handleLink]
1987
+ );
1988
+ const handleKeyDown = useCallback6(
1989
+ (e) => {
1990
+ const mod = e.metaKey || e.ctrlKey;
1991
+ if (!mod) return;
1992
+ if (e.key === "b") {
1993
+ e.preventDefault();
1994
+ handleBold();
1995
+ } else if (e.key === "i") {
1996
+ e.preventDefault();
1997
+ handleItalic();
1998
+ } else if (e.key === "k") {
1999
+ e.preventDefault();
2000
+ handleLink();
2001
+ }
2002
+ },
2003
+ [handleBold, handleItalic, handleLink]
2004
+ );
2005
+ const wordCount = useMemo2(() => {
2006
+ return value.trim().split(/\s+/).filter(Boolean).length;
2007
+ }, [value]);
2008
+ const readingTime = useMemo2(() => {
2009
+ return Math.max(1, Math.ceil(wordCount / 200));
2010
+ }, [wordCount]);
2011
+ const wrapperClass = ["md-editor", className].filter(Boolean).join(" ");
2012
+ return /* @__PURE__ */ jsxs6("div", { className: wrapperClass, children: [
2013
+ /* @__PURE__ */ jsx8("div", { className: "md-editor__toolbar", role: "toolbar", "aria-label": "Formatting", children: toolbarActions.map((btn) => /* @__PURE__ */ jsx8(
2014
+ "button",
2015
+ {
2016
+ type: "button",
2017
+ className: "md-editor__toolbar-btn",
2018
+ title: btn.title,
2019
+ disabled: readOnly,
2020
+ onClick: btn.action,
2021
+ children: btn.label
2022
+ },
2023
+ btn.label
2024
+ )) }),
2025
+ /* @__PURE__ */ jsxs6("div", { className: "md-editor__body", children: [
2026
+ /* @__PURE__ */ jsx8("div", { className: "md-editor__source", children: /* @__PURE__ */ jsx8(
2027
+ "textarea",
2028
+ {
2029
+ ref: textareaRef,
2030
+ value,
2031
+ onChange: (e) => onChange(e.target.value),
2032
+ onKeyDown: handleKeyDown,
2033
+ placeholder,
2034
+ readOnly,
2035
+ "aria-label": "Markdown source"
2036
+ }
2037
+ ) }),
2038
+ !hidePreview && /* @__PURE__ */ jsxs6(Fragment, { children: [
2039
+ /* @__PURE__ */ jsx8("div", { className: "md-editor__divider" }),
2040
+ /* @__PURE__ */ jsx8("div", { className: "md-editor__preview", children: /* @__PURE__ */ jsx8(MarkdownRenderer, { content: value }) })
2041
+ ] })
2042
+ ] }),
2043
+ /* @__PURE__ */ jsxs6("div", { className: "md-editor__footer", children: [
2044
+ /* @__PURE__ */ jsxs6("span", { "data-testid": "word-count", children: [
2045
+ wordCount,
2046
+ " words"
2047
+ ] }),
2048
+ /* @__PURE__ */ jsxs6("span", { "data-testid": "reading-time", children: [
2049
+ readingTime,
2050
+ " min read"
2051
+ ] })
2052
+ ] })
2053
+ ] });
2054
+ };
2055
+ export {
2056
+ CodeBlock,
2057
+ CodeDiffEditor,
2058
+ CodeEditor,
2059
+ GitDiffView,
2060
+ LegacyCodeEditor,
2061
+ MarkdownEditor,
2062
+ MarkdownRenderer,
2063
+ computeDiff,
2064
+ exportToImage,
2065
+ getMonacoThemeId,
2066
+ languageFromFilename,
2067
+ orchestraToMonacoTheme,
2068
+ resolveLanguage
2069
+ };