open-mcp-app-ui 0.0.3 → 0.0.4

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.
@@ -1,22 +1,81 @@
1
+ import react__default from 'react';
2
+
1
3
  /**
2
- * open-mcp-app-ui/editor
4
+ * Editor — Markdown + rich text editor.
5
+ *
6
+ * Built on Milkdown (ProseMirror + Remark) for markdown-first editing
7
+ * with perfect round-trip fidelity. Supports WYSIWYG, raw markdown,
8
+ * and split (side-by-side) editing modes.
3
9
  *
4
- * Markdown + rich text editor built on Milkdown (ProseMirror + Remark).
5
- * This is an optional import apps that don't use it pay zero bundle cost.
10
+ * Styled exclusively with MCP Apps spec CSS variables. All typography,
11
+ * colors, borders, and spacing use the host's theme tokens.
6
12
  *
7
- * Phase 2: Placeholder export. Implementation coming soon.
13
+ * Imported separately from the core library to keep apps that don't
14
+ * need it from paying any bundle cost:
8
15
  *
9
- * Planned usage:
10
16
  * import { Editor } from "open-mcp-app-ui/editor";
17
+ */
18
+
19
+ /** Available toolbar action items. */
20
+ type ToolbarItem = "bold" | "italic" | "strikethrough" | "heading" | "bulletList" | "orderedList" | "taskList" | "code" | "codeBlock" | "blockquote" | "link" | "divider" | "undo" | "redo";
21
+ /** Editing mode. */
22
+ type EditorMode = "wysiwyg" | "markdown" | "split";
23
+ interface EditorProps {
24
+ /** Markdown string (source of truth). */
25
+ value?: string;
26
+ /** Called when content changes. Receives the updated markdown string. */
27
+ onChange?: (markdown: string) => void;
28
+ /**
29
+ * Editing mode:
30
+ * - "wysiwyg" — Rich text rendering with formatting (default)
31
+ * - "markdown" — Raw markdown text editing
32
+ * - "split" — Side-by-side WYSIWYG and markdown
33
+ */
34
+ mode?: EditorMode;
35
+ /** Placeholder text when the editor is empty. */
36
+ placeholder?: string;
37
+ /**
38
+ * Toolbar buttons to show, or `false` to hide the toolbar entirely.
39
+ * Defaults to a sensible set of common formatting actions.
40
+ */
41
+ toolbar?: ToolbarItem[] | false;
42
+ /** View-only mode. Renders markdown as styled content without editing. */
43
+ readOnly?: boolean;
44
+ /** Minimum editor height in pixels. */
45
+ minHeight?: number;
46
+ /** Maximum editor height in pixels before scrolling. */
47
+ maxHeight?: number;
48
+ /** Focus the editor on mount. */
49
+ autoFocus?: boolean;
50
+ /** Additional CSS classes on the outermost wrapper. */
51
+ className?: string;
52
+ }
53
+ interface EditorRef {
54
+ /** Get the current markdown content. */
55
+ getMarkdown: () => string;
56
+ /** Imperatively set editor content without triggering onChange. */
57
+ setMarkdown: (markdown: string) => void;
58
+ /** Focus the editor. */
59
+ focus: () => void;
60
+ }
61
+ /**
62
+ * Markdown + rich text editor with toolbar and mode switching.
63
+ *
64
+ * Built on Milkdown (ProseMirror + Remark) for markdown-first editing.
65
+ * Supports WYSIWYG, raw markdown, and split editing modes. Styled with
66
+ * MCP Apps spec CSS variables for automatic host theming.
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * import { Editor } from "open-mcp-app-ui/editor";
11
71
  *
12
- * <Editor
13
- * value={markdownString}
14
- * onChange={setMarkdownString}
15
- * mode="wysiwyg"
16
- * toolbar={["bold", "italic", "heading", "list", "code", "link"]}
17
- * />
72
+ * <Editor
73
+ * value={markdown}
74
+ * onChange={setMarkdown}
75
+ * placeholder="Start writing..."
76
+ * />
77
+ * ```
18
78
  */
19
- declare const EDITOR_VERSION = "0.0.1";
20
- declare const EDITOR_STATUS: "planned";
79
+ declare const Editor: react__default.ForwardRefExoticComponent<EditorProps & react__default.RefAttributes<EditorRef>>;
21
80
 
