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.
@@ -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 { commonmark } from "@milkdown/kit/preset/commonmark";
16
- import { gfm } from "@milkdown/kit/preset/gfm";
17
- import { history } from "@milkdown/kit/plugin/history";
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 bg-bg-secondary border-b border-bdr-secondary rounded-t-md overflow-x-auto", children: items.map((item, i) => {
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
- skipRef.current = true;
220
- editor.action(replaceAll(md));
221
- contentRef.current = md;
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 syntax2 = TOOLBAR_MARKDOWN[item];
339
- if (syntax2) {
340
- const newValue = rawValue + syntax2;
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
- const syntax = TOOLBAR_MARKDOWN[item];
347
- if (syntax && actionsRef.current) {
348
- const current = actionsRef.current.getMarkdown();
349
- const newValue = current + syntax;
350
- actionsRef.current.setMarkdown(newValue);
351
- setRawValue(newValue);
352
- onChange?.(newValue);
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 rounded-md overflow-hidden",
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 rounded-t-md", children: [
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: value,
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
  {
@@ -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":"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","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 Testing with explicit mode
52
+ * @example No padding for full control
48
53
  * ```tsx
49
- * <AppLayout displayMode="inline">
50
- * <CompactView />
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
- /** Visual style. "default" has border+shadow, "ghost" is borderless. */
767
- variant?: "default" | "ghost";
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 and shadow.
785
+ * Content container with configurable border emphasis.
775
786
  *
776
787
  * @example
777
788
  * ```tsx
778
- * <Card>
779
- * <Heading size="sm">Settings</Heading>
780
- * <Input label="Name" />
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 classes = [
36
- "flex flex-col min-h-0 w-full",
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
- return /* @__PURE__ */ jsx(DisplayModeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { "data-display-mode": displayMode, className: classes, ...rest, children }) });
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 === "default" ? "bg-bg-primary" : "bg-bg-ghost",
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: variant === "default" ? { boxShadow: "inset 0 0 0 1px var(--color-border-primary, rgba(0,0,0,0.08)), 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)" } : void 0,
1894
+ style: shadow ? { boxShadow: shadow } : void 0,
1884
1895
  ...rest,
1885
1896
  children
1886
1897
  }