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