22
- export { EDITOR_STATUS, EDITOR_VERSION };
81
+ export { Editor, type EditorMode, type EditorProps, type EditorRef, type ToolbarItem };
@@ -1,8 +1,427 @@
1
- // src/editor/index.ts
2
- var EDITOR_VERSION = "0.0.1";
3
- var EDITOR_STATUS = "planned";
1
+ // src/editor/Editor.tsx
2
+ import {
3
+ useState,
4
+ useRef,
5
+ useCallback,
6
+ useEffect,
7
+ forwardRef,
8
+ useImperativeHandle
9
+ } from "react";
10
+ import {
11
+ Editor as MilkdownEditor,
12
+ rootCtx,
13
+ defaultValueCtx
14
+ } 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";
18
+ import { clipboard } from "@milkdown/kit/plugin/clipboard";
19
+ import { listener, listenerCtx } from "@milkdown/plugin-listener";
20
+ import { replaceAll } from "@milkdown/utils";
21
+ import { Milkdown, MilkdownProvider, useEditor } from "@milkdown/react";
22
+ import { jsx, jsxs } from "react/jsx-runtime";
23
+ var DEFAULT_TOOLBAR = [
24
+ "bold",
25
+ "italic",
26
+ "strikethrough",
27
+ "divider",
28
+ "heading",
29
+ "bulletList",
30
+ "orderedList",
31
+ "taskList",
32
+ "divider",
33
+ "code",
34
+ "codeBlock",
35
+ "blockquote",
36
+ "link",
37
+ "divider",
38
+ "undo",
39
+ "redo"
40
+ ];
41
+ var ToolbarIcons = {
42
+ 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
+ /* @__PURE__ */ jsx("path", { d: "M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" }),
44
+ /* @__PURE__ */ jsx("path", { d: "M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" })
45
+ ] }),
46
+ italic: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
47
+ /* @__PURE__ */ jsx("line", { x1: "19", y1: "4", x2: "10", y2: "4" }),
48
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "20", x2: "5", y2: "20" }),
49
+ /* @__PURE__ */ jsx("line", { x1: "15", y1: "4", x2: "9", y2: "20" })
50
+ ] }),
51
+ strikethrough: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
52
+ /* @__PURE__ */ jsx("path", { d: "M16 4H9a3 3 0 0 0-3 3c0 2 1.5 3 3 3h6" }),
53
+ /* @__PURE__ */ jsx("line", { x1: "4", y1: "12", x2: "20", y2: "12" }),
54
+ /* @__PURE__ */ jsx("path", { d: "M15 12c1.5 0 3 1 3 3a3 3 0 0 1-3 3H8" })
55
+ ] }),
56
+ heading: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
57
+ /* @__PURE__ */ jsx("path", { d: "M6 4v16" }),
58
+ /* @__PURE__ */ jsx("path", { d: "M18 4v16" }),
59
+ /* @__PURE__ */ jsx("path", { d: "M6 12h12" })
60
+ ] }),
61
+ bulletList: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
62
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "6", x2: "20", y2: "6" }),
63
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "12", x2: "20", y2: "12" }),
64
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "18", x2: "20", y2: "18" }),
65
+ /* @__PURE__ */ jsx("circle", { cx: "4", cy: "6", r: "1", fill: "currentColor" }),
66
+ /* @__PURE__ */ jsx("circle", { cx: "4", cy: "12", r: "1", fill: "currentColor" }),
67
+ /* @__PURE__ */ jsx("circle", { cx: "4", cy: "18", r: "1", fill: "currentColor" })
68
+ ] }),
69
+ orderedList: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
70
+ /* @__PURE__ */ jsx("line", { x1: "10", y1: "6", x2: "21", y2: "6" }),
71
+ /* @__PURE__ */ jsx("line", { x1: "10", y1: "12", x2: "21", y2: "12" }),
72
+ /* @__PURE__ */ jsx("line", { x1: "10", y1: "18", x2: "21", y2: "18" }),
73
+ /* @__PURE__ */ jsx("text", { x: "3", y: "7", fontSize: "7", fill: "currentColor", stroke: "none", fontFamily: "sans-serif", children: "1" }),
74
+ /* @__PURE__ */ jsx("text", { x: "3", y: "13", fontSize: "7", fill: "currentColor", stroke: "none", fontFamily: "sans-serif", children: "2" }),
75
+ /* @__PURE__ */ jsx("text", { x: "3", y: "19", fontSize: "7", fill: "currentColor", stroke: "none", fontFamily: "sans-serif", children: "3" })
76
+ ] }),
77
+ taskList: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
78
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "6", height: "6", rx: "1" }),
79
+ /* @__PURE__ */ jsx("path", { d: "M5 8l1.5 1.5L9 7" }),
80
+ /* @__PURE__ */ jsx("line", { x1: "13", y1: "8", x2: "21", y2: "8" }),
81
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "14", width: "6", height: "6", rx: "1" }),
82
+ /* @__PURE__ */ jsx("line", { x1: "13", y1: "17", x2: "21", y2: "17" })
83
+ ] }),
84
+ code: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
85
+ /* @__PURE__ */ jsx("polyline", { points: "16 18 22 12 16 6" }),
86
+ /* @__PURE__ */ jsx("polyline", { points: "8 6 2 12 8 18" })
87
+ ] }),
88
+ codeBlock: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
89
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
90
+ /* @__PURE__ */ jsx("polyline", { points: "9 8 5 12 9 16" }),
91
+ /* @__PURE__ */ jsx("polyline", { points: "15 8 19 12 15 16" })
92
+ ] }),
93
+ blockquote: () => /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("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" }) }),
94
+ link: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
95
+ /* @__PURE__ */ jsx("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
96
+ /* @__PURE__ */ jsx("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
97
+ ] }),
98
+ undo: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
99
+ /* @__PURE__ */ jsx("polyline", { points: "1 4 1 10 7 10" }),
100
+ /* @__PURE__ */ jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })
101
+ ] }),
102
+ redo: () => /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
103
+ /* @__PURE__ */ jsx("polyline", { points: "23 4 23 10 17 10" }),
104
+ /* @__PURE__ */ jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
105
+ ] })
106
+ };
107
+ var TOOLBAR_LABELS = {
108
+ bold: "Bold",
109
+ italic: "Italic",
110
+ strikethrough: "Strikethrough",
111
+ heading: "Heading",
112
+ bulletList: "Bullet List",
113
+ orderedList: "Ordered List",
114
+ taskList: "Task List",
115
+ code: "Inline Code",
116
+ codeBlock: "Code Block",
117
+ blockquote: "Blockquote",
118
+ link: "Link",
119
+ undo: "Undo",
120
+ redo: "Redo"
121
+ };
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
+ var EditorToolbar = ({
138
+ items,
139
+ 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) => {
141
+ if (item === "divider") {
142
+ return /* @__PURE__ */ jsx(
143
+ "div",
144
+ {
145
+ className: "w-px h-4 bg-bdr-secondary mx-1 shrink-0"
146
+ },
147
+ `divider-${i}`
148
+ );
149
+ }
150
+ const Icon = ToolbarIcons[item];
151
+ const label = TOOLBAR_LABELS[item];
152
+ return /* @__PURE__ */ jsx(
153
+ "button",
154
+ {
155
+ type: "button",
156
+ 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",
157
+ onClick: () => onAction(item),
158
+ title: label,
159
+ "aria-label": label,
160
+ children: /* @__PURE__ */ jsx(Icon, {})
161
+ },
162
+ item
163
+ );
164
+ }) });
165
+ var ModeToggle = ({
166
+ mode,
167
+ onModeChange
168
+ }) => {
169
+ const modes = [
170
+ { value: "wysiwyg", label: "Rich" },
171
+ { value: "markdown", label: "MD" },
172
+ { value: "split", label: "Split" }
173
+ ];
174
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5 ml-auto pl-2", children: modes.map((m) => /* @__PURE__ */ jsx(
175
+ "button",
176
+ {
177
+ type: "button",
178
+ className: [
179
+ "px-2 py-0.5 rounded-sm text-xs font-medium transition-colors cursor-pointer",
180
+ mode === m.value ? "bg-bg-tertiary text-txt-primary" : "text-txt-tertiary hover:text-txt-secondary hover:bg-bg-tertiary"
181
+ ].join(" "),
182
+ onClick: () => onModeChange(m.value),
183
+ children: m.label
184
+ },
185
+ m.value
186
+ )) });
187
+ };
188
+ var MilkdownInner = ({
189
+ defaultValue,
190
+ onChange,
191
+ readOnly,
192
+ placeholder,
193
+ autoFocus,
194
+ skipRef,
195
+ actionsRef
196
+ }) => {
197
+ const contentRef = useRef(defaultValue);
198
+ const wrapperRef = useRef(null);
199
+ const { get } = useEditor(
200
+ (root) => MilkdownEditor.make().config((ctx) => {
201
+ ctx.set(rootCtx, root);
202
+ ctx.set(defaultValueCtx, defaultValue);
203
+ ctx.get(listenerCtx).markdownUpdated((_, markdown) => {
204
+ contentRef.current = markdown;
205
+ if (skipRef.current) {
206
+ skipRef.current = false;
207
+ return;
208
+ }
209
+ onChange(markdown);
210
+ });
211
+ }).use(commonmark).use(gfm).use(history).use(clipboard).use(listener)
212
+ );
213
+ useEffect(() => {
214
+ actionsRef.current = {
215
+ getMarkdown: () => contentRef.current,
216
+ setMarkdown: (md) => {
217
+ const editor = get();
218
+ if (!editor) return;
219
+ skipRef.current = true;
220
+ editor.action(replaceAll(md));
221
+ contentRef.current = md;
222
+ },
223
+ focus: () => {
224
+ const el = wrapperRef.current?.querySelector(".ProseMirror");
225
+ if (el instanceof HTMLElement) el.focus();
226
+ }
227
+ };
228
+ }, [get, skipRef, actionsRef]);
229
+ useEffect(() => {
230
+ if (autoFocus) {
231
+ requestAnimationFrame(() => {
232
+ const el = wrapperRef.current?.querySelector(".ProseMirror");
233
+ if (el instanceof HTMLElement) el.focus();
234
+ });
235
+ }
236
+ }, [autoFocus]);
237
+ const handleWrapperClick = useCallback(() => {
238
+ const el = wrapperRef.current?.querySelector(".ProseMirror");
239
+ if (el instanceof HTMLElement) el.focus();
240
+ }, []);
241
+ return /* @__PURE__ */ jsx(
242
+ "div",
243
+ {
244
+ ref: wrapperRef,
245
+ className: "omu-editor-content flex-1 overflow-y-auto px-4 py-3 flex flex-col min-h-0 cursor-text",
246
+ "data-placeholder": placeholder,
247
+ "data-readonly": readOnly || void 0,
248
+ onClick: readOnly ? void 0 : handleWrapperClick,
249
+ children: /* @__PURE__ */ jsx(Milkdown, {})
250
+ }
251
+ );
252
+ };
253
+ var MarkdownTextarea = ({
254
+ value,
255
+ onChange,
256
+ readOnly,
257
+ placeholder,
258
+ autoFocus,
259
+ minHeight,
260
+ maxHeight
261
+ }) => /* @__PURE__ */ jsx(
262
+ "textarea",
263
+ {
264
+ className: [
265
+ "omu-editor-textarea flex-1 w-full px-4 py-3 resize-none",
266
+ "bg-bg-primary text-txt-primary font-mono text-sm",
267
+ "outline-none border-none",
268
+ "placeholder:text-txt-tertiary"
269
+ ].join(" "),
270
+ value,
271
+ onChange: (e) => onChange(e.target.value),
272
+ readOnly,
273
+ placeholder,
274
+ autoFocus,
275
+ style: {
276
+ minHeight: minHeight ? `${minHeight}px` : void 0,
277
+ maxHeight: maxHeight ? `${maxHeight}px` : void 0
278
+ },
279
+ spellCheck: false
280
+ }
281
+ );
282
+ var Editor = forwardRef(
283
+ ({
284
+ value = "",
285
+ onChange,
286
+ mode: modeProp,
287
+ placeholder,
288
+ toolbar: toolbarProp,
289
+ readOnly = false,
290
+ minHeight = 120,
291
+ maxHeight,
292
+ autoFocus = false,
293
+ className = ""
294
+ }, ref) => {
295
+ const [internalMode, setInternalMode] = useState(modeProp ?? "wysiwyg");
296
+ const mode = modeProp ?? internalMode;
297
+ const showModeToggle = modeProp == null;
298
+ const [rawValue, setRawValue] = useState(value);
299
+ const skipRef = useRef(false);
300
+ const actionsRef = useRef(null);
301
+ useEffect(() => {
302
+ setRawValue(value);
303
+ }, [value]);
304
+ useImperativeHandle(ref, () => ({
305
+ getMarkdown: () => {
306
+ if (mode === "markdown") return rawValue;
307
+ return actionsRef.current?.getMarkdown() ?? rawValue;
308
+ },
309
+ setMarkdown: (md) => {
310
+ setRawValue(md);
311
+ actionsRef.current?.setMarkdown(md);
312
+ },
313
+ focus: () => {
314
+ actionsRef.current?.focus();
315
+ }
316
+ }), [mode, rawValue]);
317
+ const handleWysiwygChange = useCallback(
318
+ (markdown) => {
319
+ setRawValue(markdown);
320
+ onChange?.(markdown);
321
+ },
322
+ [onChange]
323
+ );
324
+ const handleRawChange = useCallback(
325
+ (markdown) => {
326
+ setRawValue(markdown);
327
+ onChange?.(markdown);
328
+ if (mode === "split") {
329
+ actionsRef.current?.setMarkdown(markdown);
330
+ }
331
+ },
332
+ [onChange, mode]
333
+ );
334
+ const handleToolbarAction = useCallback(
335
+ (item) => {
336
+ if (item === "divider") return;
337
+ if (mode === "markdown") {
338
+ const syntax2 = TOOLBAR_MARKDOWN[item];
339
+ if (syntax2) {
340
+ const newValue = rawValue + syntax2;
341
+ setRawValue(newValue);
342
+ onChange?.(newValue);
343
+ }
344
+ return;
345
+ }
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);
353
+ }
354
+ },
355
+ [mode, rawValue, onChange]
356
+ );
357
+ const handleModeChange = useCallback(
358
+ (newMode) => {
359
+ if (mode === "wysiwyg" || mode === "split") {
360
+ const current = actionsRef.current?.getMarkdown() ?? rawValue;
361
+ setRawValue(current);
362
+ }
363
+ if ((newMode === "wysiwyg" || newMode === "split") && mode === "markdown") {
364
+ actionsRef.current?.setMarkdown(rawValue);
365
+ }
366
+ setInternalMode(newMode);
367
+ },
368
+ [mode, rawValue]
369
+ );
370
+ const toolbarItems = toolbarProp === false ? null : toolbarProp ?? DEFAULT_TOOLBAR;
371
+ const showToolbar = toolbarItems != null && !readOnly;
372
+ const wrapperStyle = {
373
+ minHeight: `${minHeight}px`,
374
+ ...maxHeight ? { maxHeight: `${maxHeight}px` } : {}
375
+ };
376
+ return /* @__PURE__ */ jsxs(
377
+ "div",
378
+ {
379
+ className: [
380
+ "omu-editor flex flex-col rounded-md overflow-hidden",
381
+ "border border-bdr-primary",
382
+ "bg-bg-primary text-txt-primary",
383
+ "focus-within:outline focus-within:outline-2 focus-within:outline-offset-0 focus-within:outline-ring-primary",
384
+ className
385
+ ].join(" "),
386
+ style: wrapperStyle,
387
+ children: [
388
+ (showToolbar || showModeToggle) && /* @__PURE__ */ jsxs("div", { className: "flex items-center bg-bg-secondary border-b border-bdr-secondary rounded-t-md", children: [
389
+ showToolbar && /* @__PURE__ */ jsx(EditorToolbar, { items: toolbarItems, onAction: handleToolbarAction }),
390
+ showModeToggle && !readOnly && /* @__PURE__ */ jsx(ModeToggle, { mode, onModeChange: handleModeChange })
391
+ ] }),
392
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0 overflow-hidden", children: [
393
+ (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
+ MilkdownInner,
395
+ {
396
+ defaultValue: value,
397
+ onChange: handleWysiwygChange,
398
+ readOnly,
399
+ placeholder,
400
+ autoFocus: autoFocus && mode === "wysiwyg",
401
+ skipRef,
402
+ actionsRef
403
+ }
404
+ ) }) }),
405
+ (mode === "markdown" || mode === "split") && /* @__PURE__ */ jsx("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx(
406
+ MarkdownTextarea,
407
+ {
408
+ value: rawValue,
409
+ onChange: handleRawChange,
410
+ readOnly,
411
+ placeholder: mode === "markdown" ? placeholder : "Raw markdown...",
412
+ autoFocus: autoFocus && mode === "markdown",
413
+ minHeight: minHeight - 40,
414
+ maxHeight: maxHeight ? maxHeight - 40 : void 0
415
+ }
416
+ ) })
417
+ ] })
418
+ ]
419
+ }
420
+ );
421
+ }
422
+ );
423
+ Editor.displayName = "Editor";
4
424
  export {
5
- EDITOR_STATUS,
6
- EDITOR_VERSION
425
+ Editor
7
426
  };
