open-mcp-app-ui 0.0.4 → 0.0.6
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/charts/index.d.ts +124 -0
- package/dist/charts/index.js +401 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/editor/index.d.ts +13 -0
- package/dist/editor/index.js +152 -43
- package/dist/editor/index.js.map +1 -1
- package/dist/index.d.ts +21 -15
- package/dist/index.js +18 -7
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +10 -4
- package/dist/styles/display-modes.css +0 -14
package/dist/editor/index.js
CHANGED
|
@@ -10,11 +10,30 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
Editor as MilkdownEditor,
|
|
12
12
|
rootCtx,
|
|
13
|
-
defaultValueCtx
|
|
13
|
+
defaultValueCtx,
|
|
14
|
+
commandsCtx
|
|
14
15
|
} from "@milkdown/kit/core";
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
commonmark,
|
|
18
|
+
toggleStrongCommand,
|
|
19
|
+
toggleEmphasisCommand,
|
|
20
|
+
wrapInHeadingCommand,
|
|
21
|
+
wrapInBulletListCommand,
|
|
22
|
+
wrapInOrderedListCommand,
|
|
23
|
+
toggleInlineCodeCommand,
|
|
24
|
+
createCodeBlockCommand,
|
|
25
|
+
wrapInBlockquoteCommand,
|
|
26
|
+
toggleLinkCommand
|
|
27
|
+
} from "@milkdown/kit/preset/commonmark";
|
|
28
|
+
import {
|
|
29
|
+
gfm,
|
|
30
|
+
toggleStrikethroughCommand
|
|
31
|
+
} from "@milkdown/kit/preset/gfm";
|
|
32
|
+
import {
|
|
33
|
+
history,
|
|
34
|
+
undoCommand,
|
|
35
|
+
redoCommand
|
|
36
|
+
} from "@milkdown/kit/plugin/history";
|
|
18
37
|
import { clipboard } from "@milkdown/kit/plugin/clipboard";
|
|
19
38
|
import { listener, listenerCtx } from "@milkdown/plugin-listener";
|
|
20
39
|
import { replaceAll } from "@milkdown/utils";
|
|
@@ -38,6 +57,21 @@ var DEFAULT_TOOLBAR = [
|
|
|
38
57
|
"undo",
|
|
39
58
|
"redo"
|
|
40
59
|
];
|
|
60
|
+
var TOOLBAR_MARKDOWN = {
|
|
61
|
+
bold: "**bold**",
|
|
62
|
+
italic: "*italic*",
|
|
63
|
+
strikethrough: "~~strikethrough~~",
|
|
64
|
+
heading: "## ",
|
|
65
|
+
bulletList: "- ",
|
|
66
|
+
orderedList: "1. ",
|
|
67
|
+
taskList: "- [ ] ",
|
|
68
|
+
code: "`code`",
|
|
69
|
+
codeBlock: "```\n\n```",
|
|
70
|
+
blockquote: "> ",
|
|
71
|
+
link: "[text](url)",
|
|
72
|
+
undo: "",
|
|
73
|
+
redo: ""
|
|
74
|
+
};
|
|
41
75
|
var ToolbarIcons = {
|
|
42
76
|
bold: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
43
77
|
/* @__PURE__ */ jsx("path", { d: "M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" }),
|
|
@@ -119,25 +153,10 @@ var TOOLBAR_LABELS = {
|
|
|
119
153
|
undo: "Undo",
|
|
120
154
|
redo: "Redo"
|
|
121
155
|
};
|
|
122
|
-
var TOOLBAR_MARKDOWN = {
|
|
123
|
-
bold: "**bold**",
|
|
124
|
-
italic: "*italic*",
|
|
125
|
-
strikethrough: "~~strikethrough~~",
|
|
126
|
-
heading: "## ",
|
|
127
|
-
bulletList: "- ",
|
|
128
|
-
orderedList: "1. ",
|
|
129
|
-
taskList: "- [ ] ",
|
|
130
|
-
code: "`code`",
|
|
131
|
-
codeBlock: "```\n\n```",
|
|
132
|
-
blockquote: "> ",
|
|
133
|
-
link: "[text](url)",
|
|
134
|
-
undo: "",
|
|
135
|
-
redo: ""
|
|
136
|
-
};
|
|
137
156
|
var EditorToolbar = ({
|
|
138
157
|
items,
|
|
139
158
|
onAction
|
|
140
|
-
}) => /* @__PURE__ */ jsx("div", { className: "omu-editor-toolbar flex items-center gap-0.5 px-2 py-1.5
|
|
159
|
+
}) => /* @__PURE__ */ jsx("div", { className: "omu-editor-toolbar flex items-center gap-0.5 px-2 py-1.5 overflow-x-auto", children: items.map((item, i) => {
|
|
141
160
|
if (item === "divider") {
|
|
142
161
|
return /* @__PURE__ */ jsx(
|
|
143
162
|
"div",
|
|
@@ -154,6 +173,9 @@ var EditorToolbar = ({
|
|
|
154
173
|
{
|
|
155
174
|
type: "button",
|
|
156
175
|
className: "inline-flex items-center justify-center w-7 h-7 rounded-sm text-txt-secondary hover:text-txt-primary hover:bg-bg-tertiary transition-colors shrink-0 cursor-pointer",
|
|
176
|
+
onMouseDown: (e) => {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
},
|
|
157
179
|
onClick: () => onAction(item),
|
|
158
180
|
title: label,
|
|
159
181
|
"aria-label": label,
|
|
@@ -171,7 +193,7 @@ var ModeToggle = ({
|
|
|
171
193
|
{ value: "markdown", label: "MD" },
|
|
172
194
|
{ value: "split", label: "Split" }
|
|
173
195
|
];
|
|
174
|
-
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5 ml-auto pl-2", children: modes.map((m) => /* @__PURE__ */ jsx(
|
|
196
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5 ml-auto pl-2 pr-2", children: modes.map((m) => /* @__PURE__ */ jsx(
|
|
175
197
|
"button",
|
|
176
198
|
{
|
|
177
199
|
type: "button",
|
|
@@ -216,9 +238,34 @@ var MilkdownInner = ({
|
|
|
216
238
|
setMarkdown: (md) => {
|
|
217
239
|
const editor = get();
|
|
218
240
|
if (!editor) return;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
241
|
+
try {
|
|
242
|
+
skipRef.current = true;
|
|
243
|
+
editor.action(replaceAll(md));
|
|
244
|
+
contentRef.current = md;
|
|
245
|
+
} catch (e) {
|
|
246
|
+
skipRef.current = false;
|
|
247
|
+
console.warn("[Editor] setMarkdown failed:", e);
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
/**
|
|
251
|
+
* Execute a Milkdown command by its key.
|
|
252
|
+
* This is how toolbar buttons apply formatting — they call
|
|
253
|
+
* real ProseMirror commands through Milkdown's command manager,
|
|
254
|
+
* which toggles marks/wraps on the current selection.
|
|
255
|
+
*/
|
|
256
|
+
runCommand: (key, payload) => {
|
|
257
|
+
const editor = get();
|
|
258
|
+
if (!editor) return false;
|
|
259
|
+
try {
|
|
260
|
+
editor.action((ctx) => {
|
|
261
|
+
const commands = ctx.get(commandsCtx);
|
|
262
|
+
commands.call(key, payload);
|
|
263
|
+
});
|
|
264
|
+
return true;
|
|
265
|
+
} catch (e) {
|
|
266
|
+
console.warn("[Editor] Command failed:", e);
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
222
269
|
},
|
|
223
270
|
focus: () => {
|
|
224
271
|
const el = wrapperRef.current?.querySelector(".ProseMirror");
|
|
@@ -226,6 +273,18 @@ var MilkdownInner = ({
|
|
|
226
273
|
}
|
|
227
274
|
};
|
|
228
275
|
}, [get, skipRef, actionsRef]);
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
if (!placeholder) return;
|
|
278
|
+
const trySet = () => {
|
|
279
|
+
const el = wrapperRef.current?.querySelector(".ProseMirror");
|
|
280
|
+
if (el) {
|
|
281
|
+
el.setAttribute("data-placeholder", placeholder);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
trySet();
|
|
285
|
+
const t = setTimeout(trySet, 100);
|
|
286
|
+
return () => clearTimeout(t);
|
|
287
|
+
}, [placeholder]);
|
|
229
288
|
useEffect(() => {
|
|
230
289
|
if (autoFocus) {
|
|
231
290
|
requestAnimationFrame(() => {
|
|
@@ -287,6 +346,7 @@ var Editor = forwardRef(
|
|
|
287
346
|
placeholder,
|
|
288
347
|
toolbar: toolbarProp,
|
|
289
348
|
readOnly = false,
|
|
349
|
+
bordered = false,
|
|
290
350
|
minHeight = 120,
|
|
291
351
|
maxHeight,
|
|
292
352
|
autoFocus = false,
|
|
@@ -296,6 +356,7 @@ var Editor = forwardRef(
|
|
|
296
356
|
const mode = modeProp ?? internalMode;
|
|
297
357
|
const showModeToggle = modeProp == null;
|
|
298
358
|
const [rawValue, setRawValue] = useState(value);
|
|
359
|
+
const [milkdownKey, setMilkdownKey] = useState(0);
|
|
299
360
|
const skipRef = useRef(false);
|
|
300
361
|
const actionsRef = useRef(null);
|
|
301
362
|
useEffect(() => {
|
|
@@ -335,21 +396,70 @@ var Editor = forwardRef(
|
|
|
335
396
|
(item) => {
|
|
336
397
|
if (item === "divider") return;
|
|
337
398
|
if (mode === "markdown") {
|
|
338
|
-
const
|
|
339
|
-
if (
|
|
340
|
-
const newValue = rawValue +
|
|
399
|
+
const syntax = TOOLBAR_MARKDOWN[item];
|
|
400
|
+
if (syntax) {
|
|
401
|
+
const newValue = rawValue + syntax;
|
|
341
402
|
setRawValue(newValue);
|
|
342
403
|
onChange?.(newValue);
|
|
343
404
|
}
|
|
344
405
|
return;
|
|
345
406
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
407
|
+
if (!actionsRef.current) return;
|
|
408
|
+
switch (item) {
|
|
409
|
+
case "bold":
|
|
410
|
+
actionsRef.current.runCommand(toggleStrongCommand.key);
|
|
411
|
+
break;
|
|
412
|
+
case "italic":
|
|
413
|
+
actionsRef.current.runCommand(toggleEmphasisCommand.key);
|
|
414
|
+
break;
|
|
415
|
+
case "strikethrough":
|
|
416
|
+
actionsRef.current.runCommand(toggleStrikethroughCommand.key);
|
|
417
|
+
break;
|
|
418
|
+
case "heading": {
|
|
419
|
+
const md = actionsRef.current.getMarkdown();
|
|
420
|
+
const lines = md.split("\n");
|
|
421
|
+
const lastHeadingMatch = lines[lines.length - 1]?.match(/^(#{1,6})\s/);
|
|
422
|
+
const currentLevel = lastHeadingMatch ? lastHeadingMatch[1].length : 0;
|
|
423
|
+
if (currentLevel >= 4 || currentLevel === 0) {
|
|
424
|
+
actionsRef.current.runCommand(wrapInHeadingCommand.key, 2);
|
|
425
|
+
} else {
|
|
426
|
+
actionsRef.current.runCommand(wrapInHeadingCommand.key, currentLevel + 1);
|
|
427
|
+
}
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
case "bulletList":
|
|
431
|
+
actionsRef.current.runCommand(wrapInBulletListCommand.key);
|
|
432
|
+
break;
|
|
433
|
+
case "orderedList":
|
|
434
|
+
actionsRef.current.runCommand(wrapInOrderedListCommand.key);
|
|
435
|
+
break;
|
|
436
|
+
case "taskList": {
|
|
437
|
+
const current = actionsRef.current.getMarkdown();
|
|
438
|
+
const suffix = current.endsWith("\n") ? "- [ ] " : "\n- [ ] ";
|
|
439
|
+
actionsRef.current.setMarkdown(current + suffix);
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
case "code":
|
|
443
|
+
actionsRef.current.runCommand(toggleInlineCodeCommand.key);
|
|
444
|
+
break;
|
|
445
|
+
case "codeBlock":
|
|
446
|
+
actionsRef.current.runCommand(createCodeBlockCommand.key);
|
|
447
|
+
break;
|
|
448
|
+
case "blockquote":
|
|
449
|
+
actionsRef.current.runCommand(wrapInBlockquoteCommand.key);
|
|
450
|
+
break;
|
|
451
|
+
case "link": {
|
|
452
|
+
actionsRef.current.runCommand(toggleLinkCommand.key, { href: "https://" });
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
case "undo":
|
|
456
|
+
actionsRef.current.runCommand(undoCommand.key);
|
|
457
|
+
break;
|
|
458
|
+
case "redo":
|
|
459
|
+
actionsRef.current.runCommand(redoCommand.key);
|
|
460
|
+
break;
|
|
461
|
+
default:
|
|
462
|
+
break;
|
|
353
463
|
}
|
|
354
464
|
},
|
|
355
465
|
[mode, rawValue, onChange]
|
|
@@ -360,10 +470,10 @@ var Editor = forwardRef(
|
|
|
360
470
|
const current = actionsRef.current?.getMarkdown() ?? rawValue;
|
|
361
471
|
setRawValue(current);
|
|
362
472
|
}
|
|
363
|
-
if ((newMode === "wysiwyg" || newMode === "split") && mode === "markdown") {
|
|
364
|
-
actionsRef.current?.setMarkdown(rawValue);
|
|
365
|
-
}
|
|
366
473
|
setInternalMode(newMode);
|
|
474
|
+
if (newMode === "wysiwyg" || newMode === "split") {
|
|
475
|
+
setMilkdownKey((k) => k + 1);
|
|
476
|
+
}
|
|
367
477
|
},
|
|
368
478
|
[mode, rawValue]
|
|
369
479
|
);
|
|
@@ -377,15 +487,14 @@ var Editor = forwardRef(
|
|
|
377
487
|
"div",
|
|
378
488
|
{
|
|
379
489
|
className: [
|
|
380
|
-
"omu-editor flex flex-col
|
|
381
|
-
"border border-bdr-primary",
|
|
490
|
+
"omu-editor flex flex-col overflow-hidden",
|
|
382
491
|
"bg-bg-primary text-txt-primary",
|
|
383
|
-
"focus-within:outline focus-within:outline-2 focus-within:outline-offset-0 focus-within:outline-ring-primary",
|
|
492
|
+
bordered ? "border border-bdr-primary rounded-md focus-within:outline focus-within:outline-2 focus-within:outline-offset-0 focus-within:outline-ring-primary" : "",
|
|
384
493
|
className
|
|
385
494
|
].join(" "),
|
|
386
495
|
style: wrapperStyle,
|
|
387
496
|
children: [
|
|
388
|
-
(showToolbar || showModeToggle) && /* @__PURE__ */ jsxs("div", { className: "flex items-center bg-bg-secondary border-b border-bdr-secondary
|
|
497
|
+
(showToolbar || showModeToggle) && /* @__PURE__ */ jsxs("div", { className: "flex items-center bg-bg-secondary border-b border-bdr-secondary", children: [
|
|
389
498
|
showToolbar && /* @__PURE__ */ jsx(EditorToolbar, { items: toolbarItems, onAction: handleToolbarAction }),
|
|
390
499
|
showModeToggle && !readOnly && /* @__PURE__ */ jsx(ModeToggle, { mode, onModeChange: handleModeChange })
|
|
391
500
|
] }),
|
|
@@ -393,7 +502,7 @@ var Editor = forwardRef(
|
|
|
393
502
|
(mode === "wysiwyg" || mode === "split") && /* @__PURE__ */ jsx("div", { className: `flex flex-col flex-1 min-h-0 ${mode === "split" ? "border-r border-bdr-secondary" : ""}`, children: /* @__PURE__ */ jsx(MilkdownProvider, { children: /* @__PURE__ */ jsx(
|
|
394
503
|
MilkdownInner,
|
|
395
504
|
{
|
|
396
|
-
defaultValue:
|
|
505
|
+
defaultValue: rawValue,
|
|
397
506
|
onChange: handleWysiwygChange,
|
|
398
507
|
readOnly,
|
|
399
508
|
placeholder,
|
|
@@ -401,7 +510,7 @@ var Editor = forwardRef(
|
|
|
401
510
|
skipRef,
|
|
402
511
|
actionsRef
|
|
403
512
|
}
|
|
404
|
-
) }) }),
|
|
513
|
+
) }, milkdownKey) }),
|
|
405
514
|
(mode === "markdown" || mode === "split") && /* @__PURE__ */ jsx("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx(
|
|
406
515
|
MarkdownTextarea,
|
|
407
516
|
{
|
package/dist/editor/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/editor/Editor.tsx"],"sourcesContent":["/**\n * Editor — Markdown + rich text editor.\n *\n * Built on Milkdown (ProseMirror + Remark) for markdown-first editing\n * with perfect round-trip fidelity. Supports WYSIWYG, raw markdown,\n * and split (side-by-side) editing modes.\n *\n * Styled exclusively with MCP Apps spec CSS variables. All typography,\n * colors, borders, and spacing use the host's theme tokens.\n *\n * Imported separately from the core library to keep apps that don't\n * need it from paying any bundle cost:\n *\n * import { Editor } from \"open-mcp-app-ui/editor\";\n */\n\nimport React, {\n useState,\n useRef,\n useCallback,\n useEffect,\n forwardRef,\n useImperativeHandle,\n type CSSProperties,\n} from \"react\";\nimport {\n Editor as MilkdownEditor,\n rootCtx,\n defaultValueCtx,\n} from \"@milkdown/kit/core\";\nimport { commonmark } from \"@milkdown/kit/preset/commonmark\";\nimport { gfm } from \"@milkdown/kit/preset/gfm\";\nimport { history } from \"@milkdown/kit/plugin/history\";\nimport { clipboard } from \"@milkdown/kit/plugin/clipboard\";\nimport { listener, listenerCtx } from \"@milkdown/plugin-listener\";\nimport { replaceAll, getMarkdown } from \"@milkdown/utils\";\nimport { Milkdown, MilkdownProvider, useEditor } from \"@milkdown/react\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Available toolbar action items. */\nexport type ToolbarItem =\n | \"bold\"\n | \"italic\"\n | \"strikethrough\"\n | \"heading\"\n | \"bulletList\"\n | \"orderedList\"\n | \"taskList\"\n | \"code\"\n | \"codeBlock\"\n | \"blockquote\"\n | \"link\"\n | \"divider\"\n | \"undo\"\n | \"redo\";\n\n/** Editing mode. */\nexport type EditorMode = \"wysiwyg\" | \"markdown\" | \"split\";\n\nexport interface EditorProps {\n /** Markdown string (source of truth). */\n value?: string;\n /** Called when content changes. Receives the updated markdown string. */\n onChange?: (markdown: string) => void;\n /**\n * Editing mode:\n * - \"wysiwyg\" — Rich text rendering with formatting (default)\n * - \"markdown\" — Raw markdown text editing\n * - \"split\" — Side-by-side WYSIWYG and markdown\n */\n mode?: EditorMode;\n /** Placeholder text when the editor is empty. */\n placeholder?: string;\n /**\n * Toolbar buttons to show, or `false` to hide the toolbar entirely.\n * Defaults to a sensible set of common formatting actions.\n */\n toolbar?: ToolbarItem[] | false;\n /** View-only mode. Renders markdown as styled content without editing. */\n readOnly?: boolean;\n /** Minimum editor height in pixels. */\n minHeight?: number;\n /** Maximum editor height in pixels before scrolling. */\n maxHeight?: number;\n /** Focus the editor on mount. */\n autoFocus?: boolean;\n /** Additional CSS classes on the outermost wrapper. */\n className?: string;\n}\n\nexport interface EditorRef {\n /** Get the current markdown content. */\n getMarkdown: () => string;\n /** Imperatively set editor content without triggering onChange. */\n setMarkdown: (markdown: string) => void;\n /** Focus the editor. */\n focus: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_TOOLBAR: ToolbarItem[] = [\n \"bold\",\n \"italic\",\n \"strikethrough\",\n \"divider\",\n \"heading\",\n \"bulletList\",\n \"orderedList\",\n \"taskList\",\n \"divider\",\n \"code\",\n \"codeBlock\",\n \"blockquote\",\n \"link\",\n \"divider\",\n \"undo\",\n \"redo\",\n];\n\n// ---------------------------------------------------------------------------\n// Toolbar\n// ---------------------------------------------------------------------------\n\n/**\n * Icon components for toolbar buttons.\n * Minimal SVG icons sized at 16x16, using currentColor for theme awareness.\n */\nconst ToolbarIcons: Record<Exclude<ToolbarItem, \"divider\">, () => React.ReactElement> = {\n bold: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" /><path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" />\n </svg>\n ),\n italic: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\" /><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\" /><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\" />\n </svg>\n ),\n strikethrough: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M16 4H9a3 3 0 0 0-3 3c0 2 1.5 3 3 3h6\" /><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\" /><path d=\"M15 12c1.5 0 3 1 3 3a3 3 0 0 1-3 3H8\" />\n </svg>\n ),\n heading: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M6 4v16\" /><path d=\"M18 4v16\" /><path d=\"M6 12h12\" />\n </svg>\n ),\n bulletList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"9\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"9\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"9\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"4\" cy=\"6\" r=\"1\" fill=\"currentColor\" /><circle cx=\"4\" cy=\"12\" r=\"1\" fill=\"currentColor\" /><circle cx=\"4\" cy=\"18\" r=\"1\" fill=\"currentColor\" />\n </svg>\n ),\n orderedList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"10\" y1=\"6\" x2=\"21\" y2=\"6\" /><line x1=\"10\" y1=\"12\" x2=\"21\" y2=\"12\" /><line x1=\"10\" y1=\"18\" x2=\"21\" y2=\"18\" />\n <text x=\"3\" y=\"7\" fontSize=\"7\" fill=\"currentColor\" stroke=\"none\" fontFamily=\"sans-serif\">1</text>\n <text x=\"3\" y=\"13\" fontSize=\"7\" fill=\"currentColor\" stroke=\"none\" fontFamily=\"sans-serif\">2</text>\n <text x=\"3\" y=\"19\" fontSize=\"7\" fill=\"currentColor\" stroke=\"none\" fontFamily=\"sans-serif\">3</text>\n </svg>\n ),\n taskList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"5\" width=\"6\" height=\"6\" rx=\"1\" /><path d=\"M5 8l1.5 1.5L9 7\" /><line x1=\"13\" y1=\"8\" x2=\"21\" y2=\"8\" />\n <rect x=\"3\" y=\"14\" width=\"6\" height=\"6\" rx=\"1\" /><line x1=\"13\" y1=\"17\" x2=\"21\" y2=\"17\" />\n </svg>\n ),\n code: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"16 18 22 12 16 6\" /><polyline points=\"8 6 2 12 8 18\" />\n </svg>\n ),\n codeBlock: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" /><polyline points=\"9 8 5 12 9 16\" /><polyline points=\"15 8 19 12 15 16\" />\n </svg>\n ),\n blockquote: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M10 8V6a6 6 0 0 0-6 6v4h4v-4H6a4 4 0 0 1 4-4zm10 0V6a6 6 0 0 0-6 6v4h4v-4h-2a4 4 0 0 1 4-4z\" />\n </svg>\n ),\n link: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\" />\n <path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\" />\n </svg>\n ),\n undo: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"1 4 1 10 7 10\" /><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\" />\n </svg>\n ),\n redo: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"23 4 23 10 17 10\" /><path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\" />\n </svg>\n ),\n};\n\n/**\n * Toolbar button labels for accessibility and tooltips.\n */\nconst TOOLBAR_LABELS: Record<Exclude<ToolbarItem, \"divider\">, string> = {\n bold: \"Bold\",\n italic: \"Italic\",\n strikethrough: \"Strikethrough\",\n heading: \"Heading\",\n bulletList: \"Bullet List\",\n orderedList: \"Ordered List\",\n taskList: \"Task List\",\n code: \"Inline Code\",\n codeBlock: \"Code Block\",\n blockquote: \"Blockquote\",\n link: \"Link\",\n undo: \"Undo\",\n redo: \"Redo\",\n};\n\n/**\n * Markdown syntax inserted when a toolbar button is clicked.\n * For WYSIWYG mode, commands would go through ProseMirror, but for\n * markdown mode or as a fallback, we insert raw syntax at the cursor.\n */\nconst TOOLBAR_MARKDOWN: Record<Exclude<ToolbarItem, \"divider\">, string> = {\n bold: \"**bold**\",\n italic: \"*italic*\",\n strikethrough: \"~~strikethrough~~\",\n heading: \"## \",\n bulletList: \"- \",\n orderedList: \"1. \",\n taskList: \"- [ ] \",\n code: \"`code`\",\n codeBlock: \"```\\n\\n```\",\n blockquote: \"> \",\n link: \"[text](url)\",\n undo: \"\",\n redo: \"\",\n};\n\n/**\n * Toolbar component rendered above the editor.\n * Provides formatting action buttons styled with spec CSS variables.\n */\nconst EditorToolbar = ({\n items,\n onAction,\n}: {\n items: ToolbarItem[];\n onAction: (item: ToolbarItem) => void;\n}) => (\n <div className=\"omu-editor-toolbar flex items-center gap-0.5 px-2 py-1.5 bg-bg-secondary border-b border-bdr-secondary rounded-t-md overflow-x-auto\">\n {items.map((item, i) => {\n if (item === \"divider\") {\n return (\n <div\n key={`divider-${i}`}\n className=\"w-px h-4 bg-bdr-secondary mx-1 shrink-0\"\n />\n );\n }\n\n const Icon = ToolbarIcons[item];\n const label = TOOLBAR_LABELS[item];\n\n return (\n <button\n key={item}\n type=\"button\"\n className=\"inline-flex items-center justify-center w-7 h-7 rounded-sm text-txt-secondary hover:text-txt-primary hover:bg-bg-tertiary transition-colors shrink-0 cursor-pointer\"\n onClick={() => onAction(item)}\n title={label}\n aria-label={label}\n >\n <Icon />\n </button>\n );\n })}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Mode Toggle\n// ---------------------------------------------------------------------------\n\n/**\n * Mode toggle buttons shown at the right of the toolbar.\n * Allows switching between wysiwyg, markdown, and split modes.\n */\nconst ModeToggle = ({\n mode,\n onModeChange,\n}: {\n mode: EditorMode;\n onModeChange: (mode: EditorMode) => void;\n}) => {\n const modes: { value: EditorMode; label: string }[] = [\n { value: \"wysiwyg\", label: \"Rich\" },\n { value: \"markdown\", label: \"MD\" },\n { value: \"split\", label: \"Split\" },\n ];\n\n return (\n <div className=\"flex items-center gap-0.5 ml-auto pl-2\">\n {modes.map((m) => (\n <button\n key={m.value}\n type=\"button\"\n className={[\n \"px-2 py-0.5 rounded-sm text-xs font-medium transition-colors cursor-pointer\",\n mode === m.value\n ? \"bg-bg-tertiary text-txt-primary\"\n : \"text-txt-tertiary hover:text-txt-secondary hover:bg-bg-tertiary\",\n ].join(\" \")}\n onClick={() => onModeChange(m.value)}\n >\n {m.label}\n </button>\n ))}\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Milkdown WYSIWYG Editor (inner component)\n// ---------------------------------------------------------------------------\n\ninterface MilkdownInnerProps {\n defaultValue: string;\n onChange: (markdown: string) => void;\n readOnly: boolean;\n placeholder?: string;\n autoFocus?: boolean;\n /** Ref for skipping onChange on imperative updates */\n skipRef: React.MutableRefObject<boolean>;\n /** Ref to expose editor actions to parent */\n actionsRef: React.MutableRefObject<{\n getMarkdown: () => string;\n setMarkdown: (md: string) => void;\n focus: () => void;\n } | null>;\n}\n\n/**\n * Inner Milkdown component that must render inside MilkdownProvider.\n * Handles the actual editor setup with plugins and listeners.\n */\nconst MilkdownInner = ({\n defaultValue,\n onChange,\n readOnly,\n placeholder,\n autoFocus,\n skipRef,\n actionsRef,\n}: MilkdownInnerProps) => {\n const contentRef = useRef(defaultValue);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const { get } = useEditor((root) =>\n MilkdownEditor.make()\n .config((ctx) => {\n ctx.set(rootCtx, root);\n ctx.set(defaultValueCtx, defaultValue);\n ctx.get(listenerCtx).markdownUpdated((_, markdown) => {\n contentRef.current = markdown;\n if (skipRef.current) {\n skipRef.current = false;\n return;\n }\n onChange(markdown);\n });\n })\n .use(commonmark)\n .use(gfm)\n .use(history)\n .use(clipboard)\n .use(listener)\n );\n\n // Expose actions to parent\n useEffect(() => {\n actionsRef.current = {\n getMarkdown: () => contentRef.current,\n setMarkdown: (md: string) => {\n const editor = get();\n if (!editor) return;\n skipRef.current = true;\n editor.action(replaceAll(md));\n contentRef.current = md;\n },\n focus: () => {\n const el = wrapperRef.current?.querySelector(\".ProseMirror\");\n if (el instanceof HTMLElement) el.focus();\n },\n };\n }, [get, skipRef, actionsRef]);\n\n // Auto-focus on mount\n useEffect(() => {\n if (autoFocus) {\n requestAnimationFrame(() => {\n const el = wrapperRef.current?.querySelector(\".ProseMirror\");\n if (el instanceof HTMLElement) el.focus();\n });\n }\n }, [autoFocus]);\n\n /**\n * Handle clicks on the wrapper to focus the editor.\n * Allows clicking empty space below content to start editing.\n */\n const handleWrapperClick = useCallback(() => {\n const el = wrapperRef.current?.querySelector(\".ProseMirror\");\n if (el instanceof HTMLElement) el.focus();\n }, []);\n\n return (\n <div\n ref={wrapperRef}\n className=\"omu-editor-content flex-1 overflow-y-auto px-4 py-3 flex flex-col min-h-0 cursor-text\"\n data-placeholder={placeholder}\n data-readonly={readOnly || undefined}\n onClick={readOnly ? undefined : handleWrapperClick}\n >\n <Milkdown />\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Raw Markdown Editor\n// ---------------------------------------------------------------------------\n\n/**\n * Simple textarea for raw markdown editing.\n * Provides a monospace code-editing experience.\n */\nconst MarkdownTextarea = ({\n value,\n onChange,\n readOnly,\n placeholder,\n autoFocus,\n minHeight,\n maxHeight,\n}: {\n value: string;\n onChange: (markdown: string) => void;\n readOnly: boolean;\n placeholder?: string;\n autoFocus?: boolean;\n minHeight?: number;\n maxHeight?: number;\n}) => (\n <textarea\n className={[\n \"omu-editor-textarea flex-1 w-full px-4 py-3 resize-none\",\n \"bg-bg-primary text-txt-primary font-mono text-sm\",\n \"outline-none border-none\",\n \"placeholder:text-txt-tertiary\",\n ].join(\" \")}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n readOnly={readOnly}\n placeholder={placeholder}\n autoFocus={autoFocus}\n style={{\n minHeight: minHeight ? `${minHeight}px` : undefined,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n spellCheck={false}\n />\n);\n\n// ---------------------------------------------------------------------------\n// Editor (Main Export)\n// ---------------------------------------------------------------------------\n\n/**\n * Markdown + rich text editor with toolbar and mode switching.\n *\n * Built on Milkdown (ProseMirror + Remark) for markdown-first editing.\n * Supports WYSIWYG, raw markdown, and split editing modes. Styled with\n * MCP Apps spec CSS variables for automatic host theming.\n *\n * @example\n * ```tsx\n * import { Editor } from \"open-mcp-app-ui/editor\";\n *\n * <Editor\n * value={markdown}\n * onChange={setMarkdown}\n * placeholder=\"Start writing...\"\n * />\n * ```\n */\nexport const Editor = forwardRef<EditorRef, EditorProps>(\n (\n {\n value = \"\",\n onChange,\n mode: modeProp,\n placeholder,\n toolbar: toolbarProp,\n readOnly = false,\n minHeight = 120,\n maxHeight,\n autoFocus = false,\n className = \"\",\n },\n ref\n ) => {\n // -----------------------------------------------------------------------\n // State\n // -----------------------------------------------------------------------\n\n const [internalMode, setInternalMode] = useState<EditorMode>(modeProp ?? \"wysiwyg\");\n const mode = modeProp ?? internalMode;\n const showModeToggle = modeProp == null;\n\n /**\n * Internal markdown state used for the raw markdown textarea.\n * The WYSIWYG editor uses Milkdown's internal state; this mirrors\n * it for the markdown pane and is synced on mode switches.\n */\n const [rawValue, setRawValue] = useState(value);\n\n const skipRef = useRef(false);\n const actionsRef = useRef<{\n getMarkdown: () => string;\n setMarkdown: (md: string) => void;\n focus: () => void;\n } | null>(null);\n\n // Sync raw value when external value changes\n useEffect(() => {\n setRawValue(value);\n }, [value]);\n\n // -----------------------------------------------------------------------\n // Imperative handle\n // -----------------------------------------------------------------------\n\n useImperativeHandle(ref, () => ({\n getMarkdown: () => {\n if (mode === \"markdown\") return rawValue;\n return actionsRef.current?.getMarkdown() ?? rawValue;\n },\n setMarkdown: (md: string) => {\n setRawValue(md);\n actionsRef.current?.setMarkdown(md);\n },\n focus: () => {\n actionsRef.current?.focus();\n },\n }), [mode, rawValue]);\n\n // -----------------------------------------------------------------------\n // Handlers\n // -----------------------------------------------------------------------\n\n /** Handle changes from the WYSIWYG editor. */\n const handleWysiwygChange = useCallback(\n (markdown: string) => {\n setRawValue(markdown);\n onChange?.(markdown);\n },\n [onChange]\n );\n\n /** Handle changes from the raw markdown textarea. */\n const handleRawChange = useCallback(\n (markdown: string) => {\n setRawValue(markdown);\n onChange?.(markdown);\n // Sync to WYSIWYG if in split mode\n if (mode === \"split\") {\n actionsRef.current?.setMarkdown(markdown);\n }\n },\n [onChange, mode]\n );\n\n /** Handle toolbar button clicks. */\n const handleToolbarAction = useCallback(\n (item: ToolbarItem) => {\n if (item === \"divider\") return;\n\n if (mode === \"markdown\") {\n // In markdown mode, insert raw syntax\n const syntax = TOOLBAR_MARKDOWN[item];\n if (syntax) {\n const newValue = rawValue + syntax;\n setRawValue(newValue);\n onChange?.(newValue);\n }\n return;\n }\n\n // In WYSIWYG/split mode, insert via Milkdown\n const syntax = TOOLBAR_MARKDOWN[item];\n if (syntax && actionsRef.current) {\n const current = actionsRef.current.getMarkdown();\n const newValue = current + syntax;\n actionsRef.current.setMarkdown(newValue);\n setRawValue(newValue);\n onChange?.(newValue);\n }\n },\n [mode, rawValue, onChange]\n );\n\n /** Handle mode switching. */\n const handleModeChange = useCallback(\n (newMode: EditorMode) => {\n // Sync content between modes before switching\n if (mode === \"wysiwyg\" || mode === \"split\") {\n const current = actionsRef.current?.getMarkdown() ?? rawValue;\n setRawValue(current);\n }\n if ((newMode === \"wysiwyg\" || newMode === \"split\") && mode === \"markdown\") {\n actionsRef.current?.setMarkdown(rawValue);\n }\n setInternalMode(newMode);\n },\n [mode, rawValue]\n );\n\n // -----------------------------------------------------------------------\n // Toolbar config\n // -----------------------------------------------------------------------\n\n const toolbarItems = toolbarProp === false ? null : (toolbarProp ?? DEFAULT_TOOLBAR);\n const showToolbar = toolbarItems != null && !readOnly;\n\n // -----------------------------------------------------------------------\n // Render\n // -----------------------------------------------------------------------\n\n const wrapperStyle: CSSProperties = {\n minHeight: `${minHeight}px`,\n ...(maxHeight ? { maxHeight: `${maxHeight}px` } : {}),\n };\n\n return (\n <div\n className={[\n \"omu-editor flex flex-col rounded-md overflow-hidden\",\n \"border border-bdr-primary\",\n \"bg-bg-primary text-txt-primary\",\n \"focus-within:outline focus-within:outline-2 focus-within:outline-offset-0 focus-within:outline-ring-primary\",\n className,\n ].join(\" \")}\n style={wrapperStyle}\n >\n {/* Toolbar */}\n {(showToolbar || showModeToggle) && (\n <div className=\"flex items-center bg-bg-secondary border-b border-bdr-secondary rounded-t-md\">\n {showToolbar && (\n <EditorToolbar items={toolbarItems} onAction={handleToolbarAction} />\n )}\n {showModeToggle && !readOnly && (\n <ModeToggle mode={mode} onModeChange={handleModeChange} />\n )}\n </div>\n )}\n\n {/* Editor content */}\n <div className=\"flex flex-1 min-h-0 overflow-hidden\">\n {/* WYSIWYG pane */}\n {(mode === \"wysiwyg\" || mode === \"split\") && (\n <div className={`flex flex-col flex-1 min-h-0 ${mode === \"split\" ? \"border-r border-bdr-secondary\" : \"\"}`}>\n <MilkdownProvider>\n <MilkdownInner\n defaultValue={value}\n onChange={handleWysiwygChange}\n readOnly={readOnly}\n placeholder={placeholder}\n autoFocus={autoFocus && mode === \"wysiwyg\"}\n skipRef={skipRef}\n actionsRef={actionsRef}\n />\n </MilkdownProvider>\n </div>\n )}\n\n {/* Markdown pane */}\n {(mode === \"markdown\" || mode === \"split\") && (\n <div className=\"flex flex-col flex-1 min-h-0\">\n <MarkdownTextarea\n value={rawValue}\n onChange={handleRawChange}\n readOnly={readOnly}\n placeholder={mode === \"markdown\" ? placeholder : \"Raw markdown...\"}\n autoFocus={autoFocus && mode === \"markdown\"}\n minHeight={minHeight - 40}\n maxHeight={maxHeight ? maxHeight - 40 : undefined}\n />\n </div>\n )}\n </div>\n </div>\n );\n }\n);\n\nEditor.displayName = \"Editor\";\n"],"mappings":"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","names":["syntax"]}
|
|
1
|
+
{"version":3,"sources":["../../src/editor/Editor.tsx"],"sourcesContent":["/**\n * Editor — Markdown + rich text editor.\n *\n * Built on Milkdown (ProseMirror + Remark) for markdown-first editing\n * with perfect round-trip fidelity. Supports WYSIWYG, raw markdown,\n * and split (side-by-side) editing modes.\n *\n * Styled exclusively with MCP Apps spec CSS variables. All typography,\n * colors, borders, and spacing use the host's theme tokens.\n *\n * Imported separately from the core library to keep apps that don't\n * need it from paying any bundle cost:\n *\n * import { Editor } from \"open-mcp-app-ui/editor\";\n */\n\nimport React, {\n useState,\n useRef,\n useCallback,\n useEffect,\n forwardRef,\n useImperativeHandle,\n type CSSProperties,\n} from \"react\";\nimport {\n Editor as MilkdownEditor,\n rootCtx,\n defaultValueCtx,\n commandsCtx,\n} from \"@milkdown/kit/core\";\nimport {\n commonmark,\n toggleStrongCommand,\n toggleEmphasisCommand,\n wrapInHeadingCommand,\n wrapInBulletListCommand,\n wrapInOrderedListCommand,\n toggleInlineCodeCommand,\n createCodeBlockCommand,\n wrapInBlockquoteCommand,\n toggleLinkCommand,\n} from \"@milkdown/kit/preset/commonmark\";\nimport {\n gfm,\n toggleStrikethroughCommand,\n} from \"@milkdown/kit/preset/gfm\";\nimport {\n history,\n undoCommand,\n redoCommand,\n} from \"@milkdown/kit/plugin/history\";\nimport { clipboard } from \"@milkdown/kit/plugin/clipboard\";\nimport { listener, listenerCtx } from \"@milkdown/plugin-listener\";\nimport { replaceAll } from \"@milkdown/utils\";\nimport { Milkdown, MilkdownProvider, useEditor } from \"@milkdown/react\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Available toolbar action items. */\nexport type ToolbarItem =\n | \"bold\"\n | \"italic\"\n | \"strikethrough\"\n | \"heading\"\n | \"bulletList\"\n | \"orderedList\"\n | \"taskList\"\n | \"code\"\n | \"codeBlock\"\n | \"blockquote\"\n | \"link\"\n | \"divider\"\n | \"undo\"\n | \"redo\";\n\n/** Editing mode. */\nexport type EditorMode = \"wysiwyg\" | \"markdown\" | \"split\";\n\nexport interface EditorProps {\n /** Markdown string (source of truth). */\n value?: string;\n /** Called when content changes. Receives the updated markdown string. */\n onChange?: (markdown: string) => void;\n /**\n * Editing mode:\n * - \"wysiwyg\" — Rich text rendering with formatting (default)\n * - \"markdown\" — Raw markdown text editing\n * - \"split\" — Side-by-side WYSIWYG and markdown\n *\n * Omit to show a mode toggle in the toolbar so users can switch freely.\n */\n mode?: EditorMode;\n /** Placeholder text when the editor is empty. */\n placeholder?: string;\n /**\n * Toolbar buttons to show, or `false` to hide the toolbar entirely.\n * Defaults to a sensible set of common formatting actions.\n */\n toolbar?: ToolbarItem[] | false;\n /** View-only mode. Renders markdown as styled content without editing. */\n readOnly?: boolean;\n /**\n * Show a border and rounded corners around the editor.\n * When false (default), the editor sits flat within its container,\n * making it easy to embed inside cards or other bordered elements.\n */\n bordered?: boolean;\n /** Minimum editor height in pixels. */\n minHeight?: number;\n /** Maximum editor height in pixels before scrolling. */\n maxHeight?: number;\n /** Focus the editor on mount. */\n autoFocus?: boolean;\n /** Additional CSS classes on the outermost wrapper. */\n className?: string;\n}\n\nexport interface EditorRef {\n /** Get the current markdown content. */\n getMarkdown: () => string;\n /** Imperatively set editor content without triggering onChange. */\n setMarkdown: (markdown: string) => void;\n /** Focus the editor. */\n focus: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_TOOLBAR: ToolbarItem[] = [\n \"bold\",\n \"italic\",\n \"strikethrough\",\n \"divider\",\n \"heading\",\n \"bulletList\",\n \"orderedList\",\n \"taskList\",\n \"divider\",\n \"code\",\n \"codeBlock\",\n \"blockquote\",\n \"link\",\n \"divider\",\n \"undo\",\n \"redo\",\n];\n\n/**\n * Markdown syntax inserted when a toolbar button is clicked in raw mode.\n * In WYSIWYG mode, Milkdown commands handle formatting instead.\n */\nconst TOOLBAR_MARKDOWN: Record<Exclude<ToolbarItem, \"divider\">, string> = {\n bold: \"**bold**\",\n italic: \"*italic*\",\n strikethrough: \"~~strikethrough~~\",\n heading: \"## \",\n bulletList: \"- \",\n orderedList: \"1. \",\n taskList: \"- [ ] \",\n code: \"`code`\",\n codeBlock: \"```\\n\\n```\",\n blockquote: \"> \",\n link: \"[text](url)\",\n undo: \"\",\n redo: \"\",\n};\n\n// ---------------------------------------------------------------------------\n// Toolbar\n// ---------------------------------------------------------------------------\n\n/**\n * Icon components for toolbar buttons.\n * Minimal SVG icons sized at 16x16, using currentColor for theme awareness.\n */\nconst ToolbarIcons: Record<Exclude<ToolbarItem, \"divider\">, () => React.ReactElement> = {\n bold: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" /><path d=\"M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z\" />\n </svg>\n ),\n italic: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\" /><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\" /><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\" />\n </svg>\n ),\n strikethrough: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M16 4H9a3 3 0 0 0-3 3c0 2 1.5 3 3 3h6\" /><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\" /><path d=\"M15 12c1.5 0 3 1 3 3a3 3 0 0 1-3 3H8\" />\n </svg>\n ),\n heading: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M6 4v16\" /><path d=\"M18 4v16\" /><path d=\"M6 12h12\" />\n </svg>\n ),\n bulletList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"9\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"9\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"9\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"4\" cy=\"6\" r=\"1\" fill=\"currentColor\" /><circle cx=\"4\" cy=\"12\" r=\"1\" fill=\"currentColor\" /><circle cx=\"4\" cy=\"18\" r=\"1\" fill=\"currentColor\" />\n </svg>\n ),\n orderedList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"10\" y1=\"6\" x2=\"21\" y2=\"6\" /><line x1=\"10\" y1=\"12\" x2=\"21\" y2=\"12\" /><line x1=\"10\" y1=\"18\" x2=\"21\" y2=\"18\" />\n <text x=\"3\" y=\"7\" fontSize=\"7\" fill=\"currentColor\" stroke=\"none\" fontFamily=\"sans-serif\">1</text>\n <text x=\"3\" y=\"13\" fontSize=\"7\" fill=\"currentColor\" stroke=\"none\" fontFamily=\"sans-serif\">2</text>\n <text x=\"3\" y=\"19\" fontSize=\"7\" fill=\"currentColor\" stroke=\"none\" fontFamily=\"sans-serif\">3</text>\n </svg>\n ),\n taskList: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"5\" width=\"6\" height=\"6\" rx=\"1\" /><path d=\"M5 8l1.5 1.5L9 7\" /><line x1=\"13\" y1=\"8\" x2=\"21\" y2=\"8\" />\n <rect x=\"3\" y=\"14\" width=\"6\" height=\"6\" rx=\"1\" /><line x1=\"13\" y1=\"17\" x2=\"21\" y2=\"17\" />\n </svg>\n ),\n code: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"16 18 22 12 16 6\" /><polyline points=\"8 6 2 12 8 18\" />\n </svg>\n ),\n codeBlock: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" /><polyline points=\"9 8 5 12 9 16\" /><polyline points=\"15 8 19 12 15 16\" />\n </svg>\n ),\n blockquote: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M10 8V6a6 6 0 0 0-6 6v4h4v-4H6a4 4 0 0 1 4-4zm10 0V6a6 6 0 0 0-6 6v4h4v-4h-2a4 4 0 0 1 4-4z\" />\n </svg>\n ),\n link: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\" />\n <path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\" />\n </svg>\n ),\n undo: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"1 4 1 10 7 10\" /><path d=\"M3.51 15a9 9 0 1 0 2.13-9.36L1 10\" />\n </svg>\n ),\n redo: () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"23 4 23 10 17 10\" /><path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\" />\n </svg>\n ),\n};\n\n/**\n * Toolbar button labels for accessibility and tooltips.\n */\nconst TOOLBAR_LABELS: Record<Exclude<ToolbarItem, \"divider\">, string> = {\n bold: \"Bold\",\n italic: \"Italic\",\n strikethrough: \"Strikethrough\",\n heading: \"Heading\",\n bulletList: \"Bullet List\",\n orderedList: \"Ordered List\",\n taskList: \"Task List\",\n code: \"Inline Code\",\n codeBlock: \"Code Block\",\n blockquote: \"Blockquote\",\n link: \"Link\",\n undo: \"Undo\",\n redo: \"Redo\",\n};\n\n/**\n * Toolbar component rendered above the editor.\n * Provides formatting action buttons styled with spec CSS variables.\n */\nconst EditorToolbar = ({\n items,\n onAction,\n}: {\n items: ToolbarItem[];\n onAction: (item: ToolbarItem) => void;\n}) => (\n <div className=\"omu-editor-toolbar flex items-center gap-0.5 px-2 py-1.5 overflow-x-auto\">\n {items.map((item, i) => {\n if (item === \"divider\") {\n return (\n <div\n key={`divider-${i}`}\n className=\"w-px h-4 bg-bdr-secondary mx-1 shrink-0\"\n />\n );\n }\n\n const Icon = ToolbarIcons[item];\n const label = TOOLBAR_LABELS[item];\n\n return (\n <button\n key={item}\n type=\"button\"\n className=\"inline-flex items-center justify-center w-7 h-7 rounded-sm text-txt-secondary hover:text-txt-primary hover:bg-bg-tertiary transition-colors shrink-0 cursor-pointer\"\n onMouseDown={(e) => {\n /**\n * Prevent the button from stealing focus from the ProseMirror editor.\n * Without this, clicking a toolbar button blurs the editor, causing\n * toggle/wrap commands to fail silently (no selection to act on).\n */\n e.preventDefault();\n }}\n onClick={() => onAction(item)}\n title={label}\n aria-label={label}\n >\n <Icon />\n </button>\n );\n })}\n </div>\n);\n\n// ---------------------------------------------------------------------------\n// Mode Toggle\n// ---------------------------------------------------------------------------\n\n/**\n * Mode toggle buttons shown at the right of the toolbar.\n * Allows switching between wysiwyg, markdown, and split modes.\n */\nconst ModeToggle = ({\n mode,\n onModeChange,\n}: {\n mode: EditorMode;\n onModeChange: (mode: EditorMode) => void;\n}) => {\n const modes: { value: EditorMode; label: string }[] = [\n { value: \"wysiwyg\", label: \"Rich\" },\n { value: \"markdown\", label: \"MD\" },\n { value: \"split\", label: \"Split\" },\n ];\n\n return (\n <div className=\"flex items-center gap-0.5 ml-auto pl-2 pr-2\">\n {modes.map((m) => (\n <button\n key={m.value}\n type=\"button\"\n className={[\n \"px-2 py-0.5 rounded-sm text-xs font-medium transition-colors cursor-pointer\",\n mode === m.value\n ? \"bg-bg-tertiary text-txt-primary\"\n : \"text-txt-tertiary hover:text-txt-secondary hover:bg-bg-tertiary\",\n ].join(\" \")}\n onClick={() => onModeChange(m.value)}\n >\n {m.label}\n </button>\n ))}\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Milkdown actions ref type\n// ---------------------------------------------------------------------------\n\ninterface MilkdownActions {\n getMarkdown: () => string;\n setMarkdown: (md: string) => void;\n runCommand: (key: unknown, payload?: unknown) => boolean;\n focus: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Milkdown WYSIWYG Editor (inner component)\n// ---------------------------------------------------------------------------\n\ninterface MilkdownInnerProps {\n defaultValue: string;\n onChange: (markdown: string) => void;\n readOnly: boolean;\n placeholder?: string;\n autoFocus?: boolean;\n /** Ref for skipping onChange on imperative updates */\n skipRef: React.MutableRefObject<boolean>;\n /** Ref to expose editor actions to parent */\n actionsRef: React.MutableRefObject<MilkdownActions | null>;\n}\n\n/**\n * Inner Milkdown component that must render inside MilkdownProvider.\n * Handles the actual editor setup with plugins and listeners.\n *\n * Exposes a command runner so toolbar buttons can call real\n * ProseMirror/Milkdown commands (bold, italic, heading, etc.)\n * instead of inserting raw markdown text.\n */\nconst MilkdownInner = ({\n defaultValue,\n onChange,\n readOnly,\n placeholder,\n autoFocus,\n skipRef,\n actionsRef,\n}: MilkdownInnerProps) => {\n const contentRef = useRef(defaultValue);\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n const { get } = useEditor((root) =>\n MilkdownEditor.make()\n .config((ctx) => {\n ctx.set(rootCtx, root);\n ctx.set(defaultValueCtx, defaultValue);\n ctx.get(listenerCtx).markdownUpdated((_, markdown) => {\n contentRef.current = markdown;\n if (skipRef.current) {\n skipRef.current = false;\n return;\n }\n onChange(markdown);\n });\n })\n .use(commonmark)\n .use(gfm)\n .use(history)\n .use(clipboard)\n .use(listener)\n );\n\n /**\n * Expose actions to parent via ref.\n * Includes a runCommand method that executes Milkdown commands\n * (e.g. toggle bold, create heading) through the commandManager.\n */\n useEffect(() => {\n actionsRef.current = {\n getMarkdown: () => contentRef.current,\n\n setMarkdown: (md: string) => {\n const editor = get();\n if (!editor) return;\n try {\n skipRef.current = true;\n editor.action(replaceAll(md));\n contentRef.current = md;\n } catch (e) {\n skipRef.current = false;\n console.warn(\"[Editor] setMarkdown failed:\", e);\n }\n },\n\n /**\n * Execute a Milkdown command by its key.\n * This is how toolbar buttons apply formatting — they call\n * real ProseMirror commands through Milkdown's command manager,\n * which toggles marks/wraps on the current selection.\n */\n runCommand: (key: unknown, payload?: unknown) => {\n const editor = get();\n if (!editor) return false;\n try {\n editor.action((ctx) => {\n const commands = ctx.get(commandsCtx);\n commands.call(key as any, payload);\n });\n return true;\n } catch (e) {\n console.warn(\"[Editor] Command failed:\", e);\n return false;\n }\n },\n\n focus: () => {\n const el = wrapperRef.current?.querySelector(\".ProseMirror\");\n if (el instanceof HTMLElement) el.focus();\n },\n };\n }, [get, skipRef, actionsRef]);\n\n /**\n * Set the data-placeholder attribute on the ProseMirror element\n * so the CSS ::before pseudo-element can show placeholder text.\n */\n useEffect(() => {\n if (!placeholder) return;\n const trySet = () => {\n const el = wrapperRef.current?.querySelector(\".ProseMirror\");\n if (el) {\n el.setAttribute(\"data-placeholder\", placeholder);\n }\n };\n trySet();\n /* Milkdown may render async, retry after a tick */\n const t = setTimeout(trySet, 100);\n return () => clearTimeout(t);\n }, [placeholder]);\n\n // Auto-focus on mount\n useEffect(() => {\n if (autoFocus) {\n requestAnimationFrame(() => {\n const el = wrapperRef.current?.querySelector(\".ProseMirror\");\n if (el instanceof HTMLElement) el.focus();\n });\n }\n }, [autoFocus]);\n\n /**\n * Handle clicks on the wrapper to focus the editor.\n * Allows clicking empty space below content to start editing.\n */\n const handleWrapperClick = useCallback(() => {\n const el = wrapperRef.current?.querySelector(\".ProseMirror\");\n if (el instanceof HTMLElement) el.focus();\n }, []);\n\n return (\n <div\n ref={wrapperRef}\n className=\"omu-editor-content flex-1 overflow-y-auto px-4 py-3 flex flex-col min-h-0 cursor-text\"\n data-placeholder={placeholder}\n data-readonly={readOnly || undefined}\n onClick={readOnly ? undefined : handleWrapperClick}\n >\n <Milkdown />\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Raw Markdown Editor\n// ---------------------------------------------------------------------------\n\n/**\n * Simple textarea for raw markdown editing.\n * Provides a monospace code-editing experience.\n */\nconst MarkdownTextarea = ({\n value,\n onChange,\n readOnly,\n placeholder,\n autoFocus,\n minHeight,\n maxHeight,\n}: {\n value: string;\n onChange: (markdown: string) => void;\n readOnly: boolean;\n placeholder?: string;\n autoFocus?: boolean;\n minHeight?: number;\n maxHeight?: number;\n}) => (\n <textarea\n className={[\n \"omu-editor-textarea flex-1 w-full px-4 py-3 resize-none\",\n \"bg-bg-primary text-txt-primary font-mono text-sm\",\n \"outline-none border-none\",\n \"placeholder:text-txt-tertiary\",\n ].join(\" \")}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n readOnly={readOnly}\n placeholder={placeholder}\n autoFocus={autoFocus}\n style={{\n minHeight: minHeight ? `${minHeight}px` : undefined,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n spellCheck={false}\n />\n);\n\n// ---------------------------------------------------------------------------\n// Editor (Main Export)\n// ---------------------------------------------------------------------------\n\n/**\n * Markdown + rich text editor with toolbar and mode switching.\n *\n * Built on Milkdown (ProseMirror + Remark) for markdown-first editing.\n * Supports WYSIWYG, raw markdown, and split editing modes. Styled with\n * MCP Apps spec CSS variables for automatic host theming.\n *\n * By default the editor has no border or rounded corners so it sits\n * flat inside a containing element. Set `bordered` to add them.\n *\n * @example\n * ```tsx\n * import { Editor } from \"open-mcp-app-ui/editor\";\n *\n * <Editor\n * value={markdown}\n * onChange={setMarkdown}\n * placeholder=\"Start writing...\"\n * />\n *\n * <Editor value={markdown} onChange={setMarkdown} bordered />\n * ```\n */\nexport const Editor = forwardRef<EditorRef, EditorProps>(\n (\n {\n value = \"\",\n onChange,\n mode: modeProp,\n placeholder,\n toolbar: toolbarProp,\n readOnly = false,\n bordered = false,\n minHeight = 120,\n maxHeight,\n autoFocus = false,\n className = \"\",\n },\n ref\n ) => {\n // -----------------------------------------------------------------------\n // State\n // -----------------------------------------------------------------------\n\n const [internalMode, setInternalMode] = useState<EditorMode>(modeProp ?? \"wysiwyg\");\n const mode = modeProp ?? internalMode;\n const showModeToggle = modeProp == null;\n\n /**\n * Internal markdown state used for the raw markdown textarea.\n * The WYSIWYG editor uses Milkdown's internal state; this mirrors\n * it for the markdown pane and is synced on mode switches.\n */\n const [rawValue, setRawValue] = useState(value);\n\n /**\n * Key counter to force MilkdownProvider remount when switching\n * from markdown-only mode back to WYSIWYG. This ensures the\n * editor initializes with the current rawValue as defaultValue\n * and avoids stale context errors.\n */\n const [milkdownKey, setMilkdownKey] = useState(0);\n\n const skipRef = useRef(false);\n const actionsRef = useRef<MilkdownActions | null>(null);\n\n // Sync raw value when external value changes\n useEffect(() => {\n setRawValue(value);\n }, [value]);\n\n // -----------------------------------------------------------------------\n // Imperative handle\n // -----------------------------------------------------------------------\n\n useImperativeHandle(ref, () => ({\n getMarkdown: () => {\n if (mode === \"markdown\") return rawValue;\n return actionsRef.current?.getMarkdown() ?? rawValue;\n },\n setMarkdown: (md: string) => {\n setRawValue(md);\n actionsRef.current?.setMarkdown(md);\n },\n focus: () => {\n actionsRef.current?.focus();\n },\n }), [mode, rawValue]);\n\n // -----------------------------------------------------------------------\n // Handlers\n // -----------------------------------------------------------------------\n\n /** Handle changes from the WYSIWYG editor. */\n const handleWysiwygChange = useCallback(\n (markdown: string) => {\n setRawValue(markdown);\n onChange?.(markdown);\n },\n [onChange]\n );\n\n /** Handle changes from the raw markdown textarea. */\n const handleRawChange = useCallback(\n (markdown: string) => {\n setRawValue(markdown);\n onChange?.(markdown);\n // Sync to WYSIWYG if in split mode\n if (mode === \"split\") {\n actionsRef.current?.setMarkdown(markdown);\n }\n },\n [onChange, mode]\n );\n\n /**\n * Handle toolbar button clicks.\n *\n * In WYSIWYG/split mode, toolbar actions execute real Milkdown\n * commands that toggle marks (bold, italic) or wrap blocks\n * (heading, list, blockquote) on the current selection.\n *\n * In markdown mode, raw syntax is appended as a fallback.\n */\n const handleToolbarAction = useCallback(\n (item: ToolbarItem) => {\n if (item === \"divider\") return;\n\n // ----- Markdown mode: insert raw syntax -----\n if (mode === \"markdown\") {\n const syntax = TOOLBAR_MARKDOWN[item];\n if (syntax) {\n const newValue = rawValue + syntax;\n setRawValue(newValue);\n onChange?.(newValue);\n }\n return;\n }\n\n // ----- WYSIWYG / split mode: use Milkdown commands -----\n if (!actionsRef.current) return;\n\n /**\n * Map toolbar items to Milkdown command keys.\n * Each command key is a CmdKey from the respective preset.\n */\n switch (item) {\n case \"bold\":\n actionsRef.current.runCommand(toggleStrongCommand.key);\n break;\n case \"italic\":\n actionsRef.current.runCommand(toggleEmphasisCommand.key);\n break;\n case \"strikethrough\":\n actionsRef.current.runCommand(toggleStrikethroughCommand.key);\n break;\n case \"heading\": {\n /**\n * Cycle through heading levels: h2 → h3 → h4 → paragraph.\n * Reads the current heading level from the editor and advances\n * to the next, or converts back to a paragraph when at h4.\n * Starts at h2 because h1 is typically reserved for the page title.\n */\n const md = actionsRef.current.getMarkdown();\n const lines = md.split(\"\\n\");\n const lastHeadingMatch = lines[lines.length - 1]?.match(/^(#{1,6})\\s/);\n const currentLevel = lastHeadingMatch ? lastHeadingMatch[1].length : 0;\n if (currentLevel >= 4 || currentLevel === 0) {\n actionsRef.current.runCommand(wrapInHeadingCommand.key, 2);\n } else {\n actionsRef.current.runCommand(wrapInHeadingCommand.key, currentLevel + 1);\n }\n break;\n }\n case \"bulletList\":\n actionsRef.current.runCommand(wrapInBulletListCommand.key);\n break;\n case \"orderedList\":\n actionsRef.current.runCommand(wrapInOrderedListCommand.key);\n break;\n case \"taskList\": {\n /* No direct Milkdown command for task list; insert markdown */\n const current = actionsRef.current.getMarkdown();\n const suffix = current.endsWith(\"\\n\") ? \"- [ ] \" : \"\\n- [ ] \";\n actionsRef.current.setMarkdown(current + suffix);\n break;\n }\n case \"code\":\n actionsRef.current.runCommand(toggleInlineCodeCommand.key);\n break;\n case \"codeBlock\":\n actionsRef.current.runCommand(createCodeBlockCommand.key);\n break;\n case \"blockquote\":\n actionsRef.current.runCommand(wrapInBlockquoteCommand.key);\n break;\n case \"link\": {\n /**\n * Insert a link by toggling the link mark with a placeholder URL.\n * If there's selected text, it wraps it; otherwise inserts a template.\n * Using a placeholder avoids window.prompt which steals focus\n * and breaks the ProseMirror selection.\n */\n actionsRef.current.runCommand(toggleLinkCommand.key, { href: \"https://\" });\n break;\n }\n case \"undo\":\n actionsRef.current.runCommand(undoCommand.key);\n break;\n case \"redo\":\n actionsRef.current.runCommand(redoCommand.key);\n break;\n default:\n break;\n }\n },\n [mode, rawValue, onChange]\n );\n\n /** Handle mode switching. */\n const handleModeChange = useCallback(\n (newMode: EditorMode) => {\n /**\n * Read the latest content from whichever pane is active before\n * switching. This ensures rawValue is always up to date.\n */\n if (mode === \"wysiwyg\" || mode === \"split\") {\n const current = actionsRef.current?.getMarkdown() ?? rawValue;\n setRawValue(current);\n }\n\n setInternalMode(newMode);\n\n /**\n * Force remount of MilkdownProvider when switching TO a mode\n * that includes the WYSIWYG editor. This recreates Milkdown\n * with the latest rawValue as defaultValue, avoiding stale\n * context and \"editorView not found\" errors.\n */\n if (newMode === \"wysiwyg\" || newMode === \"split\") {\n setMilkdownKey((k) => k + 1);\n }\n },\n [mode, rawValue]\n );\n\n // -----------------------------------------------------------------------\n // Toolbar config\n // -----------------------------------------------------------------------\n\n const toolbarItems = toolbarProp === false ? null : (toolbarProp ?? DEFAULT_TOOLBAR);\n const showToolbar = toolbarItems != null && !readOnly;\n\n // -----------------------------------------------------------------------\n // Render\n // -----------------------------------------------------------------------\n\n const wrapperStyle: CSSProperties = {\n minHeight: `${minHeight}px`,\n ...(maxHeight ? { maxHeight: `${maxHeight}px` } : {}),\n };\n\n return (\n <div\n className={[\n \"omu-editor flex flex-col overflow-hidden\",\n \"bg-bg-primary text-txt-primary\",\n bordered\n ? \"border border-bdr-primary rounded-md focus-within:outline focus-within:outline-2 focus-within:outline-offset-0 focus-within:outline-ring-primary\"\n : \"\",\n className,\n ].join(\" \")}\n style={wrapperStyle}\n >\n {/* Toolbar */}\n {(showToolbar || showModeToggle) && (\n <div className=\"flex items-center bg-bg-secondary border-b border-bdr-secondary\">\n {showToolbar && (\n <EditorToolbar items={toolbarItems} onAction={handleToolbarAction} />\n )}\n {showModeToggle && !readOnly && (\n <ModeToggle mode={mode} onModeChange={handleModeChange} />\n )}\n </div>\n )}\n\n {/* Editor content */}\n <div className=\"flex flex-1 min-h-0 overflow-hidden\">\n {/* WYSIWYG pane */}\n {(mode === \"wysiwyg\" || mode === \"split\") && (\n <div className={`flex flex-col flex-1 min-h-0 ${mode === \"split\" ? \"border-r border-bdr-secondary\" : \"\"}`}>\n <MilkdownProvider key={milkdownKey}>\n <MilkdownInner\n defaultValue={rawValue}\n onChange={handleWysiwygChange}\n readOnly={readOnly}\n placeholder={placeholder}\n autoFocus={autoFocus && mode === \"wysiwyg\"}\n skipRef={skipRef}\n actionsRef={actionsRef}\n />\n </MilkdownProvider>\n </div>\n )}\n\n {/* Markdown pane */}\n {(mode === \"markdown\" || mode === \"split\") && (\n <div className=\"flex flex-col flex-1 min-h-0\">\n <MarkdownTextarea\n value={rawValue}\n onChange={handleRawChange}\n readOnly={readOnly}\n placeholder={mode === \"markdown\" ? placeholder : \"Raw markdown...\"}\n autoFocus={autoFocus && mode === \"markdown\"}\n minHeight={minHeight - 40}\n maxHeight={maxHeight ? maxHeight - 40 : undefined}\n />\n </div>\n )}\n </div>\n </div>\n );\n }\n);\n\nEditor.displayName = \"Editor\";\n"],"mappings":";AAgBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,UAAU,mBAAmB;AACtC,SAAS,kBAAkB;AAC3B,SAAS,UAAU,kBAAkB,iBAAiB;AA+HlD,SACE,KADF;AAjDJ,IAAM,kBAAiC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMA,IAAM,mBAAoE;AAAA,EACxE,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,MAAM;AAAA,EACN,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAUA,IAAM,eAAkF;AAAA,EACtF,MAAM,MACJ,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SACvI;AAAA,wBAAC,UAAK,GAAE,yCAAwC;AAAA,IAAE,oBAAC,UAAK,GAAE,0CAAyC;AAAA,KACrG;AAAA,EAEF,QAAQ,MACN,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK,IAAG,KAAI;AAAA,IAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK;AAAA,IAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK;AAAA,KACnH;AAAA,EAEF,eAAe,MACb,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,GAAE,yCAAwC;AAAA,IAAE,oBAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,IAAE,oBAAC,UAAK,GAAE,wCAAuC;AAAA,KAC1I;AAAA,EAEF,SAAS,MACP,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,GAAE,WAAU;AAAA,IAAE,oBAAC,UAAK,GAAE,YAAW;AAAA,IAAE,oBAAC,UAAK,GAAE,YAAW;AAAA,KAC9D;AAAA,EAEF,YAAY,MACV,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK,IAAG,KAAI;AAAA,IAAE,oBAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,IAAE,oBAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,IACjH,oBAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,KAAI,MAAK,gBAAe;AAAA,IAAE,oBAAC,YAAO,IAAG,KAAI,IAAG,MAAK,GAAE,KAAI,MAAK,gBAAe;AAAA,IAAE,oBAAC,YAAO,IAAG,KAAI,IAAG,MAAK,GAAE,KAAI,MAAK,gBAAe;AAAA,KACxJ;AAAA,EAEF,aAAa,MACX,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK,IAAG,KAAI;AAAA,IAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,IAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,IACpH,oBAAC,UAAK,GAAE,KAAI,GAAE,KAAI,UAAS,KAAI,MAAK,gBAAe,QAAO,QAAO,YAAW,cAAa,eAAC;AAAA,IAC1F,oBAAC,UAAK,GAAE,KAAI,GAAE,MAAK,UAAS,KAAI,MAAK,gBAAe,QAAO,QAAO,YAAW,cAAa,eAAC;AAAA,IAC3F,oBAAC,UAAK,GAAE,KAAI,GAAE,MAAK,UAAS,KAAI,MAAK,gBAAe,QAAO,QAAO,YAAW,cAAa,eAAC;AAAA,KAC7F;AAAA,EAEF,UAAU,MACR,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,KAAI,QAAO,KAAI,IAAG,KAAI;AAAA,IAAE,oBAAC,UAAK,GAAE,oBAAmB;AAAA,IAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK,IAAG,KAAI;AAAA,IACjH,oBAAC,UAAK,GAAE,KAAI,GAAE,MAAK,OAAM,KAAI,QAAO,KAAI,IAAG,KAAI;AAAA,IAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,KACzF;AAAA,EAEF,MAAM,MACJ,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,cAAS,QAAO,oBAAmB;AAAA,IAAE,oBAAC,cAAS,QAAO,iBAAgB;AAAA,KACzE;AAAA,EAEF,WAAW,MACT,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAAE,oBAAC,cAAS,QAAO,iBAAgB;AAAA,IAAE,oBAAC,cAAS,QAAO,oBAAmB;AAAA,KAC3H;AAAA,EAEF,YAAY,MACV,oBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,gBACnD,8BAAC,UAAK,GAAE,+FAA8F,GACxG;AAAA,EAEF,MAAM,MACJ,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,UAAK,GAAE,+DAA8D;AAAA,IACtE,oBAAC,UAAK,GAAE,gEAA+D;AAAA,KACzE;AAAA,EAEF,MAAM,MACJ,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,cAAS,QAAO,iBAAgB;AAAA,IAAE,oBAAC,UAAK,GAAE,qCAAoC;AAAA,KACjF;AAAA,EAEF,MAAM,MACJ,qBAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,wBAAC,cAAS,QAAO,oBAAmB;AAAA,IAAE,oBAAC,UAAK,GAAE,uCAAsC;AAAA,KACtF;AAEJ;AAKA,IAAM,iBAAkE;AAAA,EACtE,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,MAAM;AAAA,EACN,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAMA,IAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AACF,MAIE,oBAAC,SAAI,WAAU,4EACZ,gBAAM,IAAI,CAAC,MAAM,MAAM;AACtB,MAAI,SAAS,WAAW;AACtB,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA;AAAA,MADL,WAAW,CAAC;AAAA,IAEnB;AAAA,EAEJ;AAEA,QAAM,OAAO,aAAa,IAAI;AAC9B,QAAM,QAAQ,eAAe,IAAI;AAEjC,SACE;AAAA,IAAC;AAAA;AAAA,MAEC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,aAAa,CAAC,MAAM;AAMlB,UAAE,eAAe;AAAA,MACnB;AAAA,MACA,SAAS,MAAM,SAAS,IAAI;AAAA,MAC5B,OAAO;AAAA,MACP,cAAY;AAAA,MAEZ,8BAAC,QAAK;AAAA;AAAA,IAfD;AAAA,EAgBP;AAEJ,CAAC,GACH;AAWF,IAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AACF,MAGM;AACJ,QAAM,QAAgD;AAAA,IACpD,EAAE,OAAO,WAAW,OAAO,OAAO;AAAA,IAClC,EAAE,OAAO,YAAY,OAAO,KAAK;AAAA,IACjC,EAAE,OAAO,SAAS,OAAO,QAAQ;AAAA,EACnC;AAEA,SACE,oBAAC,SAAI,WAAU,+CACZ,gBAAM,IAAI,CAAC,MACV;AAAA,IAAC;AAAA;AAAA,MAEC,MAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA,SAAS,EAAE,QACP,oCACA;AAAA,MACN,EAAE,KAAK,GAAG;AAAA,MACV,SAAS,MAAM,aAAa,EAAE,KAAK;AAAA,MAElC,YAAE;AAAA;AAAA,IAVE,EAAE;AAAA,EAWT,CACD,GACH;AAEJ;AAqCA,IAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA0B;AACxB,QAAM,aAAa,OAAO,YAAY;AACtC,QAAM,aAAa,OAAuB,IAAI;AAE9C,QAAM,EAAE,IAAI,IAAI;AAAA,IAAU,CAAC,SACzB,eAAe,KAAK,EACjB,OAAO,CAAC,QAAQ;AACf,UAAI,IAAI,SAAS,IAAI;AACrB,UAAI,IAAI,iBAAiB,YAAY;AACrC,UAAI,IAAI,WAAW,EAAE,gBAAgB,CAAC,GAAG,aAAa;AACpD,mBAAW,UAAU;AACrB,YAAI,QAAQ,SAAS;AACnB,kBAAQ,UAAU;AAClB;AAAA,QACF;AACA,iBAAS,QAAQ;AAAA,MACnB,CAAC;AAAA,IACH,CAAC,EACA,IAAI,UAAU,EACd,IAAI,GAAG,EACP,IAAI,OAAO,EACX,IAAI,SAAS,EACb,IAAI,QAAQ;AAAA,EACjB;AAOA,YAAU,MAAM;AACd,eAAW,UAAU;AAAA,MACnB,aAAa,MAAM,WAAW;AAAA,MAE9B,aAAa,CAAC,OAAe;AAC3B,cAAM,SAAS,IAAI;AACnB,YAAI,CAAC,OAAQ;AACb,YAAI;AACF,kBAAQ,UAAU;AAClB,iBAAO,OAAO,WAAW,EAAE,CAAC;AAC5B,qBAAW,UAAU;AAAA,QACvB,SAAS,GAAG;AACV,kBAAQ,UAAU;AAClB,kBAAQ,KAAK,gCAAgC,CAAC;AAAA,QAChD;AAAA,MACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA,YAAY,CAAC,KAAc,YAAsB;AAC/C,cAAM,SAAS,IAAI;AACnB,YAAI,CAAC,OAAQ,QAAO;AACpB,YAAI;AACF,iBAAO,OAAO,CAAC,QAAQ;AACrB,kBAAM,WAAW,IAAI,IAAI,WAAW;AACpC,qBAAS,KAAK,KAAY,OAAO;AAAA,UACnC,CAAC;AACD,iBAAO;AAAA,QACT,SAAS,GAAG;AACV,kBAAQ,KAAK,4BAA4B,CAAC;AAC1C,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,OAAO,MAAM;AACX,cAAM,KAAK,WAAW,SAAS,cAAc,cAAc;AAC3D,YAAI,cAAc,YAAa,IAAG,MAAM;AAAA,MAC1C;AAAA,IACF;AAAA,EACF,GAAG,CAAC,KAAK,SAAS,UAAU,CAAC;AAM7B,YAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,UAAM,SAAS,MAAM;AACnB,YAAM,KAAK,WAAW,SAAS,cAAc,cAAc;AAC3D,UAAI,IAAI;AACN,WAAG,aAAa,oBAAoB,WAAW;AAAA,MACjD;AAAA,IACF;AACA,WAAO;AAEP,UAAM,IAAI,WAAW,QAAQ,GAAG;AAChC,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B,GAAG,CAAC,WAAW,CAAC;AAGhB,YAAU,MAAM;AACd,QAAI,WAAW;AACb,4BAAsB,MAAM;AAC1B,cAAM,KAAK,WAAW,SAAS,cAAc,cAAc;AAC3D,YAAI,cAAc,YAAa,IAAG,MAAM;AAAA,MAC1C,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAMd,QAAM,qBAAqB,YAAY,MAAM;AAC3C,UAAM,KAAK,WAAW,SAAS,cAAc,cAAc;AAC3D,QAAI,cAAc,YAAa,IAAG,MAAM;AAAA,EAC1C,GAAG,CAAC,CAAC;AAEL,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,oBAAkB;AAAA,MAClB,iBAAe,YAAY;AAAA,MAC3B,SAAS,WAAW,SAAY;AAAA,MAEhC,8BAAC,YAAS;AAAA;AAAA,EACZ;AAEJ;AAUA,IAAM,mBAAmB,CAAC;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MASE;AAAA,EAAC;AAAA;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,GAAG;AAAA,IACV;AAAA,IACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,MACL,WAAW,YAAY,GAAG,SAAS,OAAO;AAAA,MAC1C,WAAW,YAAY,GAAG,SAAS,OAAO;AAAA,IAC5C;AAAA,IACA,YAAY;AAAA;AACd;AA8BK,IAAM,SAAS;AAAA,EACpB,CACE;AAAA,IACE,QAAQ;AAAA,IACR;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,EACd,GACA,QACG;AAKH,UAAM,CAAC,cAAc,eAAe,IAAI,SAAqB,YAAY,SAAS;AAClF,UAAM,OAAO,YAAY;AACzB,UAAM,iBAAiB,YAAY;AAOnC,UAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAQ9C,UAAM,CAAC,aAAa,cAAc,IAAI,SAAS,CAAC;AAEhD,UAAM,UAAU,OAAO,KAAK;AAC5B,UAAM,aAAa,OAA+B,IAAI;AAGtD,cAAU,MAAM;AACd,kBAAY,KAAK;AAAA,IACnB,GAAG,CAAC,KAAK,CAAC;AAMV,wBAAoB,KAAK,OAAO;AAAA,MAC9B,aAAa,MAAM;AACjB,YAAI,SAAS,WAAY,QAAO;AAChC,eAAO,WAAW,SAAS,YAAY,KAAK;AAAA,MAC9C;AAAA,MACA,aAAa,CAAC,OAAe;AAC3B,oBAAY,EAAE;AACd,mBAAW,SAAS,YAAY,EAAE;AAAA,MACpC;AAAA,MACA,OAAO,MAAM;AACX,mBAAW,SAAS,MAAM;AAAA,MAC5B;AAAA,IACF,IAAI,CAAC,MAAM,QAAQ,CAAC;AAOpB,UAAM,sBAAsB;AAAA,MAC1B,CAAC,aAAqB;AACpB,oBAAY,QAAQ;AACpB,mBAAW,QAAQ;AAAA,MACrB;AAAA,MACA,CAAC,QAAQ;AAAA,IACX;AAGA,UAAM,kBAAkB;AAAA,MACtB,CAAC,aAAqB;AACpB,oBAAY,QAAQ;AACpB,mBAAW,QAAQ;AAEnB,YAAI,SAAS,SAAS;AACpB,qBAAW,SAAS,YAAY,QAAQ;AAAA,QAC1C;AAAA,MACF;AAAA,MACA,CAAC,UAAU,IAAI;AAAA,IACjB;AAWA,UAAM,sBAAsB;AAAA,MAC1B,CAAC,SAAsB;AACrB,YAAI,SAAS,UAAW;AAGxB,YAAI,SAAS,YAAY;AACvB,gBAAM,SAAS,iBAAiB,IAAI;AACpC,cAAI,QAAQ;AACV,kBAAM,WAAW,WAAW;AAC5B,wBAAY,QAAQ;AACpB,uBAAW,QAAQ;AAAA,UACrB;AACA;AAAA,QACF;AAGA,YAAI,CAAC,WAAW,QAAS;AAMzB,gBAAQ,MAAM;AAAA,UACZ,KAAK;AACH,uBAAW,QAAQ,WAAW,oBAAoB,GAAG;AACrD;AAAA,UACF,KAAK;AACH,uBAAW,QAAQ,WAAW,sBAAsB,GAAG;AACvD;AAAA,UACF,KAAK;AACH,uBAAW,QAAQ,WAAW,2BAA2B,GAAG;AAC5D;AAAA,UACF,KAAK,WAAW;AAOd,kBAAM,KAAK,WAAW,QAAQ,YAAY;AAC1C,kBAAM,QAAQ,GAAG,MAAM,IAAI;AAC3B,kBAAM,mBAAmB,MAAM,MAAM,SAAS,CAAC,GAAG,MAAM,aAAa;AACrE,kBAAM,eAAe,mBAAmB,iBAAiB,CAAC,EAAE,SAAS;AACrE,gBAAI,gBAAgB,KAAK,iBAAiB,GAAG;AAC3C,yBAAW,QAAQ,WAAW,qBAAqB,KAAK,CAAC;AAAA,YAC3D,OAAO;AACL,yBAAW,QAAQ,WAAW,qBAAqB,KAAK,eAAe,CAAC;AAAA,YAC1E;AACA;AAAA,UACF;AAAA,UACA,KAAK;AACH,uBAAW,QAAQ,WAAW,wBAAwB,GAAG;AACzD;AAAA,UACF,KAAK;AACH,uBAAW,QAAQ,WAAW,yBAAyB,GAAG;AAC1D;AAAA,UACF,KAAK,YAAY;AAEf,kBAAM,UAAU,WAAW,QAAQ,YAAY;AAC/C,kBAAM,SAAS,QAAQ,SAAS,IAAI,IAAI,WAAW;AACnD,uBAAW,QAAQ,YAAY,UAAU,MAAM;AAC/C;AAAA,UACF;AAAA,UACA,KAAK;AACH,uBAAW,QAAQ,WAAW,wBAAwB,GAAG;AACzD;AAAA,UACF,KAAK;AACH,uBAAW,QAAQ,WAAW,uBAAuB,GAAG;AACxD;AAAA,UACF,KAAK;AACH,uBAAW,QAAQ,WAAW,wBAAwB,GAAG;AACzD;AAAA,UACF,KAAK,QAAQ;AAOX,uBAAW,QAAQ,WAAW,kBAAkB,KAAK,EAAE,MAAM,WAAW,CAAC;AACzE;AAAA,UACF;AAAA,UACA,KAAK;AACH,uBAAW,QAAQ,WAAW,YAAY,GAAG;AAC7C;AAAA,UACF,KAAK;AACH,uBAAW,QAAQ,WAAW,YAAY,GAAG;AAC7C;AAAA,UACF;AACE;AAAA,QACJ;AAAA,MACF;AAAA,MACA,CAAC,MAAM,UAAU,QAAQ;AAAA,IAC3B;AAGA,UAAM,mBAAmB;AAAA,MACvB,CAAC,YAAwB;AAKvB,YAAI,SAAS,aAAa,SAAS,SAAS;AAC1C,gBAAM,UAAU,WAAW,SAAS,YAAY,KAAK;AACrD,sBAAY,OAAO;AAAA,QACrB;AAEA,wBAAgB,OAAO;AAQvB,YAAI,YAAY,aAAa,YAAY,SAAS;AAChD,yBAAe,CAAC,MAAM,IAAI,CAAC;AAAA,QAC7B;AAAA,MACF;AAAA,MACA,CAAC,MAAM,QAAQ;AAAA,IACjB;AAMA,UAAM,eAAe,gBAAgB,QAAQ,OAAQ,eAAe;AACpE,UAAM,cAAc,gBAAgB,QAAQ,CAAC;AAM7C,UAAM,eAA8B;AAAA,MAClC,WAAW,GAAG,SAAS;AAAA,MACvB,GAAI,YAAY,EAAE,WAAW,GAAG,SAAS,KAAK,IAAI,CAAC;AAAA,IACrD;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA,WACI,qJACA;AAAA,UACJ;AAAA,QACF,EAAE,KAAK,GAAG;AAAA,QACV,OAAO;AAAA,QAGL;AAAA,0BAAe,mBACf,qBAAC,SAAI,WAAU,mEACZ;AAAA,2BACC,oBAAC,iBAAc,OAAO,cAAc,UAAU,qBAAqB;AAAA,YAEpE,kBAAkB,CAAC,YAClB,oBAAC,cAAW,MAAY,cAAc,kBAAkB;AAAA,aAE5D;AAAA,UAIF,qBAAC,SAAI,WAAU,uCAEX;AAAA,sBAAS,aAAa,SAAS,YAC/B,oBAAC,SAAI,WAAW,gCAAgC,SAAS,UAAU,kCAAkC,EAAE,IACrG,8BAAC,oBACC;AAAA,cAAC;AAAA;AAAA,gBACC,cAAc;AAAA,gBACd,UAAU;AAAA,gBACV;AAAA,gBACA;AAAA,gBACA,WAAW,aAAa,SAAS;AAAA,gBACjC;AAAA,gBACA;AAAA;AAAA,YACF,KATqB,WAUvB,GACF;AAAA,aAIA,SAAS,cAAc,SAAS,YAChC,oBAAC,SAAI,WAAU,gCACb;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,UAAU;AAAA,gBACV;AAAA,gBACA,aAAa,SAAS,aAAa,cAAc;AAAA,gBACjD,WAAW,aAAa,SAAS;AAAA,gBACjC,WAAW,YAAY;AAAA,gBACvB,WAAW,YAAY,YAAY,KAAK;AAAA;AAAA,YAC1C,GACF;AAAA,aAEJ;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,11 @@ interface AppLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
28
28
|
* Defaults to an array containing just the current displayMode.
|
|
29
29
|
*/
|
|
30
30
|
availableDisplayModes?: DisplayMode[];
|
|
31
|
+
/**
|
|
32
|
+
* Remove the inner padding and gap. When true, children get full control
|
|
33
|
+
* over their own spacing and can render edge-to-edge without any offset.
|
|
34
|
+
*/
|
|
35
|
+
noPadding?: boolean;
|
|
31
36
|
}
|
|
32
37
|
/**
|
|
33
38
|
* Display-mode-aware root layout component.
|
|
@@ -44,14 +49,15 @@ interface AppLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
44
49
|
* }
|
|
45
50
|
* ```
|
|
46
51
|
*
|
|
47
|
-
* @example
|
|
52
|
+
* @example No padding for full control
|
|
48
53
|
* ```tsx
|
|
49
|
-
* <AppLayout displayMode="
|
|
50
|
-
* <
|
|
54
|
+
* <AppLayout displayMode="pip" noPadding>
|
|
55
|
+
* <StickyHeader />
|
|
56
|
+
* <div className="p-3"><Content /></div>
|
|
51
57
|
* </AppLayout>
|
|
52
58
|
* ```
|
|
53
59
|
*/
|
|
54
|
-
declare const AppLayout: ({ children, displayMode, availableDisplayModes, className, ...rest }: AppLayoutProps) => react_jsx_runtime.JSX.Element;
|
|
60
|
+
declare const AppLayout: ({ children, displayMode, availableDisplayModes, noPadding, className, ...rest }: AppLayoutProps) => react_jsx_runtime.JSX.Element;
|
|
55
61
|
|
|
56
62
|
interface ShowProps {
|
|
57
63
|
/**
|
|
@@ -763,26 +769,26 @@ declare const Tabs: react.ForwardRefExoticComponent<TabsProps & react.RefAttribu
|
|
|
763
769
|
};
|
|
764
770
|
|
|
765
771
|
interface CardProps extends HTMLAttributes<HTMLDivElement> {
|
|
766
|
-
/**
|
|
767
|
-
|
|
772
|
+
/**
|
|
773
|
+
* Visual style.
|
|
774
|
+
* - "default" — primary border
|
|
775
|
+
* - "secondary" — lighter border for less emphasis
|
|
776
|
+
* - "ghost" — borderless
|
|
777
|
+
*/
|
|
778
|
+
variant?: "default" | "secondary" | "ghost";
|
|
768
779
|
/** Internal padding. */
|
|
769
780
|
padding?: "none" | "sm" | "md" | "lg";
|
|
770
781
|
/** Card content. */
|
|
771
782
|
children: ReactNode;
|
|
772
783
|
}
|
|
773
784
|
/**
|
|
774
|
-
* Content container with border
|
|
785
|
+
* Content container with configurable border emphasis.
|
|
775
786
|
*
|
|
776
787
|
* @example
|
|
777
788
|
* ```tsx
|
|
778
|
-
* <Card>
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
* </Card>
|
|
782
|
-
*
|
|
783
|
-
* <Card variant="ghost" padding="none">
|
|
784
|
-
* <Text variant="secondary">Borderless card</Text>
|
|
785
|
-
* </Card>
|
|
789
|
+
* <Card>Default card with primary border</Card>
|
|
790
|
+
* <Card variant="secondary">Subtle card with lighter border</Card>
|
|
791
|
+
* <Card variant="ghost" padding="none">Borderless card</Card>
|
|
786
792
|
* ```
|
|
787
793
|
*/
|
|
788
794
|
declare const Card: ({ variant, padding, children, className, ...rest }: CardProps) => react_jsx_runtime.JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ var AppLayout = ({
|
|
|
24
24
|
children,
|
|
25
25
|
displayMode = "pip",
|
|
26
26
|
availableDisplayModes,
|
|
27
|
+
noPadding = false,
|
|
27
28
|
className = "",
|
|
28
29
|
...rest
|
|
29
30
|
}) => {
|
|
@@ -32,14 +33,18 @@ var AppLayout = ({
|
|
|
32
33
|
() => ({ displayMode, availableDisplayModes: resolvedAvailable }),
|
|
33
34
|
[displayMode, resolvedAvailable]
|
|
34
35
|
);
|
|
35
|
-
const
|
|
36
|
-
"
|
|
37
|
-
paddingClasses[displayMode],
|
|
38
|
-
gapClasses[displayMode],
|
|
36
|
+
const outerClasses = [
|
|
37
|
+
"min-h-0 w-full",
|
|
39
38
|
displayMode === "inline" ? "overflow-hidden" : "overflow-y-auto",
|
|
40
39
|
className
|
|
41
40
|
].filter(Boolean).join(" ");
|
|
42
|
-
|
|
41
|
+
const innerClasses = [
|
|
42
|
+
"flex flex-col",
|
|
43
|
+
noPadding ? "h-full" : "min-h-full",
|
|
44
|
+
!noPadding && paddingClasses[displayMode],
|
|
45
|
+
!noPadding && gapClasses[displayMode]
|
|
46
|
+
].filter(Boolean).join(" ");
|
|
47
|
+
return /* @__PURE__ */ jsx(DisplayModeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { "data-display-mode": displayMode, className: outerClasses, ...rest, children: /* @__PURE__ */ jsx("div", { className: innerClasses, children }) }) });
|
|
43
48
|
};
|
|
44
49
|
|
|
45
50
|
// src/components/Show.tsx
|
|
@@ -1863,6 +1868,11 @@ var paddingClasses2 = {
|
|
|
1863
1868
|
md: "p-3",
|
|
1864
1869
|
lg: "p-4"
|
|
1865
1870
|
};
|
|
1871
|
+
var variantShadow = {
|
|
1872
|
+
default: "inset 0 0 0 1px var(--color-border-primary, rgba(0,0,0,0.08))",
|
|
1873
|
+
secondary: "inset 0 0 0 1px var(--color-border-secondary, rgba(0,0,0,0.04))",
|
|
1874
|
+
ghost: void 0
|
|
1875
|
+
};
|
|
1866
1876
|
var Card = ({
|
|
1867
1877
|
variant = "default",
|
|
1868
1878
|
padding = "md",
|
|
@@ -1872,15 +1882,16 @@ var Card = ({
|
|
|
1872
1882
|
}) => {
|
|
1873
1883
|
const classes = [
|
|
1874
1884
|
"rounded-lg",
|
|
1875
|
-
variant === "
|
|
1885
|
+
variant === "ghost" ? "bg-bg-ghost" : "bg-bg-primary",
|
|
1876
1886
|
paddingClasses2[padding],
|
|
1877
1887
|
className
|
|
1878
1888
|
].filter(Boolean).join(" ");
|
|
1889
|
+
const shadow = variantShadow[variant];
|
|
1879
1890
|
return /* @__PURE__ */ jsx19(
|
|
1880
1891
|
"div",
|
|
1881
1892
|
{
|
|
1882
1893
|
className: classes,
|
|
1883
|
-
style:
|
|
1894
|
+
style: shadow ? { boxShadow: shadow } : void 0,
|
|
1884
1895
|
...rest,
|
|
1885
1896
|
children
|
|
1886
1897
|
}
|