@neuronection/assistant-ui 0.13.1 → 0.15.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +2 -0
  3. package/dist/chip-input.d.ts +2 -0
  4. package/dist/chip-input.js +1 -1
  5. package/dist/chunk-535TM7SK.js +119 -0
  6. package/dist/chunk-535TM7SK.js.map +1 -0
  7. package/dist/chunk-EZTQMJ5I.js +428 -0
  8. package/dist/chunk-EZTQMJ5I.js.map +1 -0
  9. package/dist/{chunk-TPLQL62V.js → chunk-HIRR2F4R.js} +42 -5
  10. package/dist/chunk-HIRR2F4R.js.map +1 -0
  11. package/dist/{chunk-LY2B3UJH.js → chunk-OGQF4XJB.js} +3 -3
  12. package/dist/{chunk-LY2B3UJH.js.map → chunk-OGQF4XJB.js.map} +1 -1
  13. package/dist/{chunk-UIUALJNR.js → chunk-PSFJN7WH.js} +12 -7
  14. package/dist/chunk-PSFJN7WH.js.map +1 -0
  15. package/dist/{chunk-JSPXF5YG.js → chunk-RBEPQY2A.js} +60 -10
  16. package/dist/chunk-RBEPQY2A.js.map +1 -0
  17. package/dist/{chunk-YGYKYELC.js → chunk-VULOVVKF.js} +12 -6
  18. package/dist/chunk-VULOVVKF.js.map +1 -0
  19. package/dist/{chunk-JWAX2AOO.js → chunk-XSJ3TWWZ.js} +43 -3
  20. package/dist/chunk-XSJ3TWWZ.js.map +1 -0
  21. package/dist/{chunk-WA7Q4TTA.js → chunk-YIGLULRG.js} +3 -3
  22. package/dist/{chunk-WA7Q4TTA.js.map → chunk-YIGLULRG.js.map} +1 -1
  23. package/dist/connection-test-row.js +1 -1
  24. package/dist/context-menu.js +1 -1
  25. package/dist/index.d.ts +3 -1
  26. package/dist/index.js +11 -9
  27. package/dist/index.js.map +1 -1
  28. package/dist/menu.d.ts +7 -1
  29. package/dist/menu.js +1 -1
  30. package/dist/model-registry.d.ts +19 -0
  31. package/dist/model-registry.js +1 -1
  32. package/dist/scale-slider.js +1 -1
  33. package/dist/sidebar-nav.d.ts +62 -0
  34. package/dist/sidebar-nav.js +3 -0
  35. package/dist/sidebar-nav.js.map +1 -0
  36. package/dist/styles.css +1 -1
  37. package/dist/task-assignment-picker.d.ts +6 -0
  38. package/dist/task-assignment-picker.js +1 -1
  39. package/dist/user-menu.d.ts +30 -0
  40. package/dist/user-menu.js +3 -0
  41. package/dist/user-menu.js.map +1 -0
  42. package/package.json +9 -1
  43. package/dist/chunk-JSPXF5YG.js.map +0 -1
  44. package/dist/chunk-JWAX2AOO.js.map +0 -1
  45. package/dist/chunk-TPLQL62V.js.map +0 -1
  46. package/dist/chunk-UIUALJNR.js.map +0 -1
  47. package/dist/chunk-YGYKYELC.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # @neuronection/assistant-ui
2
2
 