8
427
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/editor/index.ts"],"sourcesContent":["/**\n * open-mcp-app-ui/editor\n *\n * Markdown + rich text editor built on Milkdown (ProseMirror + Remark).\n * This is an optional import — apps that don't use it pay zero bundle cost.\n *\n * Phase 2: Placeholder export. Implementation coming soon.\n *\n * Planned usage:\n * import { Editor } from \"open-mcp-app-ui/editor\";\n *\n * <Editor\n * value={markdownString}\n * onChange={setMarkdownString}\n * mode=\"wysiwyg\"\n * toolbar={[\"bold\", \"italic\", \"heading\", \"list\", \"code\", \"link\"]}\n * />\n */\n\nexport const EDITOR_VERSION = \"0.0.1\";\nexport const EDITOR_STATUS = \"planned\" as const;\n"],"mappings":";AAmBO,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;","names":[]}
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"]}
package/dist/styles.css CHANGED
@@ -1,2 +1,2 @@
1
1
  /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--spacing:.25rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg-primary:var(--color-background-primary);--color-bg-secondary:var(--color-background-secondary);--color-bg-tertiary:var(--color-background-tertiary);--color-bg-inverse:var(--color-background-inverse);--color-bg-ghost:var(--color-background-ghost);--color-bg-info:var(--color-background-info);--color-bg-danger:var(--color-background-danger);--color-bg-success:var(--color-background-success);--color-bg-warning:var(--color-background-warning);--color-txt-primary:var(--color-text-primary);--color-txt-secondary:var(--color-text-secondary);--color-txt-tertiary:var(--color-text-tertiary);--color-txt-inverse:var(--color-text-inverse);--color-txt-info:var(--color-text-info);--color-txt-danger:var(--color-text-danger);--color-txt-success:var(--color-text-success);--color-txt-warning:var(--color-text-warning);--color-bdr-primary:var(--color-border-primary);--color-bdr-secondary:var(--color-border-secondary);--color-bdr-tertiary:var(--color-border-tertiary);--color-bdr-danger:var(--color-border-danger);--color-bdr-success:var(--color-border-success);--color-bdr-warning:var(--color-border-warning);--text-xs:var(--font-text-xs-size);--text-xs--line-height:var(--font-text-xs-line-height);--text-sm:var(--font-text-sm-size);--text-sm--line-height:var(--font-text-sm-line-height);--text-base:var(--font-text-md-size);--text-base--line-height:var(--font-text-md-line-height);--text-lg:var(--font-text-lg-size);--text-lg--line-height:var(--font-text-lg-line-height);--radius-sm:var(--border-radius-sm);--radius-md:var(--border-radius-md);--radius-lg:var(--border-radius-lg);--radius-xl:var(--border-radius-xl);--radius-full:var(--border-radius-full);--color-ring-primary:var(--color-ring-primary);--color-ring-secondary:var(--color-ring-secondary);--color-ring-inverse:var(--color-ring-inverse);--color-ring-info:var(--color-ring-info);--color-ring-danger:var(--color-ring-danger);--color-ring-success:var(--color-ring-success);--color-ring-warning:var(--color-ring-warning)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:before,:after{box-sizing:border-box}*{margin:0}html,body{height:100%}html{font-synthesis-weight:none;-webkit-font-smoothing:antialiased}body{font-family:var(--font-sans);font-size:var(--font-text-md-size);line-height:var(--font-text-md-line-height);color:var(--color-text-primary);background-color:var(--color-background-primary)}input,textarea,select{appearance:none}button,input,select,textarea{touch-action:manipulation}img,svg{flex-shrink:0;max-width:100%;display:block}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.relative{position:relative}.top-0\.5{top:calc(var(--spacing)*.5)}.right-0{right:calc(var(--spacing)*0)}.bottom-0\.5{bottom:calc(var(--spacing)*.5)}.left-0{left:calc(var(--spacing)*0)}.left-1\/2{left:50%}.z-50{z-index:50}.z-\[1\]{z-index:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.my-1{margin-block:calc(var(--spacing)*1)}.my-2{margin-block:calc(var(--spacing)*2)}.my-3{margin-block:calc(var(--spacing)*3)}.my-4{margin-block:calc(var(--spacing)*4)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.-mb-px{margin-bottom:-1px}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-11{height:calc(var(--spacing)*11)}.h-px{height:1px}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-\[32px\]{min-height:32px}.min-h-\[38px\]{min-height:38px}.min-h-\[44px\]{min-height:44px}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-7{width:calc(var(--spacing)*7)}.w-9{width:calc(var(--spacing)*9)}.w-full{width:100%}.max-w-\[150px\]{max-width:150px}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-\[60px\]{min-width:60px}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1/2*100%)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0\.5{--tw-translate-x:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-4\.5{--tw-translate-x:calc(var(--spacing)*4.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.resize{resize:both}.resize-none{resize:none}.resize-x{resize:horizontal}.resize-y{resize:vertical}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-2\.5{gap:calc(var(--spacing)*2.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.\!rounded-full{border-radius:var(--radius-full)!important}.rounded-full{border-radius:var(--radius-full)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-none{--tw-border-style:none;border-style:none}.border-bdr-danger{border-color:var(--color-bdr-danger)}.border-bdr-primary{border-color:var(--color-bdr-primary)}.border-bdr-secondary{border-color:var(--color-bdr-secondary)}.border-bdr-success{border-color:var(--color-bdr-success)}.border-bdr-warning{border-color:var(--color-bdr-warning)}.border-transparent{border-color:#0000}.bg-bdr-tertiary{background-color:var(--color-bdr-tertiary)}.bg-bg-danger{background-color:var(--color-bg-danger)}.bg-bg-ghost{background-color:var(--color-bg-ghost)}.bg-bg-info{background-color:var(--color-bg-info)}.bg-bg-inverse{background-color:var(--color-bg-inverse)}.bg-bg-primary{background-color:var(--color-bg-primary)}.bg-bg-secondary{background-color:var(--color-bg-secondary)}.bg-bg-success{background-color:var(--color-bg-success)}.bg-bg-tertiary{background-color:var(--color-bg-tertiary)}.bg-bg-warning{background-color:var(--color-bg-warning)}.bg-transparent{background-color:#0000}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-3\.5{padding-inline:calc(var(--spacing)*3.5)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.pt-2{padding-top:calc(var(--spacing)*2)}.pb-1{padding-bottom:calc(var(--spacing)*1)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-2\.5{padding-bottom:calc(var(--spacing)*2.5)}.text-left{text-align:left}.heading-2xl{font-size:var(--font-heading-2xl-size);font-weight:var(--font-weight-semibold);line-height:var(--font-heading-2xl-line-height)}.heading-3xl{font-size:var(--font-heading-3xl-size);font-weight:var(--font-weight-semibold);line-height:var(--font-heading-3xl-line-height)}.heading-lg{font-size:var(--font-heading-lg-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-lg-line-height)}.heading-md{font-size:var(--font-heading-md-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-md-line-height)}.heading-sm{font-size:var(--font-heading-sm-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-sm-line-height)}.heading-xl{font-size:var(--font-heading-xl-size);font-weight:var(--font-weight-semibold);line-height:var(--font-heading-xl-line-height)}.heading-xs{font-size:var(--font-heading-xs-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-xs-line-height)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-txt-danger{color:var(--color-txt-danger)}.text-txt-info{color:var(--color-txt-info)}.text-txt-inverse{color:var(--color-txt-inverse)}.text-txt-primary{color:var(--color-txt-primary)}.text-txt-secondary{color:var(--color-txt-secondary)}.text-txt-success{color:var(--color-txt-success)}.text-txt-tertiary{color:var(--color-txt-tertiary)}.text-txt-warning{color:var(--color-txt-warning)}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-75{--tw-duration:75ms;transition-duration:75ms}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.peer-focus-visible\:\[outline\:2px_solid_var\(--color-ring-primary\)\]:is(:where(.peer):focus-visible~*){outline:2px solid var(--color-ring-primary)}.placeholder\:text-txt-tertiary::placeholder{color:var(--color-txt-tertiary)}@media (hover:hover){.hover\:bg-bg-secondary:hover{background-color:var(--color-bg-secondary)}.hover\:bg-bg-tertiary:hover{background-color:var(--color-bg-tertiary)}.hover\:text-txt-primary:hover{color:var(--color-txt-primary)}.hover\:text-txt-secondary:hover{color:var(--color-txt-secondary)}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-ring-danger:focus-visible{outline-color:var(--color-ring-danger)}.focus-visible\:outline-ring-primary:focus-visible{outline-color:var(--color-ring-primary)}.focus-visible\:outline-ring-secondary:focus-visible{outline-color:var(--color-ring-secondary)}.active\:bg-bg-tertiary:active{background-color:var(--color-bg-tertiary)}}:root{--color-background-primary:light-dark(#fff,#1a1a1a);--color-background-secondary:light-dark(#f5f5f5,#2d2d2d);--color-background-tertiary:light-dark(#e5e5e5,#404040);--color-background-inverse:light-dark(#1a1a1a,#f5f5f5);--color-background-ghost:light-dark(#0000,#fff0);--color-background-info:light-dark(#eff6ff,#1e3a5f);--color-background-danger:light-dark(#fef2f2,#5c1d1d);--color-background-success:light-dark(#f0fdf4,#14532d);--color-background-warning:light-dark(#fefce8,#713f12);--color-background-disabled:light-dark(#f5f5f5,#2d2d2d);--color-text-primary:light-dark(#171717,#f5f5f5);--color-text-secondary:light-dark(#525252,#a3a3a3);--color-text-tertiary:light-dark(#a3a3a3,#737373);--color-text-inverse:light-dark(#f5f5f5,#171717);--color-text-ghost:light-dark(#525252,#a3a3a3);--color-text-info:light-dark(#1d4ed8,#60a5fa);--color-text-danger:light-dark(#b91c1c,#f87171);--color-text-success:light-dark(#15803d,#4ade80);--color-text-warning:light-dark(#a16207,#fbbf24);--color-text-disabled:light-dark(#a3a3a3,#525252);--color-border-primary:light-dark(#e5e5e5,#404040);--color-border-secondary:light-dark(#d4d4d4,#525252);--color-border-tertiary:light-dark(#f5f5f5,#2d2d2d);--color-border-inverse:light-dark(#ffffff4d,#0000004d);--color-border-ghost:light-dark(#0000,#fff0);--color-border-info:light-dark(#93c5fd,#1e40af);--color-border-danger:light-dark(#fca5a5,#991b1b);--color-border-success:light-dark(#86efac,#166534);--color-border-warning:light-dark(#fde047,#854d0e);--color-border-disabled:light-dark(#e5e5e5,#404040);--color-ring-primary:light-dark(#666,#cdcdcd);--color-ring-secondary:light-dark(#a3a3a3,#737373);--color-ring-inverse:light-dark(#fff,#171717);--color-ring-info:light-dark(#2563eb,#3b82f6);--color-ring-danger:light-dark(#dc2626,#ef4444);--color-ring-success:light-dark(#16a34a,#22c55e);--color-ring-warning:light-dark(#ca8a04,#eab308);--font-sans:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;--font-mono:ui-monospace,"SF Mono",Monaco,"Cascadia Code",monospace;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-text-xs-size:.75rem;--font-text-sm-size:.875rem;--font-text-md-size:1rem;--font-text-lg-size:1.125rem;--font-heading-xs-size:.75rem;--font-heading-sm-size:.875rem;--font-heading-md-size:1rem;--font-heading-lg-size:1.25rem;--font-heading-xl-size:1.5rem;--font-heading-2xl-size:1.875rem;--font-heading-3xl-size:2.25rem;--font-text-xs-line-height:1.4;--font-text-sm-line-height:1.4;--font-text-md-line-height:1.5;--font-text-lg-line-height:1.5;--font-heading-xs-line-height:1.4;--font-heading-sm-line-height:1.4;--font-heading-md-line-height:1.4;--font-heading-lg-line-height:1.3;--font-heading-xl-line-height:1.25;--font-heading-2xl-line-height:1.2;--font-heading-3xl-line-height:1.1;--border-radius-xs:2px;--border-radius-sm:4px;--border-radius-md:6px;--border-radius-lg:8px;--border-radius-xl:12px;--border-radius-full:9999px;--border-width-regular:1px;--shadow-hairline:0 1px 2px 0 #0000000d;--shadow-sm:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--shadow-md:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--shadow-lg:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.omu-control{box-shadow:inset 0 0 0 1px var(--color-border-primary);border:0;transition:box-shadow .15s,background-color .15s;outline:none!important}.omu-control:focus,.omu-control:focus-visible{outline:none!important}.omu-control:hover:not(:disabled):not(:has(:disabled)):not([data-error]):not(:focus):not(:focus-within){box-shadow:inset 0 0 0 1px var(--color-border-secondary)}.omu-control:focus:not([data-error]),.omu-control:focus-within:not([data-error]){box-shadow:inset 0 0 0 2px var(--color-ring-primary)}.omu-control[data-error]{box-shadow:inset 0 0 0 1px var(--color-border-danger)}.omu-control[data-error]:focus-within{box-shadow:inset 0 0 0 2px var(--color-border-danger)}.omu-control:where([data-disabled]),.omu-control:has(:disabled){opacity:.5;cursor:not-allowed;pointer-events:none}@keyframes omu-datepicker-enter{0%{opacity:0;transform:scale(.97)translateY(-2px)}to{opacity:1;transform:scale(1)translateY(0)}}.omu-checkbox{border:1px solid var(--color-border-primary);border-radius:var(--border-radius-sm);cursor:pointer;background-color:#0000;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:border-color .15s,background-color .15s;display:inline-flex;position:relative}.omu-checkbox:hover:not([data-disabled]):not([data-checked]){border-color:var(--color-border-secondary)}.omu-checkbox[data-checked]{border-color:var(--color-background-inverse);background-color:var(--color-background-inverse)}.omu-checkbox[data-disabled]{opacity:.5;cursor:not-allowed}.omu-checkmark{transform-origin:50%;width:64%;height:32%;position:absolute;top:0;left:0;transform:rotate(-45deg)translate(-10%,100%)}.omu-arm-short{background:var(--color-text-inverse);transform-origin:0 0;width:2px;transition:transform .1s;position:absolute;top:0;bottom:0;left:0;transform:scaleY(0)}.omu-checkbox[data-checked] .omu-arm-short{transition:transform .1s 80ms;transform:scaleY(1)}.omu-arm-long{background:var(--color-text-inverse);transform-origin:0 100%;height:2px;transition:transform .1s;position:absolute;bottom:0;left:0;right:0;transform:scaleX(0)}.omu-checkbox[data-checked] .omu-arm-long{transition:transform .1s .16s;transform:scaleX(1)}.omu-radio{border:1.5px solid var(--color-border-primary);cursor:pointer;background-color:#0000;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:border-color .15s;display:inline-flex;position:relative}.omu-radio:hover:not([data-disabled]):not([data-checked]){border-color:var(--color-border-secondary)}.omu-radio[data-checked]{border-color:var(--color-background-inverse)}.omu-radio[data-disabled]{opacity:.5;cursor:not-allowed}.omu-radio-dot{background-color:var(--color-background-inverse);border-radius:50%;width:10px;height:10px;animation:.25s cubic-bezier(.4,0,.2,1) omu-radio-scale-in}@keyframes omu-radio-scale-in{0%{transform:scale(0)}to{transform:scale(1)}}.omu-slider{appearance:none;background:var(--color-border-primary);border-radius:3px;outline:none;height:6px;transition:background .15s}.omu-slider:hover:not(:disabled){background:var(--color-border-secondary)}.omu-slider::-webkit-slider-thumb{appearance:none;border:2px solid var(--color-background-inverse);background:var(--color-background-primary);cursor:pointer;border-radius:50%;width:18px;height:18px;transition:box-shadow .15s,transform .15s}.omu-slider::-webkit-slider-thumb:hover{box-shadow:0 0 0 4px var(--color-background-inverse)}@supports (color:color-mix(in lab, red, red)){.omu-slider::-webkit-slider-thumb:hover{box-shadow:0 0 0 4px color-mix(in srgb,var(--color-background-inverse)15%,transparent)}}.omu-slider::-webkit-slider-thumb:active{transform:scale(1.1)}.omu-slider:focus-visible::-webkit-slider-thumb{outline:2px solid var(--color-ring-primary);outline-offset:2px}.omu-slider::-moz-range-thumb{border:2px solid var(--color-background-inverse);background:var(--color-background-primary);cursor:pointer;border-radius:50%;width:18px;height:18px;transition:box-shadow .15s,transform .15s}.omu-slider::-moz-range-thumb:hover{box-shadow:0 0 0 4px var(--color-background-inverse)}@supports (color:color-mix(in lab, red, red)){.omu-slider::-moz-range-thumb:hover{box-shadow:0 0 0 4px color-mix(in srgb,var(--color-background-inverse)15%,transparent)}}.omu-slider::-moz-range-track{background:var(--color-border-primary);border-radius:3px;height:6px}.omu-slider:disabled{opacity:.5;cursor:not-allowed}.omu-slider:disabled::-webkit-slider-thumb{cursor:not-allowed}.omu-slider:disabled::-moz-range-thumb{cursor:not-allowed}.omu-shake{animation:.5s omu-shake}@keyframes omu-shake{0%,to{transform:translate(0)}20%,60%{transform:translate(-4px)}40%,80%{transform:translate(4px)}}@keyframes omu-menu-enter{0%{opacity:0;transform:scale(.97)translateY(-2px)}to{opacity:1;transform:scale(1)translateY(0)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--spacing:.25rem;--container-xs:20rem;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg-primary:var(--color-background-primary);--color-bg-secondary:var(--color-background-secondary);--color-bg-tertiary:var(--color-background-tertiary);--color-bg-inverse:var(--color-background-inverse);--color-bg-ghost:var(--color-background-ghost);--color-bg-info:var(--color-background-info);--color-bg-danger:var(--color-background-danger);--color-bg-success:var(--color-background-success);--color-bg-warning:var(--color-background-warning);--color-txt-primary:var(--color-text-primary);--color-txt-secondary:var(--color-text-secondary);--color-txt-tertiary:var(--color-text-tertiary);--color-txt-inverse:var(--color-text-inverse);--color-txt-info:var(--color-text-info);--color-txt-danger:var(--color-text-danger);--color-txt-success:var(--color-text-success);--color-txt-warning:var(--color-text-warning);--color-txt-disabled:var(--color-text-disabled);--color-bdr-primary:var(--color-border-primary);--color-bdr-secondary:var(--color-border-secondary);--color-bdr-tertiary:var(--color-border-tertiary);--color-bdr-danger:var(--color-border-danger);--color-bdr-success:var(--color-border-success);--color-bdr-warning:var(--color-border-warning);--text-xs:var(--font-text-xs-size);--text-xs--line-height:var(--font-text-xs-line-height);--text-sm:var(--font-text-sm-size);--text-sm--line-height:var(--font-text-sm-line-height);--text-base:var(--font-text-md-size);--text-base--line-height:var(--font-text-md-line-height);--text-lg:var(--font-text-lg-size);--text-lg--line-height:var(--font-text-lg-line-height);--radius-sm:var(--border-radius-sm);--radius-md:var(--border-radius-md);--radius-lg:var(--border-radius-lg);--radius-xl:var(--border-radius-xl);--radius-full:var(--border-radius-full);--color-ring-primary:var(--color-ring-primary);--color-ring-secondary:var(--color-ring-secondary);--color-ring-inverse:var(--color-ring-inverse);--color-ring-info:var(--color-ring-info);--color-ring-danger:var(--color-ring-danger);--color-ring-success:var(--color-ring-success);--color-ring-warning:var(--color-ring-warning)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:before,:after{box-sizing:border-box}*{margin:0}html,body{height:100%}html{font-synthesis-weight:none;-webkit-font-smoothing:antialiased}body{font-family:var(--font-sans);font-size:var(--font-text-md-size);line-height:var(--font-text-md-line-height);color:var(--color-text-primary);background-color:var(--color-background-primary)}input,textarea,select{appearance:none}button,input,select,textarea{touch-action:manipulation}img,svg{flex-shrink:0;max-width:100%;display:block}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.top-0\.5{top:calc(var(--spacing)*.5)}.right-0{right:calc(var(--spacing)*0)}.bottom-0\.5{bottom:calc(var(--spacing)*.5)}.left-0{left:calc(var(--spacing)*0)}.left-1\/2{left:50%}.z-10{z-index:10}.z-50{z-index:50}.z-\[1\]{z-index:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.my-1{margin-block:calc(var(--spacing)*1)}.my-2{margin-block:calc(var(--spacing)*2)}.my-3{margin-block:calc(var(--spacing)*3)}.my-4{margin-block:calc(var(--spacing)*4)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.-mb-px{margin-bottom:-1px}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-11{height:calc(var(--spacing)*11)}.h-px{height:1px}.max-h-\[600px\]{max-height:600px}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-\[32px\]{min-height:32px}.min-h-\[38px\]{min-height:38px}.min-h-\[44px\]{min-height:44px}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-7{width:calc(var(--spacing)*7)}.w-9{width:calc(var(--spacing)*9)}.w-full{width:100%}.w-px{width:1px}.max-w-\[150px\]{max-width:150px}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-\[60px\]{min-width:60px}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:calc(calc(1/2*100%)*-1);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0\.5{--tw-translate-x:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-4\.5{--tw-translate-x:calc(var(--spacing)*4.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-text{cursor:text}.resize{resize:both}.resize-none{resize:none}.resize-x{resize:horizontal}.resize-y{resize:vertical}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-2\.5{gap:calc(var(--spacing)*2.5)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.\!rounded-full{border-radius:var(--radius-full)!important}.rounded-full{border-radius:var(--radius-full)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-none{--tw-border-style:none;border-style:none}.border-bdr-danger{border-color:var(--color-bdr-danger)}.border-bdr-primary{border-color:var(--color-bdr-primary)}.border-bdr-secondary{border-color:var(--color-bdr-secondary)}.border-bdr-success{border-color:var(--color-bdr-success)}.border-bdr-warning{border-color:var(--color-bdr-warning)}.border-transparent{border-color:#0000}.bg-bdr-secondary{background-color:var(--color-bdr-secondary)}.bg-bdr-tertiary{background-color:var(--color-bdr-tertiary)}.bg-bg-danger{background-color:var(--color-bg-danger)}.bg-bg-ghost{background-color:var(--color-bg-ghost)}.bg-bg-info{background-color:var(--color-bg-info)}.bg-bg-inverse{background-color:var(--color-bg-inverse)}.bg-bg-primary{background-color:var(--color-bg-primary)}.bg-bg-secondary{background-color:var(--color-bg-secondary)}.bg-bg-success{background-color:var(--color-bg-success)}.bg-bg-tertiary{background-color:var(--color-bg-tertiary)}.bg-bg-warning{background-color:var(--color-bg-warning)}.bg-transparent{background-color:#0000}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-3\.5{padding-inline:calc(var(--spacing)*3.5)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-8{padding-block:calc(var(--spacing)*8)}.pt-2{padding-top:calc(var(--spacing)*2)}.pb-1{padding-bottom:calc(var(--spacing)*1)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-2\.5{padding-bottom:calc(var(--spacing)*2.5)}.pl-2{padding-left:calc(var(--spacing)*2)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.heading-2xl{font-size:var(--font-heading-2xl-size);font-weight:var(--font-weight-semibold);line-height:var(--font-heading-2xl-line-height)}.heading-3xl{font-size:var(--font-heading-3xl-size);font-weight:var(--font-weight-semibold);line-height:var(--font-heading-3xl-line-height)}.heading-lg{font-size:var(--font-heading-lg-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-lg-line-height)}.heading-md{font-size:var(--font-heading-md-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-md-line-height)}.heading-sm{font-size:var(--font-heading-sm-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-sm-line-height)}.heading-xl{font-size:var(--font-heading-xl-size);font-weight:var(--font-weight-semibold);line-height:var(--font-heading-xl-line-height)}.heading-xs{font-size:var(--font-heading-xs-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-xs-line-height)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-txt-danger{color:var(--color-txt-danger)}.text-txt-disabled{color:var(--color-txt-disabled)}.text-txt-info{color:var(--color-txt-info)}.text-txt-inverse{color:var(--color-txt-inverse)}.text-txt-primary{color:var(--color-txt-primary)}.text-txt-secondary{color:var(--color-txt-secondary)}.text-txt-success{color:var(--color-txt-success)}.text-txt-tertiary{color:var(--color-txt-tertiary)}.text-txt-warning{color:var(--color-txt-warning)}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-75{--tw-duration:75ms;transition-duration:75ms}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.peer-focus-visible\:\[outline\:2px_solid_var\(--color-ring-primary\)\]:is(:where(.peer):focus-visible~*){outline:2px solid var(--color-ring-primary)}.placeholder\:text-txt-tertiary::placeholder{color:var(--color-txt-tertiary)}.focus-within\:outline:focus-within{outline-style:var(--tw-outline-style);outline-width:1px}.focus-within\:outline-2:focus-within{outline-style:var(--tw-outline-style);outline-width:2px}.focus-within\:outline-offset-0:focus-within{outline-offset:0px}.focus-within\:outline-ring-primary:focus-within{outline-color:var(--color-ring-primary)}@media (hover:hover){.hover\:bg-bg-secondary:hover{background-color:var(--color-bg-secondary)}.hover\:bg-bg-tertiary:hover{background-color:var(--color-bg-tertiary)}.hover\:text-txt-primary:hover{color:var(--color-txt-primary)}.hover\:text-txt-secondary:hover{color:var(--color-txt-secondary)}.hover\:opacity-90:hover{opacity:.9}.hover\:opacity-100:hover{opacity:1}}.focus\:outline:focus{outline-style:var(--tw-outline-style);outline-width:1px}.focus\:outline-2:focus{outline-style:var(--tw-outline-style);outline-width:2px}.focus\:outline-offset-0:focus{outline-offset:0px}.focus\:outline-ring-primary:focus{outline-color:var(--color-ring-primary)}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-ring-danger:focus-visible{outline-color:var(--color-ring-danger)}.focus-visible\:outline-ring-primary:focus-visible{outline-color:var(--color-ring-primary)}.focus-visible\:outline-ring-secondary:focus-visible{outline-color:var(--color-ring-secondary)}.active\:bg-bg-tertiary:active{background-color:var(--color-bg-tertiary)}}:root{--color-background-primary:light-dark(#fff,#1a1a1a);--color-background-secondary:light-dark(#f5f5f5,#2d2d2d);--color-background-tertiary:light-dark(#e5e5e5,#404040);--color-background-inverse:light-dark(#1a1a1a,#f5f5f5);--color-background-ghost:light-dark(#0000,#fff0);--color-background-info:light-dark(#eff6ff,#1e3a5f);--color-background-danger:light-dark(#fef2f2,#5c1d1d);--color-background-success:light-dark(#f0fdf4,#14532d);--color-background-warning:light-dark(#fefce8,#713f12);--color-background-disabled:light-dark(#f5f5f5,#2d2d2d);--color-text-primary:light-dark(#171717,#f5f5f5);--color-text-secondary:light-dark(#525252,#a3a3a3);--color-text-tertiary:light-dark(#a3a3a3,#737373);--color-text-inverse:light-dark(#f5f5f5,#171717);--color-text-ghost:light-dark(#525252,#a3a3a3);--color-text-info:light-dark(#1d4ed8,#60a5fa);--color-text-danger:light-dark(#b91c1c,#f87171);--color-text-success:light-dark(#15803d,#4ade80);--color-text-warning:light-dark(#a16207,#fbbf24);--color-text-disabled:light-dark(#a3a3a3,#525252);--color-border-primary:light-dark(#e5e5e5,#404040);--color-border-secondary:light-dark(#d4d4d4,#525252);--color-border-tertiary:light-dark(#f5f5f5,#2d2d2d);--color-border-inverse:light-dark(#ffffff4d,#0000004d);--color-border-ghost:light-dark(#0000,#fff0);--color-border-info:light-dark(#93c5fd,#1e40af);--color-border-danger:light-dark(#fca5a5,#991b1b);--color-border-success:light-dark(#86efac,#166534);--color-border-warning:light-dark(#fde047,#854d0e);--color-border-disabled:light-dark(#e5e5e5,#404040);--color-ring-primary:light-dark(#666,#cdcdcd);--color-ring-secondary:light-dark(#a3a3a3,#737373);--color-ring-inverse:light-dark(#fff,#171717);--color-ring-info:light-dark(#2563eb,#3b82f6);--color-ring-danger:light-dark(#dc2626,#ef4444);--color-ring-success:light-dark(#16a34a,#22c55e);--color-ring-warning:light-dark(#ca8a04,#eab308);--font-sans:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;--font-mono:ui-monospace,"SF Mono",Monaco,"Cascadia Code",monospace;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-text-xs-size:.75rem;--font-text-sm-size:.875rem;--font-text-md-size:1rem;--font-text-lg-size:1.125rem;--font-heading-xs-size:.75rem;--font-heading-sm-size:.875rem;--font-heading-md-size:1rem;--font-heading-lg-size:1.25rem;--font-heading-xl-size:1.5rem;--font-heading-2xl-size:1.875rem;--font-heading-3xl-size:2.25rem;--font-text-xs-line-height:1.4;--font-text-sm-line-height:1.4;--font-text-md-line-height:1.5;--font-text-lg-line-height:1.5;--font-heading-xs-line-height:1.4;--font-heading-sm-line-height:1.4;--font-heading-md-line-height:1.4;--font-heading-lg-line-height:1.3;--font-heading-xl-line-height:1.25;--font-heading-2xl-line-height:1.2;--font-heading-3xl-line-height:1.1;--border-radius-xs:2px;--border-radius-sm:4px;--border-radius-md:6px;--border-radius-lg:8px;--border-radius-xl:12px;--border-radius-full:9999px;--border-width-regular:1px;--shadow-hairline:0 1px 2px 0 #0000000d;--shadow-sm:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--shadow-md:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--shadow-lg:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.omu-control{box-shadow:inset 0 0 0 1px var(--color-border-primary);border:0;transition:box-shadow .15s,background-color .15s;outline:none!important}.omu-control:focus,.omu-control:focus-visible{outline:none!important}.omu-control:hover:not(:disabled):not(:has(:disabled)):not([data-error]):not(:focus):not(:focus-within){box-shadow:inset 0 0 0 1px var(--color-border-secondary)}.omu-control:focus:not([data-error]),.omu-control:focus-within:not([data-error]){box-shadow:inset 0 0 0 2px var(--color-ring-primary)}.omu-control[data-error]{box-shadow:inset 0 0 0 1px var(--color-border-danger)}.omu-control[data-error]:focus-within{box-shadow:inset 0 0 0 2px var(--color-border-danger)}.omu-control:where([data-disabled]),.omu-control:has(:disabled){opacity:.5;cursor:not-allowed;pointer-events:none}@keyframes omu-datepicker-enter{0%{opacity:0;transform:scale(.97)translateY(-2px)}to{opacity:1;transform:scale(1)translateY(0)}}.omu-checkbox{border:1px solid var(--color-border-primary);border-radius:var(--border-radius-sm);cursor:pointer;background-color:#0000;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:border-color .15s,background-color .15s;display:inline-flex;position:relative}.omu-checkbox:hover:not([data-disabled]):not([data-checked]){border-color:var(--color-border-secondary)}.omu-checkbox[data-checked]{border-color:var(--color-background-inverse);background-color:var(--color-background-inverse)}.omu-checkbox[data-disabled]{opacity:.5;cursor:not-allowed}.omu-checkmark{transform-origin:50%;width:64%;height:32%;position:absolute;top:0;left:0;transform:rotate(-45deg)translate(-10%,100%)}.omu-arm-short{background:var(--color-text-inverse);transform-origin:0 0;width:2px;transition:transform .1s;position:absolute;top:0;bottom:0;left:0;transform:scaleY(0)}.omu-checkbox[data-checked] .omu-arm-short{transition:transform .1s 80ms;transform:scaleY(1)}.omu-arm-long{background:var(--color-text-inverse);transform-origin:0 100%;height:2px;transition:transform .1s;position:absolute;bottom:0;left:0;right:0;transform:scaleX(0)}.omu-checkbox[data-checked] .omu-arm-long{transition:transform .1s .16s;transform:scaleX(1)}.omu-radio{border:1.5px solid var(--color-border-primary);cursor:pointer;background-color:#0000;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:border-color .15s;display:inline-flex;position:relative}.omu-radio:hover:not([data-disabled]):not([data-checked]){border-color:var(--color-border-secondary)}.omu-radio[data-checked]{border-color:var(--color-background-inverse)}.omu-radio[data-disabled]{opacity:.5;cursor:not-allowed}.omu-radio-dot{background-color:var(--color-background-inverse);border-radius:50%;width:10px;height:10px;animation:.25s cubic-bezier(.4,0,.2,1) omu-radio-scale-in}@keyframes omu-radio-scale-in{0%{transform:scale(0)}to{transform:scale(1)}}.omu-slider{appearance:none;background:var(--color-border-primary);border-radius:3px;outline:none;height:6px;transition:background .15s}.omu-slider:hover:not(:disabled){background:var(--color-border-secondary)}.omu-slider::-webkit-slider-thumb{appearance:none;border:2px solid var(--color-background-inverse);background:var(--color-background-primary);cursor:pointer;border-radius:50%;width:18px;height:18px;transition:box-shadow .15s,transform .15s}.omu-slider::-webkit-slider-thumb:hover{box-shadow:0 0 0 4px var(--color-background-inverse)}@supports (color:color-mix(in lab, red, red)){.omu-slider::-webkit-slider-thumb:hover{box-shadow:0 0 0 4px color-mix(in srgb,var(--color-background-inverse)15%,transparent)}}.omu-slider::-webkit-slider-thumb:active{transform:scale(1.1)}.omu-slider:focus-visible::-webkit-slider-thumb{outline:2px solid var(--color-ring-primary);outline-offset:2px}.omu-slider::-moz-range-thumb{border:2px solid var(--color-background-inverse);background:var(--color-background-primary);cursor:pointer;border-radius:50%;width:18px;height:18px;transition:box-shadow .15s,transform .15s}.omu-slider::-moz-range-thumb:hover{box-shadow:0 0 0 4px var(--color-background-inverse)}@supports (color:color-mix(in lab, red, red)){.omu-slider::-moz-range-thumb:hover{box-shadow:0 0 0 4px color-mix(in srgb,var(--color-background-inverse)15%,transparent)}}.omu-slider::-moz-range-track{background:var(--color-border-primary);border-radius:3px;height:6px}.omu-slider:disabled{opacity:.5;cursor:not-allowed}.omu-slider:disabled::-webkit-slider-thumb{cursor:not-allowed}.omu-slider:disabled::-moz-range-thumb{cursor:not-allowed}.omu-shake{animation:.5s omu-shake}@keyframes omu-shake{0%,to{transform:translate(0)}20%,60%{transform:translate(-4px)}40%,80%{transform:translate(4px)}}@keyframes omu-menu-enter{0%{opacity:0;transform:scale(.97)translateY(-2px)}to{opacity:1;transform:scale(1)translateY(0)}}.omu-editor-content .milkdown{font-size:var(--font-text-md-size);line-height:var(--font-text-md-line-height);outline:none;flex-direction:column;flex:1;min-height:0;display:flex}.omu-editor-content .milkdown .editor{outline:none;flex-direction:column;flex:1;display:flex}.omu-editor-content .milkdown .editor>.paragraph:only-child,.omu-editor-content .milkdown .editor>p:only-child{flex:1}.omu-editor-content .milkdown .editor.ProseMirror-empty:before{content:attr(data-placeholder);color:var(--color-text-tertiary);pointer-events:none;font-style:italic;position:absolute}.omu-editor-content[data-placeholder] .milkdown .editor:empty:before{content:attr(data-placeholder);color:var(--color-text-tertiary);font-style:italic}.omu-editor-content .milkdown h1{font-size:var(--font-heading-lg-size);font-weight:var(--font-weight-semibold);line-height:var(--font-heading-lg-line-height);color:var(--color-text-primary);margin-top:1.5rem;margin-bottom:1rem}.omu-editor-content .milkdown h1:first-child{margin-top:0}.omu-editor-content .milkdown h2{font-size:var(--font-heading-md-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-md-line-height);color:var(--color-text-primary);margin-top:1.5rem;margin-bottom:.75rem}.omu-editor-content .milkdown h3{font-size:var(--font-heading-sm-size);font-weight:var(--font-weight-medium);line-height:var(--font-heading-sm-line-height);color:var(--color-text-primary);margin-top:1.25rem;margin-bottom:.5rem}.omu-editor-content .milkdown p{font-size:var(--font-text-md-size);line-height:var(--font-text-md-line-height);margin-bottom:.75rem}.omu-editor-content .milkdown p:last-child{margin-bottom:0}.omu-editor-content .milkdown strong{font-weight:var(--font-weight-semibold)}.omu-editor-content .milkdown em{font-style:italic}.omu-editor-content .milkdown del,.omu-editor-content .milkdown s{color:var(--color-text-secondary);text-decoration:line-through}.omu-editor-content .milkdown a{color:var(--color-text-info);cursor:pointer;text-decoration:underline}.omu-editor-content .milkdown a:hover{opacity:.8}.omu-editor-content .milkdown code{font-family:var(--font-mono);font-size:var(--font-text-sm-size);background:var(--color-background-secondary);border-radius:var(--border-radius-sm);padding:.125rem .375rem}.omu-editor-content .milkdown pre{background:var(--color-background-secondary);border:var(--border-width-regular)solid var(--color-border-secondary);border-radius:var(--border-radius-md);margin-bottom:.75rem;padding:.75rem 1rem;overflow-x:auto}.omu-editor-content .milkdown pre code{font-size:var(--font-text-sm-size);line-height:var(--font-text-sm-line-height);background:0 0;padding:0}.omu-editor-content .milkdown ul,.omu-editor-content .milkdown ol{margin-bottom:.75rem;padding-left:1.5rem}.omu-editor-content .milkdown li{font-size:var(--font-text-md-size);line-height:var(--font-text-md-line-height);margin-bottom:.25rem}.omu-editor-content .milkdown li p{margin-bottom:.25rem}.omu-editor-content .milkdown li:last-child{margin-bottom:0}.omu-editor-content .milkdown li[data-item-type=task]{cursor:pointer;margin-left:-4px;padding-left:24px;list-style:none;position:relative}.omu-editor-content .milkdown li[data-item-type=task]:before{content:"";border:var(--border-width-regular)solid var(--color-border-primary);background:var(--color-background-primary);pointer-events:none;border-radius:2px;width:12px;height:12px;position:absolute;top:2px;left:0}.omu-editor-content .milkdown li[data-item-type=task][data-checked=true]:before{background:var(--color-text-primary);border-color:var(--color-text-primary)}.omu-editor-content .milkdown li[data-item-type=task][data-checked=true]:after{content:"";border:solid var(--color-background-primary);pointer-events:none;border-width:0 1.5px 1.5px 0;width:4px;height:8px;position:absolute;top:3px;left:4px;transform:rotate(45deg)}.omu-editor-content .milkdown blockquote{border-left:3px solid var(--color-border-info);color:var(--color-text-secondary);margin-bottom:.75rem;padding-left:1rem;font-style:italic}.omu-editor-content .milkdown blockquote p{margin-bottom:.5rem}.omu-editor-content .milkdown blockquote p:last-child{margin-bottom:0}.omu-editor-content .milkdown hr{border:none;border-top:var(--border-width-regular)solid var(--color-border-primary);margin:1.5rem 0}.omu-editor-content .milkdown ::selection{background:var(--color-background-info);color:var(--color-text-primary)}.omu-editor-content[data-readonly],.omu-editor-content[data-readonly] .milkdown .editor{cursor:default}.omu-editor .omu-editor-toolbar{border:none;border-radius:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}
@@ -1,22 +1,66 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { ColumnDef } from '@tanstack/react-table';
4
+ export { ColumnDef } from '@tanstack/react-table';
5
+
6
+ interface DataTableProps<T> {
7
+ /**
8
+ * Column definitions following TanStack Table's ColumnDef format.
9
+ * Use `accessorKey` for simple property access, `header` for the
10
+ * column label, and `cell` for custom cell rendering.
11
+ */
12
+ columns: ColumnDef<T, unknown>[];
13
+ /** Row data array. */
14
+ data: T[];
15
+ /** Enable client-side column sorting (click headers to toggle). */
16
+ sortable?: boolean;
17
+ /** Enable a global text filter input above the table. */
18
+ filterable?: boolean;
19
+ /** Enable pagination with this many rows per page. Omit for no pagination. */
20
+ pageSize?: number;
21
+ /**
22
+ * Enable row virtualization for large datasets. Renders only visible
23
+ * rows plus a small overscan buffer. Recommended for >100 rows.
24
+ */
25
+ virtualized?: boolean;
26
+ /** Keep the header row visible while scrolling. Defaults to true. */
27
+ stickyHeader?: boolean;
28
+ /** Content shown when the data array is empty. */
29
+ emptyMessage?: ReactNode;
30
+ /** Called when a row is clicked. Receives the row's original data and index. */
31
+ onRowClick?: (row: {
32
+ original: T;
33
+ index: number;
34
+ }) => void;
35
+ /** Show a loading skeleton instead of data rows. */
36
+ loading?: boolean;
37
+ /** Reduce row height for tighter layouts (e.g. inline display mode). */
38
+ compact?: boolean;
39
+ /** Additional CSS classes on the outermost wrapper. */
40
+ className?: string;
41
+ /** Placeholder text for the filter input. */
42
+ filterPlaceholder?: string;
43
+ }
1
44
  /**
2
- * open-mcp-app-ui/table
45
+ * High-performance data table with sorting, filtering, pagination,
46
+ * and row virtualization.
3
47
  *
4
- * High-performance virtualized data table built on TanStack Table + TanStack Virtual.
5
- * This is an optional import apps that don't use it pay zero bundle cost.
48
+ * All visual aspects map to MCP Apps spec CSS variables for automatic
49
+ * host theming. The table adapts to display modes when inside <AppLayout>.
6
50
  *
7
- * Phase 2: Placeholder export. Implementation coming soon.
51
+ * @example
52
+ * ```tsx
53
+ * import { DataTable } from "open-mcp-app-ui/table";
8
54
  *
9
- * Planned usage:
10
- * import { DataTable } from "open-mcp-app-ui/table";
55
+ * const columns = [
56
+ * { accessorKey: "name", header: "Name" },
57
+ * { accessorKey: "status", header: "Status" },
58
+ * { accessorKey: "date", header: "Created" },
59
+ * ];
11
60
  *
12
- * <DataTable
13
- * columns={[{ accessorKey: "name", header: "Name", sortable: true }]}
14
- * data={items}
15
- * sortable
16
- * virtualized
17
- * />
61
+ * <DataTable columns={columns} data={items} sortable filterable />
62
+ * ```
18
63
  */
19
- declare const DATA_TABLE_VERSION = "0.0.1";
20
- declare const DATA_TABLE_STATUS: "planned";
64
+ declare const DataTable: <T>({ columns, data, sortable, filterable, pageSize, virtualized, stickyHeader, emptyMessage, onRowClick, loading, compact, className, filterPlaceholder, }: DataTableProps<T>) => react_jsx_runtime.JSX.Element;
21
65
 
22
- export { DATA_TABLE_STATUS, DATA_TABLE_VERSION };
66
+ export { DataTable, type DataTableProps };
@@ -1,8 +1,270 @@
1
- // src/table/index.ts
2
- var DATA_TABLE_VERSION = "0.0.1";
3
- var DATA_TABLE_STATUS = "planned";
1
+ // src/table/DataTable.tsx
2
+ import {
3
+ useState,
4
+ useMemo,
5
+ useRef,
6
+ useCallback
7
+ } from "react";
8
+ import {
9
+ useReactTable,
10
+ getCoreRowModel,
11
+ getSortedRowModel,
12
+ getFilteredRowModel,
13
+ getPaginationRowModel,
14
+ flexRender
15
+ } from "@tanstack/react-table";
16
+ import { useVirtualizer } from "@tanstack/react-virtual";
17
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
18
+ var ROW_HEIGHT_COMPACT = 32;
19
+ var ROW_HEIGHT_DEFAULT = 40;
20
+ var OVERSCAN_COUNT = 10;
21
+ var SKELETON_ROW_COUNT = 8;
22
+ var SortIndicator = ({ direction }) => {
23
+ if (direction === "asc") {
24
+ return /* @__PURE__ */ jsx("span", { className: "text-txt-primary ml-1", "aria-label": "sorted ascending", children: "\u2191" });
25
+ }
26
+ if (direction === "desc") {
27
+ return /* @__PURE__ */ jsx("span", { className: "text-txt-primary ml-1", "aria-label": "sorted descending", children: "\u2193" });
28
+ }
29
+ return /* @__PURE__ */ jsx("span", { className: "text-txt-tertiary ml-1 opacity-0 group-hover:opacity-100 transition-opacity", children: "\u2195" });
30
+ };
31
+ var HeaderCell = ({
32
+ header,
33
+ sortable
34
+ }) => {
35
+ const canSort = sortable && header.column.getCanSort();
36
+ const sorted = header.column.getIsSorted();
37
+ const content = header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext());
38
+ if (canSort) {
39
+ return /* @__PURE__ */ jsxs(
40
+ "button",
41
+ {
42
+ type: "button",
43
+ className: "group inline-flex items-center gap-0.5 cursor-pointer select-none w-full text-left",
44
+ onClick: header.column.getToggleSortingHandler(),
45
+ "aria-sort": sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : "none",
46
+ children: [
47
+ content,
48
+ /* @__PURE__ */ jsx(SortIndicator, { direction: sorted })
49
+ ]
50
+ }
51
+ );
52
+ }
53
+ return /* @__PURE__ */ jsx(Fragment, { children: content });
54
+ };
55
+ var SkeletonRow = ({ columnCount, compact }) => /* @__PURE__ */ jsx("tr", { className: "border-b border-bdr-secondary", children: Array.from({ length: columnCount }, (_, i) => /* @__PURE__ */ jsx(
56
+ "td",
57
+ {
58
+ className: compact ? "px-3 py-1.5" : "px-3 py-2.5",
59
+ children: /* @__PURE__ */ jsx("div", { className: "h-3.5 bg-bg-secondary rounded-sm animate-pulse", style: { width: `${50 + Math.random() * 40}%` } })
60
+ },
61
+ i
62
+ )) });
63
+ var DataTable = ({
64
+ columns,
65
+ data,
66
+ sortable = false,
67
+ filterable = false,
68
+ pageSize,
69
+ virtualized = false,
70
+ stickyHeader = true,
71
+ emptyMessage = "No data",
72
+ onRowClick,
73
+ loading = false,
74
+ compact = false,
75
+ className = "",
76
+ filterPlaceholder = "Filter..."
77
+ }) => {
78
+ const [sorting, setSorting] = useState([]);
79
+ const [globalFilter, setGlobalFilter] = useState("");
80
+ const table = useReactTable({
81
+ data,
82
+ columns,
83
+ state: {
84
+ sorting,
85
+ globalFilter
86
+ },
87
+ onSortingChange: setSorting,
88
+ onGlobalFilterChange: setGlobalFilter,
89
+ getCoreRowModel: getCoreRowModel(),
90
+ ...sortable ? { getSortedRowModel: getSortedRowModel() } : {},
91
+ ...filterable ? { getFilteredRowModel: getFilteredRowModel() } : {},
92
+ ...pageSize ? { getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize } } } : {}
93
+ });
94
+ const { rows } = table.getRowModel();
95
+ const headerGroups = table.getHeaderGroups();
96
+ const scrollRef = useRef(null);
97
+ const rowHeight = compact ? ROW_HEIGHT_COMPACT : ROW_HEIGHT_DEFAULT;
98
+ const virtualizer = useVirtualizer({
99
+ count: rows.length,
100
+ getScrollElement: () => scrollRef.current,
101
+ estimateSize: () => rowHeight,
102
+ overscan: OVERSCAN_COUNT,
103
+ enabled: virtualized
104
+ });
105
+ const virtualRows = virtualized ? virtualizer.getVirtualItems() : null;
106
+ const totalHeight = virtualized ? virtualizer.getTotalSize() : 0;
107
+ const hasPagination = pageSize != null && !loading;
108
+ const pageCount = table.getPageCount();
109
+ const currentPage = table.getState().pagination.pageIndex;
110
+ const canPrev = table.getCanPreviousPage();
111
+ const canNext = table.getCanNextPage();
112
+ const renderRow = useCallback(
113
+ (row, style) => {
114
+ const isClickable = onRowClick != null;
115
+ return /* @__PURE__ */ jsx(
116
+ "tr",
117
+ {
118
+ className: [
119
+ "border-b border-bdr-secondary transition-colors",
120
+ isClickable ? "cursor-pointer hover:bg-bg-secondary" : ""
121
+ ].join(" "),
122
+ style,
123
+ onClick: isClickable ? () => onRowClick({ original: row.original, index: row.index }) : void 0,
124
+ role: isClickable ? "button" : void 0,
125
+ tabIndex: isClickable ? 0 : void 0,
126
+ onKeyDown: isClickable ? (e) => {
127
+ if (e.key === "Enter" || e.key === " ") {
128
+ e.preventDefault();
129
+ onRowClick({ original: row.original, index: row.index });
130
+ }
131
+ } : void 0,
132
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(
133
+ "td",
134
+ {
135
+ className: [
136
+ "text-txt-primary",
137
+ compact ? "px-3 py-1.5 text-xs" : "px-3 py-2.5 text-sm"
138
+ ].join(" "),
139
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
140
+ },
141
+ cell.id
142
+ ))
143
+ },
144
+ row.id
145
+ );
146
+ },
147
+ [onRowClick, compact]
148
+ );
149
+ const bodyContent = useMemo(() => {
150
+ if (loading) {
151
+ return Array.from({ length: SKELETON_ROW_COUNT }, (_, i) => /* @__PURE__ */ jsx(SkeletonRow, { columnCount: columns.length, compact }, i));
152
+ }
153
+ if (rows.length === 0) {
154
+ return /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
155
+ "td",
156
+ {
157
+ colSpan: columns.length,
158
+ className: "text-center text-txt-tertiary py-8 text-sm",
159
+ children: emptyMessage
160
+ }
161
+ ) });
162
+ }
163
+ if (virtualized && virtualRows) {
164
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
165
+ virtualRows.length > 0 && virtualRows[0].start > 0 && /* @__PURE__ */ jsx("tr", { style: { height: virtualRows[0].start }, children: /* @__PURE__ */ jsx("td", { colSpan: columns.length }) }),
166
+ virtualRows.map((vRow) => {
167
+ const row = rows[vRow.index];
168
+ return renderRow(row, { height: `${vRow.size}px` });
169
+ }),
170
+ virtualRows.length > 0 && /* @__PURE__ */ jsx("tr", { style: { height: Math.max(0, totalHeight - (virtualRows[virtualRows.length - 1]?.end ?? 0)) }, children: /* @__PURE__ */ jsx("td", { colSpan: columns.length }) })
171
+ ] });
172
+ }
173
+ return rows.map((row) => renderRow(row));
174
+ }, [loading, rows, columns.length, compact, emptyMessage, virtualized, virtualRows, totalHeight, renderRow]);
175
+ return /* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-2 ${className}`, children: [
176
+ filterable && !loading && /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsx(
177
+ "input",
178
+ {
179
+ type: "text",
180
+ value: globalFilter,
181
+ onChange: (e) => setGlobalFilter(e.target.value),
182
+ placeholder: filterPlaceholder,
183
+ className: [
184
+ "w-full max-w-xs text-sm px-3 py-1.5 rounded-md",
185
+ "bg-bg-primary text-txt-primary placeholder:text-txt-tertiary",
186
+ "border border-bdr-secondary",
187
+ "outline-none focus:outline focus:outline-2 focus:outline-offset-0 focus:outline-ring-primary",
188
+ "transition-colors"
189
+ ].join(" ")
190
+ }
191
+ ) }),
192
+ /* @__PURE__ */ jsx(
193
+ "div",
194
+ {
195
+ ref: scrollRef,
196
+ className: [
197
+ "overflow-auto rounded-lg",
198
+ "border border-bdr-primary",
199
+ virtualized ? "max-h-[600px]" : ""
200
+ ].join(" "),
201
+ children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left", children: [
202
+ /* @__PURE__ */ jsx(
203
+ "thead",
204
+ {
205
+ className: [
206
+ "bg-bg-secondary",
207
+ stickyHeader ? "sticky top-0 z-10" : ""
208
+ ].join(" "),
209
+ children: headerGroups.map((hg) => /* @__PURE__ */ jsx("tr", { children: hg.headers.map((header) => /* @__PURE__ */ jsx(
210
+ "th",
211
+ {
212
+ className: [
213
+ "text-txt-secondary font-medium",
214
+ compact ? "px-3 py-1.5 text-xs" : "px-3 py-2.5 text-xs",
215
+ "border-b border-bdr-primary",
216
+ "whitespace-nowrap"
217
+ ].join(" "),
218
+ style: { width: header.getSize() !== 150 ? header.getSize() : void 0 },
219
+ children: /* @__PURE__ */ jsx(HeaderCell, { header, sortable })
220
+ },
221
+ header.id
222
+ )) }, hg.id))
223
+ }
224
+ ),
225
+ /* @__PURE__ */ jsx("tbody", { className: "bg-bg-primary", children: bodyContent })
226
+ ] })
227
+ }
228
+ ),
229
+ hasPagination && pageCount > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm text-txt-secondary", children: [
230
+ /* @__PURE__ */ jsxs("span", { children: [
231
+ "Page ",
232
+ currentPage + 1,
233
+ " of ",
234
+ pageCount
235
+ ] }),
236
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
237
+ /* @__PURE__ */ jsx(
238
+ "button",
239
+ {
240
+ type: "button",
241
+ onClick: () => table.previousPage(),
242
+ disabled: !canPrev,
243
+ className: [
244
+ "px-2.5 py-1 rounded-md text-xs font-medium transition-colors",
245
+ canPrev ? "bg-bg-secondary text-txt-primary hover:bg-bg-tertiary cursor-pointer" : "bg-bg-secondary text-txt-disabled cursor-not-allowed"
246
+ ].join(" "),
247
+ children: "Previous"
248
+ }
249
+ ),
250
+ /* @__PURE__ */ jsx(
251
+ "button",
252
+ {
253
+ type: "button",
254
+ onClick: () => table.nextPage(),
255
+ disabled: !canNext,
256
+ className: [
257
+ "px-2.5 py-1 rounded-md text-xs font-medium transition-colors",
258
+ canNext ? "bg-bg-secondary text-txt-primary hover:bg-bg-tertiary cursor-pointer" : "bg-bg-secondary text-txt-disabled cursor-not-allowed"
259
+ ].join(" "),
260
+ children: "Next"
261
+ }
262
+ )
263
+ ] })
264
+ ] })
265
+ ] });
266
+ };
4
267
  export {
5
- DATA_TABLE_STATUS,
6
- DATA_TABLE_VERSION
268
+ DataTable
7
269
  };
