@neuronection/assistant-ui 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @neuronection/assistant-ui
2
2
 
3
+ ## 0.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`7537028`](https://github.com/neuronection/assistant-ui/commit/75370281e944b43b295ff6a315ff4fb7112d471f) Thanks [@constLiakos](https://github.com/constLiakos)! - Combobox and ComboboxMulti gain `allowCreate` (+ optional `createLabel`):
8
+ when the search term matches no option, an `Add "term"` row is offered
9
+ (keyboard included) and picking it reports the raw trimmed term — for
10
+ free-text entry into taxonomy-backed pickers.
11
+
12
+ - [`2b7e756`](https://github.com/neuronection/assistant-ui/commit/2b7e7566529af6e43528b63908ab590c04085864) Thanks [@constLiakos](https://github.com/constLiakos)! - RichTextEditor becomes hostable by full apps: `extensions` is now a **full
13
+ override** of the built-in `[StarterKit, Markdown]` set (apps declare their
14
+ complete extension set), and three composition props land — `contentClassName`
15
+ (merged onto the editable region), `onReady` (editor instance on create,
16
+ `null` on destroy — the app handle for commands and event wiring), and
17
+ `headingLevels` (one toggle per level; `labels.heading` is now
18
+ `(level) => string`, default `Heading ${level}`). History buttons now reflect
19
+ undo/redo availability and disable while there is nothing to undo/redo.
20
+
3
21
  ## 0.21.0
4
22
 
5
23
  ### Minor Changes
package/README.md CHANGED
@@ -76,7 +76,7 @@ Visual reference: [gallery](https://neuronection.github.io/assistant-ui/).
76
76
  | [`tooltip`](./docs/components/tooltip.md) | `Tooltip` + parts, `InfoTooltip` | [stories](https://neuronection.github.io/assistant-ui/?story=overlay--tooltips) |
77
77
  | [`wizard`](./docs/components/wizard.md) | `Wizard` (steps config + `renderStep`, per-step validation gates, modal/drawer variants), `Stepper` (dots/labels) | [stories](https://neuronection.github.io/assistant-ui/?story=wizard--modal-wizard) |
78
78
  | [`input`](./docs/components/input.md) / [`textarea`](./docs/components/textarea.md) / [`search-input`](./docs/components/search-input.md) / [`expandable-search`](./docs/components/expandable-search.md) | `Input` (label/hint/error wiring) · `Textarea` · `SearchInput` · `ExpandableSearch` | [stories](https://neuronection.github.io/assistant-ui/?story=misc--inputs) |
79
- | [`rich-text-editor`](./docs/components/rich-text-editor.md) | `RichTextEditor` — controlled tiptap markdown editor: configurable toolbar groups + `toolbarExtra` slot, label/icon props, caret-preserving external sync | [stories](https://neuronection.github.io/assistant-ui/?story=rich-text-editor--default) |
79
+ | [`rich-text-editor`](./docs/components/rich-text-editor.md) | `RichTextEditor` — controlled tiptap markdown editor: full-override `extensions`, configurable toolbar groups + per-level headings + `toolbarExtra` slot, `onReady`/`contentClassName` composition props, label/icon props, caret-preserving external sync | [stories](https://neuronection.github.io/assistant-ui/?story=rich-text-editor--default) |
80
80
  | [`dictation`](./docs/components/dictation.md) / [`ai-text-transform`](./docs/components/ai-text-transform.md) | `useDictation` + `DictationButton`/`DictationStrip` (transport-injected STT) · `useAiTextTransform` (streaming transform state machine) | [stories](https://neuronection.github.io/assistant-ui/?story=dictation--dictation) |
81
81
  | [`text-diff-view`](./docs/components/text-diff-view.md) | `TextDiffView` — side-by-side diff with word-level highlights, line numbers, change navigation, folds, virtualization (+ exported `computeLineDiff`/`wordSegments`) | [stories](https://neuronection.github.io/assistant-ui/?story=text-diff-view--default) |
82
82
  | [`chip-input`](./docs/components/chip-input.md) / [`chip-list`](./docs/components/chip-list.md) | `ChipInput` (Enter/comma commit, paste-splits, Backspace-removes) · `ChipList` (variant pills, clickable, removable) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--chip-editing) |
@@ -13,13 +13,15 @@ var DEFAULT_TOOLBAR = [
13
13
  "list",
14
14
  "quote"
15
15
  ];
16
+ var DEFAULT_HEADING_LEVELS = [2];
17
+ var DEFAULT_EXTENSIONS = [StarterKit, Markdown];
16
18
  var DEFAULT_LABELS = {
17
19
  toolbar: "Formatting",
18
20
  bold: "Bold",
19
21
  italic: "Italic",
20
22
  strike: "Strikethrough",
21
23
  code: "Code",
22
- heading: "Heading",
24
+ heading: (level) => `Heading ${level}`,
23
25
  bulletList: "Bulleted list",
24
26
  orderedList: "Numbered list",
25
27
  blockquote: "Quote",
@@ -60,6 +62,9 @@ var RichTextEditor = React.forwardRef(
60
62
  toolbar = DEFAULT_TOOLBAR,
61
63
  toolbarExtra,
62
64
  extensions,
65
+ contentClassName,
66
+ onReady,
67
+ headingLevels,
63
68
  parseMarkdown,
64
69
  serializeMarkdown,
65
70
  labels,
@@ -68,9 +73,16 @@ var RichTextEditor = React.forwardRef(
68
73
  }, ref) {
69
74
  const mergedLabels = { ...DEFAULT_LABELS, ...labels };
70
75
  const mergedIcons = { ...DEFAULT_ICONS, ...icons };
76
+ const resolvedExtensions = React.useMemo(
77
+ () => extensions ?? DEFAULT_EXTENSIONS,
78
+ [extensions]
79
+ );
80
+ const levels = headingLevels ?? DEFAULT_HEADING_LEVELS;
71
81
  const lastEmittedRef = React.useRef(value);
72
82
  const onValueChangeRef = React.useRef(onValueChange);
73
83
  onValueChangeRef.current = onValueChange;
84
+ const onReadyRef = React.useRef(onReady);
85
+ onReadyRef.current = onReady;
74
86
  const hooksRef = React.useRef({ parseMarkdown, serializeMarkdown });
75
87
  hooksRef.current = { parseMarkdown, serializeMarkdown };
76
88
  const [, bump] = React.useReducer((count) => count + 1, 0);
@@ -84,17 +96,17 @@ var RichTextEditor = React.forwardRef(
84
96
  );
85
97
  const editor = useEditor(
86
98
  {
87
- extensions: React.useMemo(
88
- () => [StarterKit, Markdown, ...extensions ?? []],
89
- [extensions]
90
- ),
99
+ extensions: resolvedExtensions,
91
100
  content: toDocument(value),
92
101
  editable: !disabled,
93
102
  immediatelyRender: false,
94
103
  editorProps: {
95
104
  attributes: {
96
105
  "aria-label": ariaLabel,
97
- class: "as-rich-text-editor__content min-h-24 w-full bg-transparent px-3 py-2 text-sm text-[var(--as-fg)] outline-none"
106
+ class: cn(
107
+ "as-rich-text-editor__content min-h-24 w-full bg-transparent px-3 py-2 text-sm text-[var(--as-fg)] outline-none",
108
+ contentClassName
109
+ )
98
110
  }
99
111
  },
100
112
  onUpdate: ({ editor: current }) => {
@@ -105,6 +117,15 @@ var RichTextEditor = React.forwardRef(
105
117
  }
106
118
  );
107
119
  const isMounted = editor !== null;
120
+ React.useEffect(() => {
121
+ if (editor === null) {
122
+ return;
123
+ }
124
+ onReadyRef.current?.(editor);
125
+ return () => {
126
+ onReadyRef.current?.(null);
127
+ };
128
+ }, [editor]);
108
129
  React.useEffect(() => {
109
130
  if (!isMounted || editor === null) {
110
131
  return;
@@ -139,26 +160,47 @@ var RichTextEditor = React.forwardRef(
139
160
  lastEmittedRef.current = value;
140
161
  }, [editor, value, toApp, toDocument]);
141
162
  const groups = toolbar === false ? [] : toolbar ?? DEFAULT_TOOLBAR;
142
- const renderButton = (key, group, onClick, active) => {
163
+ const buttonClass = (active) => cn(
164
+ "rounded p-1.5 text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50",
165
+ active && "bg-[var(--as-surface-active)] text-[var(--as-fg)]"
166
+ );
167
+ const renderButton = (key, group, onClick, active, buttonDisabled = disabled) => {
143
168
  const Icon = mergedIcons[key];
169
+ const label = mergedLabels[key];
144
170
  return /* @__PURE__ */ jsx(
145
171
  "button",
146
172
  {
147
173
  type: "button",
148
- "aria-label": mergedLabels[key],
174
+ "aria-label": label,
149
175
  "aria-pressed": active,
150
- title: mergedLabels[key],
151
- disabled,
152
- className: cn(
153
- "rounded p-1.5 text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50",
154
- active && "bg-[var(--as-surface-active)] text-[var(--as-fg)]"
155
- ),
176
+ title: label,
177
+ disabled: buttonDisabled,
178
+ className: buttonClass(active),
156
179
  onClick,
157
180
  children: /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true })
158
181
  },
159
182
  `${group}-${key}`
160
183
  );
161
184
  };
185
+ const historyDisabled = (can) => disabled || editor === null || editor.can()[can]() !== true;
186
+ const renderHeadingButton = (level) => {
187
+ const Icon = mergedIcons.heading;
188
+ const label = mergedLabels.heading(level);
189
+ return /* @__PURE__ */ jsx(
190
+ "button",
191
+ {
192
+ type: "button",
193
+ "aria-label": label,
194
+ "aria-pressed": editor?.isActive("heading", { level }) === true,
195
+ title: label,
196
+ disabled,
197
+ className: buttonClass(editor?.isActive("heading", { level }) === true),
198
+ onClick: () => editor?.chain().focus().toggleHeading({ level }).run(),
199
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true })
200
+ },
201
+ `heading-${level}`
202
+ );
203
+ };
162
204
  return /* @__PURE__ */ jsxs(
163
205
  "div",
164
206
  {
@@ -181,20 +223,17 @@ var RichTextEditor = React.forwardRef(
181
223
  "undo",
182
224
  "history",
183
225
  () => editor?.chain().focus().undo().run(),
184
- false
226
+ false,
227
+ historyDisabled("undo")
185
228
  ) : null,
186
229
  groups.includes("history") ? renderButton(
187
230
  "redo",
188
231
  "history",
189
232
  () => editor?.chain().focus().redo().run(),
190
- false
191
- ) : null,
192
- groups.includes("heading") ? renderButton(
193
- "heading",
194
- "heading",
195
- () => editor?.chain().focus().toggleHeading({ level: 2 }).run(),
196
- editor?.isActive("heading", { level: 2 }) === true
233
+ false,
234
+ historyDisabled("redo")
197
235
  ) : null,
236
+ groups.includes("heading") ? levels.map((level) => renderHeadingButton(level)) : null,
198
237
  groups.includes("format") ? renderButton(
199
238
  "bold",
200
239
  "format",
@@ -249,5 +288,5 @@ var RichTextEditor = React.forwardRef(
249
288
  );
250
289
 
251
290
  export { RichTextEditor };
252
- //# sourceMappingURL=chunk-XUDCCQRN.js.map
253
- //# sourceMappingURL=chunk-XUDCCQRN.js.map
291
+ //# sourceMappingURL=chunk-4UJKXTWY.js.map
292
+ //# sourceMappingURL=chunk-4UJKXTWY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/rich-text-editor/RichTextEditor.tsx"],"names":["RichTextEditor"],"mappings":"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","file":"chunk-4UJKXTWY.js","sourcesContent":["import * as React from 'react'\nimport { Editor, EditorContent, useEditor, type Extensions } from '@tiptap/react'\nimport StarterKit from '@tiptap/starter-kit'\nimport {\n Bold,\n Code,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n type LucideIcon,\n} from 'lucide-react'\nimport { Markdown } from 'tiptap-markdown'\n\nimport { cn } from '../../lib/utils'\n\nexport type RichTextToolbarGroup = 'history' | 'heading' | 'format' | 'list' | 'quote'\n\nexport type RichTextHeadingLevel = 1 | 2 | 3 | 4\n\nconst DEFAULT_TOOLBAR: RichTextToolbarGroup[] = [\n 'history',\n 'heading',\n 'format',\n 'list',\n 'quote',\n]\n\nconst DEFAULT_HEADING_LEVELS: RichTextHeadingLevel[] = [2]\n\nconst DEFAULT_EXTENSIONS: Extensions = [StarterKit, Markdown]\n\nexport interface RichTextEditorLabels {\n toolbar?: string\n bold?: string\n italic?: string\n strike?: string\n code?: string\n heading?: (level: number) => string\n bulletList?: string\n orderedList?: string\n blockquote?: string\n undo?: string\n redo?: string\n}\n\nexport interface RichTextEditorIcons {\n bold?: LucideIcon\n italic?: LucideIcon\n strike?: LucideIcon\n code?: LucideIcon\n heading?: LucideIcon\n bulletList?: LucideIcon\n orderedList?: LucideIcon\n blockquote?: LucideIcon\n undo?: LucideIcon\n redo?: LucideIcon\n}\n\nconst DEFAULT_LABELS: Required<RichTextEditorLabels> = {\n toolbar: 'Formatting',\n bold: 'Bold',\n italic: 'Italic',\n strike: 'Strikethrough',\n code: 'Code',\n heading: (level) => `Heading ${level}`,\n bulletList: 'Bulleted list',\n orderedList: 'Numbered list',\n blockquote: 'Quote',\n undo: 'Undo',\n redo: 'Redo',\n}\n\nconst DEFAULT_ICONS: Required<RichTextEditorIcons> = {\n bold: Bold,\n italic: Italic,\n strike: Strikethrough,\n code: Code,\n heading: Heading2,\n bulletList: List,\n orderedList: ListOrdered,\n blockquote: Quote,\n undo: Undo2,\n redo: Redo2,\n}\n\ntype FormatKey =\n | 'bold'\n | 'italic'\n | 'strike'\n | 'code'\n | 'bulletList'\n | 'orderedList'\n | 'blockquote'\n | 'undo'\n | 'redo'\n\nexport interface RichTextEditorProps {\n value: string\n onValueChange: (markdown: string) => void\n ariaLabel: string\n disabled?: boolean\n toolbar?: RichTextToolbarGroup[] | false\n /** App-owned controls at the end of the toolbar; render-prop form receives the editor. */\n toolbarExtra?: React.ReactNode | ((editor: Editor | null) => React.ReactNode)\n /** Full override of the document extension set; default is `[StarterKit, Markdown]`. Apps declare their complete set. */\n extensions?: Extensions\n /** Extra classes for the editable region (merged after the built-ins via tailwind-merge). */\n contentClassName?: string\n /** Called once when the editor instance is created and again with `null` on destroy/unmount. */\n onReady?: (editor: Editor | null) => void\n /** Heading toggles rendered for the `heading` toolbar group, one button per level. */\n headingLevels?: RichTextHeadingLevel[]\n /** Map incoming app-domain markdown before it reaches the document. */\n parseMarkdown?: (markdown: string) => string\n /** Map the serialized document before it is emitted / echo-compared. */\n serializeMarkdown?: (markdown: string) => string\n labels?: Partial<RichTextEditorLabels>\n icons?: Partial<RichTextEditorIcons>\n className?: string\n}\n\nfunction getMarkdown(editor: Editor): string {\n const storage = editor.storage as { markdown?: { getMarkdown?: () => string } }\n return storage.markdown?.getMarkdown?.() ?? ''\n}\n\nfunction applyExternalValue(editor: Editor, markdown: string): void {\n const hadFocus = editor.isFocused\n const caret = editor.state.selection.head\n editor.commands.setContent(markdown, { emitUpdate: false })\n if (hadFocus) {\n editor.commands.setTextSelection(Math.min(caret, editor.state.doc.content.size))\n editor.commands.focus()\n }\n}\n\nexport const RichTextEditor = React.forwardRef<HTMLDivElement, RichTextEditorProps>(\n function RichTextEditor(\n {\n value,\n onValueChange,\n ariaLabel,\n disabled = false,\n toolbar = DEFAULT_TOOLBAR,\n toolbarExtra,\n extensions,\n contentClassName,\n onReady,\n headingLevels,\n parseMarkdown,\n serializeMarkdown,\n labels,\n icons,\n className,\n },\n ref,\n ) {\n const mergedLabels = { ...DEFAULT_LABELS, ...labels }\n const mergedIcons = { ...DEFAULT_ICONS, ...icons }\n const resolvedExtensions = React.useMemo(\n () => extensions ?? DEFAULT_EXTENSIONS,\n [extensions],\n )\n const levels = headingLevels ?? DEFAULT_HEADING_LEVELS\n const lastEmittedRef = React.useRef<string>(value)\n const onValueChangeRef = React.useRef(onValueChange)\n onValueChangeRef.current = onValueChange\n const onReadyRef = React.useRef(onReady)\n onReadyRef.current = onReady\n const hooksRef = React.useRef({ parseMarkdown, serializeMarkdown })\n hooksRef.current = { parseMarkdown, serializeMarkdown }\n const [, bump] = React.useReducer((count: number) => count + 1, 0)\n\n const toDocument = React.useCallback(\n (markdown: string) => (parseMarkdown ? parseMarkdown(markdown) : markdown),\n [parseMarkdown],\n )\n const toApp = React.useCallback(\n (markdown: string) => (serializeMarkdown ? serializeMarkdown(markdown) : markdown),\n [serializeMarkdown],\n )\n\n const editor = useEditor(\n {\n extensions: resolvedExtensions,\n content: toDocument(value),\n editable: !disabled,\n immediatelyRender: false,\n editorProps: {\n attributes: {\n 'aria-label': ariaLabel,\n class: cn(\n 'as-rich-text-editor__content min-h-24 w-full bg-transparent px-3 py-2 text-sm text-[var(--as-fg)] outline-none',\n contentClassName,\n ),\n },\n },\n onUpdate: ({ editor: current }) => {\n const markdown = toApp(getMarkdown(current))\n lastEmittedRef.current = markdown\n onValueChangeRef.current(markdown)\n },\n },\n )\n\n const isMounted = editor !== null\n\n React.useEffect(() => {\n if (editor === null) {\n return\n }\n onReadyRef.current?.(editor)\n return () => {\n onReadyRef.current?.(null)\n }\n }, [editor])\n\n React.useEffect(() => {\n if (!isMounted || editor === null) {\n return\n }\n const handler = () => bump()\n editor.on('transaction', handler)\n editor.on('update', handler)\n return () => {\n editor.off('transaction', handler)\n editor.off('update', handler)\n }\n }, [editor, isMounted])\n\n React.useEffect(() => {\n if (editor === null || editor.isDestroyed) {\n return\n }\n editor.setEditable(!disabled)\n }, [editor, disabled])\n\n React.useEffect(() => {\n if (editor === null || editor.isDestroyed) {\n return\n }\n if (value === lastEmittedRef.current) {\n return\n }\n const current = toApp(getMarkdown(editor))\n if (value === current) {\n lastEmittedRef.current = value\n return\n }\n applyExternalValue(editor, toDocument(value))\n lastEmittedRef.current = value\n }, [editor, value, toApp, toDocument])\n\n const groups = toolbar === false ? [] : (toolbar ?? DEFAULT_TOOLBAR)\n\n const buttonClass = (active: boolean) =>\n cn(\n 'rounded p-1.5 text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--as-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50',\n active && 'bg-[var(--as-surface-active)] text-[var(--as-fg)]',\n )\n\n const renderButton = (\n key: FormatKey,\n group: RichTextToolbarGroup,\n onClick: () => void,\n active: boolean,\n buttonDisabled = disabled,\n ) => {\n const Icon = mergedIcons[key]\n const label = mergedLabels[key]\n return (\n <button\n key={`${group}-${key}`}\n type=\"button\"\n aria-label={label}\n aria-pressed={active}\n title={label}\n disabled={buttonDisabled}\n className={buttonClass(active)}\n onClick={onClick}\n >\n <Icon className=\"size-4\" aria-hidden />\n </button>\n )\n }\n\n const historyDisabled = (can: 'undo' | 'redo') =>\n disabled || editor === null || editor.can()[can]() !== true\n\n const renderHeadingButton = (level: RichTextHeadingLevel) => {\n const Icon = mergedIcons.heading\n const label = mergedLabels.heading(level)\n return (\n <button\n key={`heading-${level}`}\n type=\"button\"\n aria-label={label}\n aria-pressed={editor?.isActive('heading', { level }) === true}\n title={label}\n disabled={disabled}\n className={buttonClass(editor?.isActive('heading', { level }) === true)}\n onClick={() => editor?.chain().focus().toggleHeading({ level }).run()}\n >\n <Icon className=\"size-4\" aria-hidden />\n </button>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"rich-text-editor\"\n className={cn(\n 'w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] focus-within:border-[var(--as-focus-ring)]',\n disabled && 'opacity-50',\n className,\n )}\n >\n {groups.length > 0 || toolbarExtra ? (\n <div\n role=\"toolbar\"\n aria-label={mergedLabels.toolbar}\n className=\"flex flex-wrap items-center gap-0.5 border-b border-[var(--as-border)] p-1\"\n >\n {groups.includes('history')\n ? renderButton(\n 'undo',\n 'history',\n () => editor?.chain().focus().undo().run(),\n false,\n historyDisabled('undo'),\n )\n : null}\n {groups.includes('history')\n ? renderButton(\n 'redo',\n 'history',\n () => editor?.chain().focus().redo().run(),\n false,\n historyDisabled('redo'),\n )\n : null}\n {groups.includes('heading') ? levels.map((level) => renderHeadingButton(level)) : null}\n {groups.includes('format')\n ? renderButton(\n 'bold',\n 'format',\n () => editor?.chain().focus().toggleBold().run(),\n editor?.isActive('bold') === true,\n )\n : null}\n {groups.includes('format')\n ? renderButton(\n 'italic',\n 'format',\n () => editor?.chain().focus().toggleItalic().run(),\n editor?.isActive('italic') === true,\n )\n : null}\n {groups.includes('format')\n ? renderButton(\n 'strike',\n 'format',\n () => editor?.chain().focus().toggleStrike().run(),\n editor?.isActive('strike') === true,\n )\n : null}\n {groups.includes('format')\n ? renderButton(\n 'code',\n 'format',\n () => editor?.chain().focus().toggleCode().run(),\n editor?.isActive('code') === true,\n )\n : null}\n {groups.includes('list')\n ? renderButton(\n 'bulletList',\n 'list',\n () => editor?.chain().focus().toggleBulletList().run(),\n editor?.isActive('bulletList') === true,\n )\n : null}\n {groups.includes('list')\n ? renderButton(\n 'orderedList',\n 'list',\n () => editor?.chain().focus().toggleOrderedList().run(),\n editor?.isActive('orderedList') === true,\n )\n : null}\n {groups.includes('quote')\n ? renderButton(\n 'blockquote',\n 'quote',\n () => editor?.chain().focus().toggleBlockquote().run(),\n editor?.isActive('blockquote') === true,\n )\n : null}\n {typeof toolbarExtra === 'function' ? toolbarExtra(editor) : toolbarExtra}\n </div>\n ) : null}\n <EditorContent editor={editor} />\n </div>\n )\n },\n)\n"]}
@@ -0,0 +1,83 @@
1
+ import { cn } from './chunk-BLJWR4ZV.js';
2
+ import * as React from 'react';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ var SettingsShell = React.forwardRef(
6
+ function SettingsShell2({ nav, active, onNavigate, header, children, className, navClassName, navTestId }, ref) {
7
+ const HeaderIcon = header?.icon;
8
+ return /* @__PURE__ */ jsxs(
9
+ "div",
10
+ {
11
+ ref,
12
+ "data-as": "settings-shell",
13
+ className: cn("grid grid-cols-1 gap-8 lg:grid-cols-4", className),
14
+ children: [
15
+ /* @__PURE__ */ jsx(
16
+ "nav",
17
+ {
18
+ className: "lg:col-span-1",
19
+ "aria-label": "Settings sections",
20
+ "data-testid": navTestId,
21
+ children: /* @__PURE__ */ jsxs(
22
+ "div",
23
+ {
24
+ className: cn(
25
+ "space-y-1 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-3 lg:sticky lg:top-24",
26
+ navClassName
27
+ ),
28
+ children: [
29
+ header ? /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2.5 border-b border-[var(--as-border)] px-3 py-2", children: [
30
+ HeaderIcon ? /* @__PURE__ */ jsx(
31
+ HeaderIcon,
32
+ {
33
+ className: "size-5 shrink-0 text-[var(--as-primary)]",
34
+ "aria-hidden": true
35
+ }
36
+ ) : null,
37
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-bold text-[var(--as-fg)]", children: header.title })
38
+ ] }) : null,
39
+ nav.map(({ id, label, description, icon: Icon, trailing }) => {
40
+ const isActive = id === active;
41
+ return /* @__PURE__ */ jsxs(
42
+ "button",
43
+ {
44
+ type: "button",
45
+ "aria-current": isActive ? "page" : void 0,
46
+ onClick: () => onNavigate(id),
47
+ className: cn(
48
+ "flex w-full cursor-pointer items-start rounded-[var(--as-radius)] px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
49
+ isActive ? "bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-bold text-[var(--as-primary)]" : "font-medium text-[var(--as-fg)] hover:bg-[var(--as-muted)]"
50
+ ),
51
+ children: [
52
+ Icon ? /* @__PURE__ */ jsx(
53
+ Icon,
54
+ {
55
+ className: "mr-3 mt-0.5 size-4 shrink-0",
56
+ "aria-hidden": true
57
+ }
58
+ ) : null,
59
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
60
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: label }),
61
+ description ? /* @__PURE__ */ jsx("span", { className: "text-[11px] font-normal text-[var(--as-muted-fg)]", children: description }) : null
62
+ ] }),
63
+ trailing ? /* @__PURE__ */ jsx("span", { className: "ml-auto flex items-center self-center pl-2", children: trailing }) : null
64
+ ]
65
+ },
66
+ id
67
+ );
68
+ })
69
+ ]
70
+ }
71
+ )
72
+ }
73
+ ),
74
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 lg:col-span-3", children })
75
+ ]
76
+ }
77
+ );
78
+ }
79
+ );
80
+
81
+ export { SettingsShell };
82
+ //# sourceMappingURL=chunk-7KD4MG75.js.map
83
+ //# sourceMappingURL=chunk-7KD4MG75.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/settings-shell/SettingsShell.tsx"],"names":["SettingsShell"],"mappings":";;;;AAyBO,IAAM,aAAA,GAAsB,KAAA,CAAA,UAAA;AAAA,EACjC,SAASA,cAAAA,CACP,EAAE,GAAA,EAAK,MAAA,EAAQ,UAAA,EAAY,MAAA,EAAQ,QAAA,EAAU,SAAA,EAAW,YAAA,EAAc,SAAA,EAAU,EAChF,GAAA,EACA;AACA,IAAA,MAAM,aAAa,MAAA,EAAQ,IAAA;AAC3B,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,gBAAA;AAAA,QACR,SAAA,EAAW,EAAA,CAAG,uCAAA,EAAyC,SAAS,CAAA;AAAA,QAEhE,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,eAAA;AAAA,cACV,YAAA,EAAW,mBAAA;AAAA,cACX,aAAA,EAAa,SAAA;AAAA,cAEb,QAAA,kBAAA,IAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAW,EAAA;AAAA,oBACT,gIAAA;AAAA,oBACA;AAAA,mBACF;AAAA,kBAEC,QAAA,EAAA;AAAA,oBAAA,MAAA,mBACC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6EAAA,EACZ,QAAA,EAAA;AAAA,sBAAA,UAAA,mBACC,GAAA;AAAA,wBAAC,UAAA;AAAA,wBAAA;AAAA,0BACC,SAAA,EAAU,0CAAA;AAAA,0BACV,aAAA,EAAW;AAAA;AAAA,uBACb,GACE,IAAA;AAAA,sCACJ,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gDAAA,EACb,iBAAO,KAAA,EACV;AAAA,qBAAA,EACF,CAAA,GACE,IAAA;AAAA,oBACH,GAAA,CAAI,GAAA,CAAI,CAAC,EAAE,EAAA,EAAI,OAAO,WAAA,EAAa,IAAA,EAAM,IAAA,EAAM,QAAA,EAAS,KAAM;AAC7D,sBAAA,MAAM,WAAW,EAAA,KAAO,MAAA;AACxB,sBAAA,uBACE,IAAA;AAAA,wBAAC,QAAA;AAAA,wBAAA;AAAA,0BAEC,IAAA,EAAK,QAAA;AAAA,0BACL,cAAA,EAAc,WAAW,MAAA,GAAS,MAAA;AAAA,0BAClC,OAAA,EAAS,MAAM,UAAA,CAAW,EAAE,CAAA;AAAA,0BAC5B,SAAA,EAAW,EAAA;AAAA,4BACT,uNAAA;AAAA,4BACA,WACI,8FAAA,GACA;AAAA,2BACN;AAAA,0BAEC,QAAA,EAAA;AAAA,4BAAA,IAAA,mBACC,GAAA;AAAA,8BAAC,IAAA;AAAA,8BAAA;AAAA,gCACC,SAAA,EAAU,6BAAA;AAAA,gCACV,aAAA,EAAW;AAAA;AAAA,6BACb,GACE,IAAA;AAAA,4CACJ,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EACd,QAAA,EAAA;AAAA,8CAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,UAAA,EAAY,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,8BACjC,8BACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mDAAA,EACb,uBACH,CAAA,GACE;AAAA,6BAAA,EACN,CAAA;AAAA,4BACC,2BACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,4CAAA,EACb,oBACH,CAAA,GACE;AAAA;AAAA,yBAAA;AAAA,wBA7BC;AAAA,uBA8BP;AAAA,oBAEJ,CAAC;AAAA;AAAA;AAAA;AACH;AAAA,WACF;AAAA,0BACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAS;AAAA;AAAA;AAAA,KACnD;AAAA,EAEJ;AACF","file":"chunk-7KD4MG75.js","sourcesContent":["import * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '../../lib/utils'\n\nexport interface SettingsNavItem {\n id: string\n label: string\n description?: string\n icon?: LucideIcon\n /** Optional trailing node (status dot, count, badge) at the row edge. */\n trailing?: React.ReactNode\n}\n\nexport interface SettingsShellProps {\n nav: SettingsNavItem[]\n active: string\n onNavigate: (id: string) => void\n header?: { icon?: LucideIcon; title: string }\n children: React.ReactNode\n className?: string\n navClassName?: string\n /** Test hook: data-testid for the `<nav>` element. */\n navTestId?: string\n}\n\nexport const SettingsShell = React.forwardRef<HTMLDivElement, SettingsShellProps>(\n function SettingsShell(\n { nav, active, onNavigate, header, children, className, navClassName, navTestId },\n ref,\n ) {\n const HeaderIcon = header?.icon\n return (\n <div\n ref={ref}\n data-as=\"settings-shell\"\n className={cn('grid grid-cols-1 gap-8 lg:grid-cols-4', className)}\n >\n <nav\n className=\"lg:col-span-1\"\n aria-label=\"Settings sections\"\n data-testid={navTestId}\n >\n <div\n className={cn(\n 'space-y-1 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-3 lg:sticky lg:top-24',\n navClassName,\n )}\n >\n {header ? (\n <div className=\"mb-2 flex items-center gap-2.5 border-b border-[var(--as-border)] px-3 py-2\">\n {HeaderIcon ? (\n <HeaderIcon\n className=\"size-5 shrink-0 text-[var(--as-primary)]\"\n aria-hidden\n />\n ) : null}\n <span className=\"truncate text-sm font-bold text-[var(--as-fg)]\">\n {header.title}\n </span>\n </div>\n ) : null}\n {nav.map(({ id, label, description, icon: Icon, trailing }) => {\n const isActive = id === active\n return (\n <button\n key={id}\n type=\"button\"\n aria-current={isActive ? 'page' : undefined}\n onClick={() => onNavigate(id)}\n className={cn(\n 'flex w-full cursor-pointer items-start rounded-[var(--as-radius)] px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n isActive\n ? 'bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-bold text-[var(--as-primary)]'\n : 'font-medium text-[var(--as-fg)] hover:bg-[var(--as-muted)]',\n )}\n >\n {Icon ? (\n <Icon\n className=\"mr-3 mt-0.5 size-4 shrink-0\"\n aria-hidden\n />\n ) : null}\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{label}</span>\n {description ? (\n <span className=\"text-[11px] font-normal text-[var(--as-muted-fg)]\">\n {description}\n </span>\n ) : null}\n </span>\n {trailing ? (\n <span className=\"ml-auto flex items-center self-center pl-2\">\n {trailing}\n </span>\n ) : null}\n </button>\n )\n })}\n </div>\n </nav>\n <div className=\"min-w-0 lg:col-span-3\">{children}</div>\n </div>\n )\n },\n)\n"]}
@@ -1,5 +1,5 @@
1
1
  import { CapabilityChips } from './chunk-G3DWN2DJ.js';
2
- import { ModelPicker } from './chunk-SVQK7TXU.js';
2
+ import { ModelPicker } from './chunk-S4WHBQJ2.js';
3
3
  import { beautifyId } from './chunk-B2G66OSQ.js';
4
4
  import { Modal, ModalContent, ModalHeader, ModalTitle, ModalFooter } from './chunk-Y7BKBG57.js';
5
5
  import { Button } from './chunk-GTXGTQ6F.js';
@@ -616,5 +616,5 @@ var ModelRegistry = React.forwardRef(
616
616
  );
617
617
 
618
618
  export { ModelRegistry };
619
- //# sourceMappingURL=chunk-RBEPQY2A.js.map
620
- //# sourceMappingURL=chunk-RBEPQY2A.js.map
619
+ //# sourceMappingURL=chunk-BHHDCY2B.js.map
620
+ //# sourceMappingURL=chunk-BHHDCY2B.js.map