3
+ ## 0.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`e7a75e3`](https://github.com/neuronection/assistant-ui/commit/e7a75e3a3970e01f7bdcaf9060f54abe145d5a27) Thanks [@constLiakos](https://github.com/constLiakos)! - ChipInput: the typed draft now renders as a dashed pending chip
8
+ (`data-pending` on the container while a draft exists) so it is visually
9
+ explicit that each entry is its own separate item, and a new `hint` prop
10
+ renders helper text below the field wired to the input via
11
+ `aria-describedby` — use it to spell out the "press Enter or Add after each
12
+ one" contract.
13
+
14
+ - [`163e5e4`](https://github.com/neuronection/assistant-ui/commit/163e5e4f13d5a097dea56727447fe7e1070e3395) Thanks [@constLiakos](https://github.com/constLiakos)! - ScaleSlider: modern track + thumb (filled progress, larger thumb, visible
15
+ focus ring) via new `styles.css` range rules scoped to
16
+ `[data-as='scale-slider']`; low/high labels are larger and readable
17
+ (text-xs, medium weight, muted color) instead of 9px uppercase.
18
+
19
+ ChipInput: new `addLabel` prop renders an explicit add button that commits
20
+ the draft (disabled while empty, re-focuses the input) — makes the
21
+ multi-entry affordance discoverable without keyboard knowledge.
22
+
23
+ - [`3826518`](https://github.com/neuronection/assistant-ui/commit/3826518fd86f03cbef56344bec9b34b2234086c9) Thanks [@constLiakos](https://github.com/constLiakos)! - New `SidebarNav` component: the family-standard vertical navigation panel
24
+ (groups + section dividers, icons, badges, controlled `activeId`, optional
25
+ collapsed icon rail with hover flyouts, header/footer slots). Fully
26
+ presentational + controlled per ADR-006 — routing, role filtering and i18n
27
+ stay app-side. Ships with keyboard-nav + axe assertions, Ladle stories and
28
+ a docs page; adoption across the family apps is tracked in the nav
29
+ primitives program (family ADR-0007).
30
+
31
+ - [`3df9ac1`](https://github.com/neuronection/assistant-ui/commit/3df9ac1dbc90079cf1910e34bd213f63e700688b) Thanks [@constLiakos](https://github.com/constLiakos)! - `SidebarNav`: top-level `NavItem` entries now accept `section` — a divider
32
+ label rendered above the item (first occurrence only) so flat sidebars can
33
+ group their entries visually without nested expand/collapse. Sections are
34
+ ignored in the collapsed rail. (Career's sidebar adoption is the first
35
+ consumer; matches the existing child-level `section` dividers.)
36
+
37
+ - [`3826518`](https://github.com/neuronection/assistant-ui/commit/3826518fd86f03cbef56344bec9b34b2234086c9) Thanks [@constLiakos](https://github.com/constLiakos)! - New `UserMenu` component: the family-standard user menu (avatar or
38
+ initials-disc trigger with name/email, identity header in the panel, item
39
+ model with `tone: 'danger'`, `pending`, and checkable items for
40
+ theme/language toggles). Built on the `Menu` primitives; also adds
41
+ `MenuCheckboxItem` (Radix checkbox menu item with an indicator) to the
42
+ menu module. Health's hand-rolled header dropdown and career's plain
43
+ sign-out button are the adoption targets (family ADR-0007 program).
44
+
45
+ ## 0.14.0
46
+
47
+ ### Minor Changes
48
+
49
+ - [`d94e5a8`](https://github.com/neuronection/assistant-ui/commit/d94e5a82e3ddaa67e7157a8775b42347b2a6ecdb) Thanks [@constLiakos](https://github.com/constLiakos)! - `ModelRegistry` gains an app-supplied extra-field slot and a per-row enable
50
+ toggle. `extraFields` (`{ key, label, placeholder?, multiline? }[]`) renders
51
+ app-declared fields inside the add/edit modal — e.g. health-assistant's model
52
+ description — with the string values carried verbatim on
53
+ `Model.extra`/`Draft.extra`/`Patch.extra` (included in a patch only when the
54
+ user touched a field or the model already carried non-empty values).
55
+ `onToggleEnabled(model, enabled)` renders a native per-row checkbox
56
+ (`Enabled — <id>` accessible name, hidden for read-only providers) so apps can
57
+ persist the model's enabled flag from the row itself. Note: the visual
58
+ baseline for `model-registry--model-registry-story` needs a CI rebaseline.
59
+
60
+ - [`4401664`](https://github.com/neuronection/assistant-ui/commit/4401664b46b0a76a70fbca4711c31f5277913982) Thanks [@constLiakos](https://github.com/constLiakos)! - `TaskAssignmentTask` gains `secondaryOnly` — renders ONLY the fallback picker
61
+ line for that row (Fallback badge + picker + info + clear, no primary line)
62
+ for tasks whose assignment itself is a fallback, e.g. an app's single global
63
+ default model; the value round-trips via `secondaryValue`/`onAssignSecondary`.
64
+ It implies `secondary`, so it works flat or inside a section without the
65
+ section-level flag.
66
+
67
+ ### Patch Changes
68
+
69
+ - [`2e1fe5a`](https://github.com/neuronection/assistant-ui/commit/2e1fe5a0383d810cb01ed1d40c13dea30a7b9292) Thanks [@constLiakos](https://github.com/constLiakos)! - `ConnectionTestRow` wraps long error messages (`whitespace-pre-wrap` +
70
+ `break-words` instead of a single clipped line) so full provider API errors —
71
+ e.g. OpenAI's multi-line 400 payloads — stay readable in app cards.
72
+
73
+ - [`6d6d6b4`](https://github.com/neuronection/assistant-ui/commit/6d6d6b4c4b8ea8fc73cfd1ff37956bbbe73e5aa2) Thanks [@constLiakos](https://github.com/constLiakos)! - `ModelRegistry` providers accept a `readOnly` flag: read-only providers render their model rows without the Edit/Remove controls and without the Add-model button — for org-managed providers the current user may not modify.
74
+
3
75
  ## 0.13.1
4
76
 
5
77
  ### Patch Changes
package/README.md CHANGED
@@ -86,6 +86,8 @@ Visual reference: [gallery](https://neuronection.github.io/assistant-ui/).
86
86
  | [`info-button`](./docs/components/info-button.md) / [`field-label`](./docs/components/field-label.md) / [`copy-button`](./docs/components/copy-button.md) / [`breadcrumbs`](./docs/components/breadcrumbs.md) | `InfoButton` · `FieldLabel` · `CopyButton` (clipboard + copied state) · `Breadcrumbs` (SPA links via `linkComponent`) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--copy-buttons) |
87
87
  | [`ai-button`](./docs/components/ai-button.md) / [`ai-actions-dropdown`](./docs/components/ai-actions-dropdown.md) / [`ai-magic-fill`](./docs/components/ai-magic-fill.md) | AI affordances: ask-with-suggestions (controlled open, icon-only mode) · action menu + custom prompt · describe-in-words fill modal — API calls stay app-side | [stories](https://neuronection.github.io/assistant-ui/?story=ai-patterns--ai-buttons) |
88
88
  | [`settings-shell`](./docs/components/settings-shell.md) / [`provider-form`](./docs/components/provider-form.md) / [`connection-test-row`](./docs/components/connection-test-row.md) | `SettingsShell` (controlled two-pane nav) · `ProviderForm` (write-only API-key field) · `ConnectionTestRow` (idle/testing/ok/fail + latency, `inline` variant + meta slot) | [stories](https://neuronection.github.io/assistant-ui/?story=settings-trio--provider-form-story) |
89
+ | [`sidebar-nav`](./docs/components/sidebar-nav.md) | `SidebarNav` (family-standard vertical nav: groups + sections, badges, controlled active id, optional collapsed icon rail with hover flyouts, header/footer slots) | [stories](https://neuronection.github.io/assistant-ui/?story=sidebar-nav--groups-story) |
90
+ | [`user-menu`](./docs/components/user-menu.md) | `UserMenu` (avatar/identity trigger + action menu incl. checkable items; composed over `Menu`) | [stories](https://neuronection.github.io/assistant-ui/?story=user-menu--checkable-items-story) |
89
91
  | [`model-picker`](./docs/components/model-picker.md) / [`task-assignment-picker`](./docs/components/task-assignment-picker.md) / [`capability-chips`](./docs/components/capability-chips.md) | `ModelPicker` (grouped provider→model combobox, capability chips) · `TaskAssignmentPicker` (task → model mapping rows, capability-filtered, sections + fallback assignment, meta slot) · `CapabilityChips` (cap toggle group / badges, min-selected) | [stories](https://neuronection.github.io/assistant-ui/?story=model-assignment--task-assignment-v2-story) |
90
92
  | [`model-registry`](./docs/components/model-registry.md) | `ModelRegistry` (provider cards → registered model rows; add/edit in one catalog modal fed by app-fetched remote models; manual id entry; add-all of pending ids) | [stories](https://neuronection.github.io/assistant-ui/?story=model-registry--model-registry-story) |
91
93
  | [`upload-dropzone`](./docs/components/upload-dropzone.md) / [`file-card`](./docs/components/file-card.md) / [`file-queue`](./docs/components/file-queue.md) | file attachment surface: dropzone (block/row) · card (status, include toggle) · queue (summary + reorder) | [stories](https://neuronection.github.io/assistant-ui/?story=file-surface--dropzones) |
@@ -7,6 +7,8 @@ interface ChipInputProps extends Omit<React.ComponentProps<'div'>, 'onChange'> {
7
7
  separators?: string[];
8
8
  inputLabel?: string;
9
9
  removeLabel?: string;
10
+ addLabel?: string;
11
+ hint?: string;
10
12
  disabled?: boolean;
11
13
  }
12
14
  declare const ChipInput: React.ForwardRefExoticComponent<Omit<ChipInputProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -1,3 +1,3 @@
1
- export { ChipInput } from './chunk-TPLQL62V.js';
1
+ export { ChipInput } from './chunk-HIRR2F4R.js';
2
2
  //# sourceMappingURL=chip-input.js.map
3
3
  //# sourceMappingURL=chip-input.js.map
@@ -0,0 +1,119 @@
1
+ import { Menu, MenuTrigger, MenuContent, MenuSeparator, MenuCheckboxItem, MenuItem } from './chunk-XSJ3TWWZ.js';
2
+ import { cn } from './chunk-BLJWR4ZV.js';
3
+ import * as React from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ function deriveInitials(name) {
7
+ if (!name) return "";
8
+ const parts = name.trim().split(/\s+/).filter(Boolean);
9
+ if (parts.length === 0) return "";
10
+ const first = parts[0]?.charAt(0) ?? "";
11
+ const last = parts.length > 1 ? parts[parts.length - 1]?.charAt(0) ?? "" : "";
12
+ return (first + last).toUpperCase();
13
+ }
14
+ function UserGlyph() {
15
+ return /* @__PURE__ */ jsxs(
16
+ "svg",
17
+ {
18
+ viewBox: "0 0 24 24",
19
+ fill: "none",
20
+ stroke: "currentColor",
21
+ strokeWidth: "2",
22
+ strokeLinecap: "round",
23
+ strokeLinejoin: "round",
24
+ className: "size-4",
25
+ "aria-hidden": true,
26
+ children: [
27
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
28
+ /* @__PURE__ */ jsx("path", { d: "M4 21c0-4 3.6-6 8-6s8 2 8 6" })
29
+ ]
30
+ }
31
+ );
32
+ }
33
+ var UserMenu = React.forwardRef(
34
+ function UserMenu2({
35
+ name,
36
+ email,
37
+ avatarUrl,
38
+ initials,
39
+ items,
40
+ onItemSelect,
41
+ align = "end",
42
+ labels,
43
+ className
44
+ }, ref) {
45
+ const disc = initials ?? deriveInitials(name);
46
+ const hasIdentity = name !== void 0 || email !== void 0;
47
+ return /* @__PURE__ */ jsx("div", { ref, "data-as": "user-menu", className: cn("inline-flex", className), children: /* @__PURE__ */ jsxs(Menu, { children: [
48
+ /* @__PURE__ */ jsxs(
49
+ MenuTrigger,
50
+ {
51
+ type: "button",
52
+ "aria-label": labels?.openMenu ?? "Open user menu",
53
+ "aria-haspopup": "menu",
54
+ className: "flex cursor-pointer items-center gap-2.5 rounded-full p-1 pr-3 transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
55
+ children: [
56
+ avatarUrl ? /* @__PURE__ */ jsx(
57
+ "img",
58
+ {
59
+ src: avatarUrl,
60
+ alt: "",
61
+ className: "size-8 shrink-0 rounded-full object-cover"
62
+ }
63
+ ) : /* @__PURE__ */ jsx(
64
+ "span",
65
+ {
66
+ "aria-hidden": true,
67
+ className: "flex size-8 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-xs font-bold text-[var(--as-primary-fg)]",
68
+ children: disc || /* @__PURE__ */ jsx(UserGlyph, {})
69
+ }
70
+ ),
71
+ name || email ? /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col text-left leading-tight", children: [
72
+ name ? /* @__PURE__ */ jsx("span", { className: "max-w-40 truncate text-sm font-medium text-[var(--as-fg)]", children: name }) : null,
73
+ email ? /* @__PURE__ */ jsx("span", { className: "max-w-40 truncate text-xs text-[var(--as-muted-fg)]", children: email }) : null
74
+ ] }) : null
75
+ ]
76
+ }
77
+ ),
78
+ /* @__PURE__ */ jsxs(MenuContent, { align, sideOffset: 8, className: "min-w-48", children: [
79
+ hasIdentity ? /* @__PURE__ */ jsxs(Fragment, { children: [
80
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col px-2 py-1.5", children: [
81
+ name ? /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-bold text-[var(--as-fg)]", children: name }) : null,
82
+ email ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-[var(--as-muted-fg)]", children: email }) : null
83
+ ] }),
84
+ /* @__PURE__ */ jsx(MenuSeparator, {})
85
+ ] }) : null,
86
+ items.map(
87
+ (item) => item.checked !== void 0 ? /* @__PURE__ */ jsx(
88
+ MenuCheckboxItem,
89
+ {
90
+ icon: item.icon,
91
+ danger: item.tone === "danger",
92
+ disabled: item.disabled,
93
+ pending: item.pending,
94
+ checked: item.checked,
95
+ onSelect: () => onItemSelect(item.id),
96
+ children: item.label
97
+ },
98
+ item.id
99
+ ) : /* @__PURE__ */ jsx(
100
+ MenuItem,
101
+ {
102
+ icon: item.icon,
103
+ danger: item.tone === "danger",
104
+ disabled: item.disabled,
105
+ pending: item.pending,
106
+ onSelect: () => onItemSelect(item.id),
107
+ children: item.label
108
+ },
109
+ item.id
110
+ )
111
+ )
112
+ ] })
113
+ ] }) });
114
+ }
115
+ );
116
+
117
+ export { UserMenu };
118
+ //# sourceMappingURL=chunk-535TM7SK.js.map
119
+ //# sourceMappingURL=chunk-535TM7SK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/user-menu/UserMenu.tsx"],"names":["UserMenu"],"mappings":"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","file":"chunk-535TM7SK.js","sourcesContent":["import * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport {\n Menu,\n MenuCheckboxItem,\n MenuContent,\n MenuItem,\n MenuSeparator,\n MenuTrigger,\n} from '../menu/Menu'\nimport { cn } from '../../lib/utils'\n\nexport interface UserMenuItem {\n id: string\n label: string\n icon?: LucideIcon\n tone?: 'default' | 'danger'\n disabled?: boolean\n pending?: boolean\n /** Renders as a checkable item (aria-checked) — e.g. theme/language toggles. */\n checked?: boolean\n}\n\nexport interface UserMenuProps {\n name?: string\n email?: string\n avatarUrl?: string\n /** Fallback disc text when no avatarUrl; derived from `name` when omitted. */\n initials?: string\n items: UserMenuItem[]\n onItemSelect: (id: string) => void\n align?: 'start' | 'end'\n labels?: { openMenu?: string }\n className?: string\n}\n\nfunction deriveInitials(name?: string): string {\n if (!name) return ''\n const parts = name.trim().split(/\\s+/).filter(Boolean)\n if (parts.length === 0) return ''\n const first = parts[0]?.charAt(0) ?? ''\n const last = parts.length > 1 ? (parts[parts.length - 1]?.charAt(0) ?? '') : ''\n return (first + last).toUpperCase()\n}\n\nfunction UserGlyph() {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4\"\n aria-hidden\n >\n <circle cx=\"12\" cy=\"8\" r=\"4\" />\n <path d=\"M4 21c0-4 3.6-6 8-6s8 2 8 6\" />\n </svg>\n )\n}\n\nexport const UserMenu = React.forwardRef<HTMLDivElement, UserMenuProps>(\n function UserMenu(\n {\n name,\n email,\n avatarUrl,\n initials,\n items,\n onItemSelect,\n align = 'end',\n labels,\n className,\n },\n ref,\n ) {\n const disc = initials ?? deriveInitials(name)\n const hasIdentity = name !== undefined || email !== undefined\n\n return (\n <div ref={ref} data-as=\"user-menu\" className={cn('inline-flex', className)}>\n <Menu>\n <MenuTrigger\n type=\"button\"\n aria-label={labels?.openMenu ?? 'Open user menu'}\n aria-haspopup=\"menu\"\n className=\"flex cursor-pointer items-center gap-2.5 rounded-full p-1 pr-3 transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n {avatarUrl ? (\n <img\n src={avatarUrl}\n alt=\"\"\n className=\"size-8 shrink-0 rounded-full object-cover\"\n />\n ) : (\n <span\n aria-hidden\n className=\"flex size-8 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-xs font-bold text-[var(--as-primary-fg)]\"\n >\n {disc || <UserGlyph />}\n </span>\n )}\n {name || email ? (\n <span className=\"flex min-w-0 flex-col text-left leading-tight\">\n {name ? (\n <span className=\"max-w-40 truncate text-sm font-medium text-[var(--as-fg)]\">\n {name}\n </span>\n ) : null}\n {email ? (\n <span className=\"max-w-40 truncate text-xs text-[var(--as-muted-fg)]\">\n {email}\n </span>\n ) : null}\n </span>\n ) : null}\n </MenuTrigger>\n <MenuContent align={align} sideOffset={8} className=\"min-w-48\">\n {hasIdentity ? (\n <>\n <div className=\"flex flex-col px-2 py-1.5\">\n {name ? (\n <span className=\"truncate text-sm font-bold text-[var(--as-fg)]\">\n {name}\n </span>\n ) : null}\n {email ? (\n <span className=\"truncate text-xs text-[var(--as-muted-fg)]\">\n {email}\n </span>\n ) : null}\n </div>\n <MenuSeparator />\n </>\n ) : null}\n {items.map((item) =>\n item.checked !== undefined ? (\n <MenuCheckboxItem\n key={item.id}\n icon={item.icon}\n danger={item.tone === 'danger'}\n disabled={item.disabled}\n pending={item.pending}\n checked={item.checked}\n onSelect={() => onItemSelect(item.id)}\n >\n {item.label}\n </MenuCheckboxItem>\n ) : (\n <MenuItem\n key={item.id}\n icon={item.icon}\n danger={item.tone === 'danger'}\n disabled={item.disabled}\n pending={item.pending}\n onSelect={() => onItemSelect(item.id)}\n >\n {item.label}\n </MenuItem>\n ),\n )}\n </MenuContent>\n </Menu>\n </div>\n )\n },\n)\n"]}