8
270
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/table/index.ts"],"sourcesContent":["/**\n * open-mcp-app-ui/table\n *\n * High-performance virtualized data table built on TanStack Table + TanStack Virtual.\n * This is an optional import — apps that don't use it pay zero bundle cost.\n *\n * Phase 2: Placeholder export. Implementation coming soon.\n *\n * Planned usage:\n * import { DataTable } from \"open-mcp-app-ui/table\";\n *\n * <DataTable\n * columns={[{ accessorKey: \"name\", header: \"Name\", sortable: true }]}\n * data={items}\n * sortable\n * virtualized\n * />\n */\n\nexport const DATA_TABLE_VERSION = \"0.0.1\";\nexport const DATA_TABLE_STATUS = \"planned\" as const;\n"],"mappings":";AAmBO,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB;","names":[]}
1
+ {"version":3,"sources":["../../src/table/DataTable.tsx"],"sourcesContent":["/**\n * DataTable — High-performance virtualized data table.\n *\n * Built on TanStack Table v8 for headless data logic (sorting, filtering,\n * pagination) and TanStack Virtual for row virtualization. This gives full\n * control over rendering and styling while handling thousands of rows smoothly.\n *\n * Styled exclusively with MCP Apps spec CSS variables via the SDK's\n * Tailwind theme mapping. Automatically adapts to display modes when\n * used inside an <AppLayout>.\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 { DataTable } from \"open-mcp-app-ui/table\";\n */\n\nimport {\n useState,\n useMemo,\n useRef,\n useCallback,\n type ReactNode,\n type CSSProperties,\n} from \"react\";\nimport {\n useReactTable,\n getCoreRowModel,\n getSortedRowModel,\n getFilteredRowModel,\n getPaginationRowModel,\n flexRender,\n type ColumnDef,\n type SortingState,\n type Row,\n type HeaderGroup,\n type Header,\n type Cell,\n} from \"@tanstack/react-table\";\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DataTableProps<T> {\n /**\n * Column definitions following TanStack Table's ColumnDef format.\n * Use `accessorKey` for simple property access, `header` for the\n * column label, and `cell` for custom cell rendering.\n */\n columns: ColumnDef<T, unknown>[];\n /** Row data array. */\n data: T[];\n /** Enable client-side column sorting (click headers to toggle). */\n sortable?: boolean;\n /** Enable a global text filter input above the table. */\n filterable?: boolean;\n /** Enable pagination with this many rows per page. Omit for no pagination. */\n pageSize?: number;\n /**\n * Enable row virtualization for large datasets. Renders only visible\n * rows plus a small overscan buffer. Recommended for >100 rows.\n */\n virtualized?: boolean;\n /** Keep the header row visible while scrolling. Defaults to true. */\n stickyHeader?: boolean;\n /** Content shown when the data array is empty. */\n emptyMessage?: ReactNode;\n /** Called when a row is clicked. Receives the row's original data and index. */\n onRowClick?: (row: { original: T; index: number }) => void;\n /** Show a loading skeleton instead of data rows. */\n loading?: boolean;\n /** Reduce row height for tighter layouts (e.g. inline display mode). */\n compact?: boolean;\n /** Additional CSS classes on the outermost wrapper. */\n className?: string;\n /** Placeholder text for the filter input. */\n filterPlaceholder?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Estimated row heights used by the virtualizer. */\nconst ROW_HEIGHT_COMPACT = 32;\nconst ROW_HEIGHT_DEFAULT = 40;\n\n/** Number of rows rendered beyond the visible area for smooth scrolling. */\nconst OVERSCAN_COUNT = 10;\n\n/** Number of skeleton rows shown while loading. */\nconst SKELETON_ROW_COUNT = 8;\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\n/**\n * Sort direction indicator displayed next to sortable column headers.\n * Shows an up arrow, down arrow, or a subtle bi-directional hint.\n */\nconst SortIndicator = ({ direction }: { direction: false | \"asc\" | \"desc\" }) => {\n if (direction === \"asc\") {\n return <span className=\"text-txt-primary ml-1\" aria-label=\"sorted ascending\">↑</span>;\n }\n if (direction === \"desc\") {\n return <span className=\"text-txt-primary ml-1\" aria-label=\"sorted descending\">↓</span>;\n }\n return <span className=\"text-txt-tertiary ml-1 opacity-0 group-hover:opacity-100 transition-opacity\">↕</span>;\n};\n\n/**\n * Renders a single header cell with optional sort interaction.\n * Wraps in a button when the column is sortable for keyboard accessibility.\n */\nconst HeaderCell = <T,>({\n header,\n sortable,\n}: {\n header: Header<T, unknown>;\n sortable: boolean;\n}) => {\n const canSort = sortable && header.column.getCanSort();\n const sorted = header.column.getIsSorted();\n\n const content = header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext());\n\n if (canSort) {\n return (\n <button\n type=\"button\"\n className=\"group inline-flex items-center gap-0.5 cursor-pointer select-none w-full text-left\"\n onClick={header.column.getToggleSortingHandler()}\n aria-sort={sorted === \"asc\" ? \"ascending\" : sorted === \"desc\" ? \"descending\" : \"none\"}\n >\n {content}\n <SortIndicator direction={sorted} />\n </button>\n );\n }\n\n return <>{content}</>;\n};\n\n/**\n * Skeleton row placeholder displayed during the loading state.\n * Uses subtle animated pulse bars that inherit the theme.\n */\nconst SkeletonRow = ({ columnCount, compact }: { columnCount: number; compact: boolean }) => (\n <tr className=\"border-b border-bdr-secondary\">\n {Array.from({ length: columnCount }, (_, i) => (\n <td\n key={i}\n className={compact ? \"px-3 py-1.5\" : \"px-3 py-2.5\"}\n >\n <div className=\"h-3.5 bg-bg-secondary rounded-sm animate-pulse\" style={{ width: `${50 + Math.random() * 40}%` }} />\n </td>\n ))}\n </tr>\n);\n\n// ---------------------------------------------------------------------------\n// DataTable\n// ---------------------------------------------------------------------------\n\n/**\n * High-performance data table with sorting, filtering, pagination,\n * and row virtualization.\n *\n * All visual aspects map to MCP Apps spec CSS variables for automatic\n * host theming. The table adapts to display modes when inside <AppLayout>.\n *\n * @example\n * ```tsx\n * import { DataTable } from \"open-mcp-app-ui/table\";\n *\n * const columns = [\n * { accessorKey: \"name\", header: \"Name\" },\n * { accessorKey: \"status\", header: \"Status\" },\n * { accessorKey: \"date\", header: \"Created\" },\n * ];\n *\n * <DataTable columns={columns} data={items} sortable filterable />\n * ```\n */\nexport const DataTable = <T,>({\n columns,\n data,\n sortable = false,\n filterable = false,\n pageSize,\n virtualized = false,\n stickyHeader = true,\n emptyMessage = \"No data\",\n onRowClick,\n loading = false,\n compact = false,\n className = \"\",\n filterPlaceholder = \"Filter...\",\n}: DataTableProps<T>) => {\n // -------------------------------------------------------------------------\n // State\n // -------------------------------------------------------------------------\n\n const [sorting, setSorting] = useState<SortingState>([]);\n const [globalFilter, setGlobalFilter] = useState(\"\");\n\n // -------------------------------------------------------------------------\n // Table instance\n // -------------------------------------------------------------------------\n\n const table = useReactTable({\n data,\n columns,\n state: {\n sorting,\n globalFilter,\n },\n onSortingChange: setSorting,\n onGlobalFilterChange: setGlobalFilter,\n getCoreRowModel: getCoreRowModel(),\n ...(sortable ? { getSortedRowModel: getSortedRowModel() } : {}),\n ...(filterable ? { getFilteredRowModel: getFilteredRowModel() } : {}),\n ...(pageSize ? { getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize } } } : {}),\n });\n\n const { rows } = table.getRowModel();\n const headerGroups = table.getHeaderGroups();\n\n // -------------------------------------------------------------------------\n // Virtualization\n // -------------------------------------------------------------------------\n\n const scrollRef = useRef<HTMLDivElement>(null);\n const rowHeight = compact ? ROW_HEIGHT_COMPACT : ROW_HEIGHT_DEFAULT;\n\n const virtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => rowHeight,\n overscan: OVERSCAN_COUNT,\n enabled: virtualized,\n });\n\n const virtualRows = virtualized ? virtualizer.getVirtualItems() : null;\n const totalHeight = virtualized ? virtualizer.getTotalSize() : 0;\n\n // -------------------------------------------------------------------------\n // Pagination helpers\n // -------------------------------------------------------------------------\n\n const hasPagination = pageSize != null && !loading;\n const pageCount = table.getPageCount();\n const currentPage = table.getState().pagination.pageIndex;\n const canPrev = table.getCanPreviousPage();\n const canNext = table.getCanNextPage();\n\n // -------------------------------------------------------------------------\n // Row rendering\n // -------------------------------------------------------------------------\n\n /**\n * Renders a single data row. Shared between virtualized and\n * non-virtualized modes to keep styling consistent.\n */\n const renderRow = useCallback(\n (row: Row<T>, style?: CSSProperties) => {\n const isClickable = onRowClick != null;\n return (\n <tr\n key={row.id}\n className={[\n \"border-b border-bdr-secondary transition-colors\",\n isClickable ? \"cursor-pointer hover:bg-bg-secondary\" : \"\",\n ].join(\" \")}\n style={style}\n onClick={isClickable ? () => onRowClick({ original: row.original, index: row.index }) : undefined}\n role={isClickable ? \"button\" : undefined}\n tabIndex={isClickable ? 0 : undefined}\n onKeyDown={isClickable ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onRowClick({ original: row.original, index: row.index });\n }\n } : undefined}\n >\n {row.getVisibleCells().map((cell: Cell<T, unknown>) => (\n <td\n key={cell.id}\n className={[\n \"text-txt-primary\",\n compact ? \"px-3 py-1.5 text-xs\" : \"px-3 py-2.5 text-sm\",\n ].join(\" \")}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </td>\n ))}\n </tr>\n );\n },\n [onRowClick, compact]\n );\n\n // -------------------------------------------------------------------------\n // Memoized body content\n // -------------------------------------------------------------------------\n\n const bodyContent = useMemo(() => {\n if (loading) {\n return Array.from({ length: SKELETON_ROW_COUNT }, (_, i) => (\n <SkeletonRow key={i} columnCount={columns.length} compact={compact} />\n ));\n }\n\n if (rows.length === 0) {\n return (\n <tr>\n <td\n colSpan={columns.length}\n className=\"text-center text-txt-tertiary py-8 text-sm\"\n >\n {emptyMessage}\n </td>\n </tr>\n );\n }\n\n // Virtualized rendering — only render visible rows with absolute positioning\n if (virtualized && virtualRows) {\n return (\n <>\n {/* Spacer for rows above the visible window */}\n {virtualRows.length > 0 && virtualRows[0].start > 0 && (\n <tr style={{ height: virtualRows[0].start }}>\n <td colSpan={columns.length} />\n </tr>\n )}\n {virtualRows.map((vRow) => {\n const row = rows[vRow.index];\n return renderRow(row, { height: `${vRow.size}px` });\n })}\n {/* Spacer for rows below the visible window */}\n {virtualRows.length > 0 && (\n <tr style={{ height: Math.max(0, totalHeight - (virtualRows[virtualRows.length - 1]?.end ?? 0)) }}>\n <td colSpan={columns.length} />\n </tr>\n )}\n </>\n );\n }\n\n // Standard rendering — all rows\n return rows.map((row) => renderRow(row));\n }, [loading, rows, columns.length, compact, emptyMessage, virtualized, virtualRows, totalHeight, renderRow]);\n\n // -------------------------------------------------------------------------\n // Render\n // -------------------------------------------------------------------------\n\n return (\n <div className={`flex flex-col gap-2 ${className}`}>\n {/* Global filter input */}\n {filterable && !loading && (\n <div className=\"flex items-center\">\n <input\n type=\"text\"\n value={globalFilter}\n onChange={(e) => setGlobalFilter(e.target.value)}\n placeholder={filterPlaceholder}\n className={[\n \"w-full max-w-xs text-sm px-3 py-1.5 rounded-md\",\n \"bg-bg-primary text-txt-primary placeholder:text-txt-tertiary\",\n \"border border-bdr-secondary\",\n \"outline-none focus:outline focus:outline-2 focus:outline-offset-0 focus:outline-ring-primary\",\n \"transition-colors\",\n ].join(\" \")}\n />\n </div>\n )}\n\n {/* Scrollable table container */}\n <div\n ref={scrollRef}\n className={[\n \"overflow-auto rounded-lg\",\n \"border border-bdr-primary\",\n virtualized ? \"max-h-[600px]\" : \"\",\n ].join(\" \")}\n >\n <table className=\"w-full border-collapse text-left\">\n {/* Table header */}\n <thead\n className={[\n \"bg-bg-secondary\",\n stickyHeader ? \"sticky top-0 z-10\" : \"\",\n ].join(\" \")}\n >\n {headerGroups.map((hg: HeaderGroup<T>) => (\n <tr key={hg.id}>\n {hg.headers.map((header: Header<T, unknown>) => (\n <th\n key={header.id}\n className={[\n \"text-txt-secondary font-medium\",\n compact ? \"px-3 py-1.5 text-xs\" : \"px-3 py-2.5 text-xs\",\n \"border-b border-bdr-primary\",\n \"whitespace-nowrap\",\n ].join(\" \")}\n style={{ width: header.getSize() !== 150 ? header.getSize() : undefined }}\n >\n <HeaderCell header={header} sortable={sortable} />\n </th>\n ))}\n </tr>\n ))}\n </thead>\n\n {/* Table body */}\n <tbody className=\"bg-bg-primary\">\n {bodyContent}\n </tbody>\n </table>\n </div>\n\n {/* Pagination controls */}\n {hasPagination && pageCount > 1 && (\n <div className=\"flex items-center justify-between text-sm text-txt-secondary\">\n <span>\n Page {currentPage + 1} of {pageCount}\n </span>\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={() => table.previousPage()}\n disabled={!canPrev}\n className={[\n \"px-2.5 py-1 rounded-md text-xs font-medium transition-colors\",\n canPrev\n ? \"bg-bg-secondary text-txt-primary hover:bg-bg-tertiary cursor-pointer\"\n : \"bg-bg-secondary text-txt-disabled cursor-not-allowed\",\n ].join(\" \")}\n >\n Previous\n </button>\n <button\n type=\"button\"\n onClick={() => table.nextPage()}\n disabled={!canNext}\n className={[\n \"px-2.5 py-1 rounded-md text-xs font-medium transition-colors\",\n canNext\n ? \"bg-bg-secondary text-txt-primary hover:bg-bg-tertiary cursor-pointer\"\n : \"bg-bg-secondary text-txt-disabled cursor-not-allowed\",\n ].join(\" \")}\n >\n Next\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-mcp-app-ui",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "UI component library for MCP Apps, built on the MCP Apps spec",
5
5
  "type": "module",
6
6
  "exports": {
@@ -71,6 +71,12 @@
71
71
  "url": "https://github.com/creature-run/open-mcp-app/issues"
72
72
  },
73
73
  "dependencies": {
74
+ "@milkdown/kit": "^7.18.0",
75
+ "@milkdown/plugin-listener": "^7.18.0",
76
+ "@milkdown/react": "^7.18.0",
77
+ "@milkdown/utils": "^7.18.0",
78
+ "@tanstack/react-table": "^8.21.3",
79
+ "@tanstack/react-virtual": "^3.13.18",
74
80
  "react-syntax-highlighter": "^16.1.0"
75
81
  }
76
82
  }