@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.
- package/CHANGELOG.md +72 -0
- package/README.md +2 -0
- package/dist/chip-input.d.ts +2 -0
- package/dist/chip-input.js +1 -1
- package/dist/chunk-535TM7SK.js +119 -0
- package/dist/chunk-535TM7SK.js.map +1 -0
- package/dist/chunk-EZTQMJ5I.js +428 -0
- package/dist/chunk-EZTQMJ5I.js.map +1 -0
- package/dist/{chunk-TPLQL62V.js → chunk-HIRR2F4R.js} +42 -5
- package/dist/chunk-HIRR2F4R.js.map +1 -0
- package/dist/{chunk-LY2B3UJH.js → chunk-OGQF4XJB.js} +3 -3
- package/dist/{chunk-LY2B3UJH.js.map → chunk-OGQF4XJB.js.map} +1 -1
- package/dist/{chunk-UIUALJNR.js → chunk-PSFJN7WH.js} +12 -7
- package/dist/chunk-PSFJN7WH.js.map +1 -0
- package/dist/{chunk-JSPXF5YG.js → chunk-RBEPQY2A.js} +60 -10
- package/dist/chunk-RBEPQY2A.js.map +1 -0
- package/dist/{chunk-YGYKYELC.js → chunk-VULOVVKF.js} +12 -6
- package/dist/chunk-VULOVVKF.js.map +1 -0
- package/dist/{chunk-JWAX2AOO.js → chunk-XSJ3TWWZ.js} +43 -3
- package/dist/chunk-XSJ3TWWZ.js.map +1 -0
- package/dist/{chunk-WA7Q4TTA.js → chunk-YIGLULRG.js} +3 -3
- package/dist/{chunk-WA7Q4TTA.js.map → chunk-YIGLULRG.js.map} +1 -1
- package/dist/connection-test-row.js +1 -1
- package/dist/context-menu.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +11 -9
- package/dist/index.js.map +1 -1
- package/dist/menu.d.ts +7 -1
- package/dist/menu.js +1 -1
- package/dist/model-registry.d.ts +19 -0
- package/dist/model-registry.js +1 -1
- package/dist/scale-slider.js +1 -1
- package/dist/sidebar-nav.d.ts +62 -0
- package/dist/sidebar-nav.js +3 -0
- package/dist/sidebar-nav.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/task-assignment-picker.d.ts +6 -0
- package/dist/task-assignment-picker.js +1 -1
- package/dist/user-menu.d.ts +30 -0
- package/dist/user-menu.js +3 -0
- package/dist/user-menu.js.map +1 -0
- package/package.json +9 -1
- package/dist/chunk-JSPXF5YG.js.map +0 -1
- package/dist/chunk-JWAX2AOO.js.map +0 -1
- package/dist/chunk-TPLQL62V.js.map +0 -1
- package/dist/chunk-UIUALJNR.js.map +0 -1
- 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) |
|
package/dist/chip-input.d.ts
CHANGED
|
@@ -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>>;
|
package/dist/chip-input.js
CHANGED
|
@@ -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"]}
|