@neuronection/assistant-ui 0.47.0 → 0.48.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,25 @@
1
1
  # @neuronection/assistant-ui
2
2
 
3
+ ## 0.48.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`e24b449`](https://github.com/neuronection/assistant-ui/commit/e24b4496037347ce369dd9e90ed568870f3ae32f) Thanks [@constLiakos](https://github.com/constLiakos)! - **ChatMessage**: new `actions.errorExtra` slot — an app-composed node rendered inside the error card (e.g. an "Open AI settings" link for not-configured failures). It sits next to the Retry button when the error is retryable and renders alone otherwise.
8
+
9
+ - [`70797f0`](https://github.com/neuronection/assistant-ui/commit/70797f04ad71eea3fe803a0000c541e348dbde3f) Thanks [@constLiakos](https://github.com/constLiakos)! - **ProfileSwitcher redesign**: the trigger becomes an avatar chip (round
10
+ avatar + name + chevron, `UserMenu`-style) instead of an outline button;
11
+ the panel moves to the Menu aesthetic (`w-80`, `backdrop-blur-xl`,
12
+ `--as-shadow-pop`) with a titled header + profile count. Rows are roomier
13
+ (size-8 ringed avatars, `font-medium` names) with pill badges — the
14
+ accent-tinted `Current` pill replaces the bare check text, `Default` gets a
15
+ muted pill — and the row actions compact to `size-7` ghost icon buttons
16
+ (delete hovers to danger). The create row gains an inline `+` prefix icon,
17
+ `↓/↑`/`Home`/`End` now walk the rows, and loading gets `as-anim-fade` +
18
+ `role="status"`. No API changes: every label, icon and callback prop is
19
+ unchanged.
20
+
21
+ - [`237dcc7`](https://github.com/neuronection/assistant-ui/commit/237dcc7b8afe729cd27f8415d4db36d53b5caa90) Thanks [@constLiakos](https://github.com/constLiakos)! - **UserMenu**: removed the legacy flat identity props (`name`, `email`, `avatarUrl`) — pass the structured `user` prop instead (every family app already does). New `themeLayout` prop (`'auto' | 'submenu' | 'inline'`, default `'auto'`) makes the appearance-section layout explicit instead of inferred from `themeLabels` cardinality. Single-option toggle rows now report `system → light` when toggled off (was `dark`).
22
+
3
23
  ## 0.47.0
4
24
 
5
25
  ### Minor Changes
@@ -57,6 +57,12 @@ interface ChatMessageActions {
57
57
  onRetry?: () => void;
58
58
  /** Extra icon actions (app-specific). */
59
59
  extras?: ChatMessageAction[];
60
+ /**
61
+ * Extra node rendered inside the error card next to the Retry button —
62
+ * e.g. an "Open AI settings" link when the error is a not-configured
63
+ * failure. Rendered only while the error card is visible.
64
+ */
65
+ errorExtra?: React.ReactNode;
60
66
  }
61
67
  interface ChatMessageLabels {
62
68
  copy: string;
@@ -1,3 +1,3 @@
1
- export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher } from './chunk-EAJU4LFG.js';
1
+ export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher } from './chunk-C5JZCCU7.js';
2
2
  //# sourceMappingURL=chat-message.js.map
3
3
  //# sourceMappingURL=chat-message.js.map
@@ -1,3 +1,3 @@
1
- export { ChatTranscript } from './chunk-MTFHU6QY.js';
1
+ export { ChatTranscript } from './chunk-XOODPYJ3.js';
2
2
  //# sourceMappingURL=chat-transcript.js.map
3
3
  //# sourceMappingURL=chat-transcript.js.map
@@ -169,7 +169,10 @@ var ChatMessage = React.forwardRef(
169
169
  children: [
170
170
  /* @__PURE__ */ jsx("span", { className: "font-mono font-semibold uppercase tracking-wide", children: error.code }),
171
171
  /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap break-words", children: error.message }),
172
- error.retryable && actions?.onRetry ? /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "self-start", onClick: actions.onRetry, children: labels?.retry ?? "Retry" }) : null
172
+ error.retryable && actions?.onRetry ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
173
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "self-start", onClick: actions.onRetry, children: labels?.retry ?? "Retry" }),
174
+ actions?.errorExtra
175
+ ] }) : actions?.errorExtra ? /* @__PURE__ */ jsx("div", { className: "self-start", children: actions.errorExtra }) : null
173
176
  ]
174
177
  }
175
178
  ) : null,
@@ -213,5 +216,5 @@ var ChatMessage = React.forwardRef(
213
216
  );
214
217
 
215
218
  export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher };
216
- //# sourceMappingURL=chunk-EAJU4LFG.js.map
217
- //# sourceMappingURL=chunk-EAJU4LFG.js.map
219
+ //# sourceMappingURL=chunk-C5JZCCU7.js.map
220
+ //# sourceMappingURL=chunk-C5JZCCU7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-message/ChatMessageEditor.tsx","../src/components/chat-message/MessageVariantSwitcher.tsx","../src/components/chat-message/ChatMessage.tsx"],"names":["ChatMessageEditor","React2","MessageVariantSwitcher","jsxs","jsx","React3","ChatMessage"],"mappings":"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","file":"chunk-C5JZCCU7.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\n\nexport interface ChatMessageEditorLabels {\n save: string\n cancel: string\n ariaLabel: string\n}\n\nexport interface ChatMessageEditorProps {\n value: string\n onValueChange: (value: string) => void\n /** Save & resend — branches the tree (family `edit` endpoint) and reruns the turn. */\n onSubmit: () => void\n onCancel: () => void\n submitDisabled?: boolean\n rows?: number\n labels?: Partial<ChatMessageEditorLabels>\n className?: string\n}\n\n/**\n * Inline user-prompt editor for the edit-and-resend flow (study pattern):\n * controlled textarea, Cmd/Ctrl+Enter saves, Escape cancels.\n */\nexport const ChatMessageEditor = React.forwardRef<HTMLDivElement, ChatMessageEditorProps>(\n function ChatMessageEditor(\n { value, onValueChange, onSubmit, onCancel, submitDisabled = false, rows = 3, labels, className },\n ref,\n ) {\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!submitDisabled) {\n onSubmit()\n }\n }\n }\n\n return (\n <div ref={ref} data-as=\"chat-message-editor\" className={cn('flex w-full flex-col gap-2', className)}>\n <textarea\n value={value}\n rows={rows}\n aria-label={labels?.ariaLabel ?? 'Edit message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n className=\"w-full resize-y rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n />\n <div className=\"flex items-center justify-end gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onCancel}>\n {labels?.cancel ?? 'Cancel'}\n </Button>\n <Button size=\"sm\" disabled={submitDisabled} onClick={onSubmit}>\n {labels?.save ?? 'Save & resend'}\n </Button>\n </div>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\n\nimport type { ChatMessageVariants } from '../chat-core/types'\nimport { cn } from '../../lib/utils'\n\nexport interface MessageVariantSwitcherLabels {\n group: string\n variantOf: (index: number, count: number) => string\n previous: string\n next: string\n}\n\nexport interface MessageVariantSwitcherProps {\n variants: ChatMessageVariants\n /** Flip the parent's active-child pointer to the sibling id (family `select` endpoint). */\n onSelect: (messageId: string) => void\n labels?: Partial<MessageVariantSwitcherLabels>\n className?: string\n}\n\n/**\n * The `‹ n/N ›` sibling-variant switcher under a branched message\n * (study's inline switcher, family branching contract).\n */\nexport const MessageVariantSwitcher = React.forwardRef<HTMLDivElement, MessageVariantSwitcherProps>(\n function MessageVariantSwitcher({ variants, onSelect, labels, className }, ref) {\n const { index, count, siblingIds } = variants\n const previousId = index > 1 ? siblingIds[index - 2] : undefined\n const nextId = index < count ? siblingIds[index] : undefined\n\n const select = (event: React.MouseEvent<HTMLButtonElement>, messageId: string | undefined) => {\n event.stopPropagation()\n if (messageId !== undefined) {\n onSelect(messageId)\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={labels?.group ?? 'Message variants'}\n data-as=\"message-variant-switcher\"\n className={cn('flex items-center gap-0.5 text-xs text-[var(--as-muted-fg)]', className)}\n >\n <button\n type=\"button\"\n disabled={previousId === undefined}\n aria-label={labels?.previous ?? 'Previous variant'}\n title={labels?.previous ?? 'Previous variant'}\n onClick={(event) => select(event, previousId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronLeft className=\"size-3.5\" aria-hidden />\n </button>\n <span className=\"tabular-nums\" title={labels?.variantOf?.(index, count) ?? `Variant ${index} of ${count}`}>\n {index}/{count}\n </span>\n <button\n type=\"button\"\n disabled={nextId === undefined}\n aria-label={labels?.next ?? 'Next variant'}\n title={labels?.next ?? 'Next variant'}\n onClick={(event) => select(event, nextId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronRight className=\"size-3.5\" aria-hidden />\n </button>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { Copy, Pencil, RotateCcw, TriangleAlert } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\nimport type { ChatError, ChatMessageVariants, ChatRole } from '../chat-core/types'\nimport { ChatMessageEditor, type ChatMessageEditorLabels } from './ChatMessageEditor'\nimport { MessageVariantSwitcher, type MessageVariantSwitcherLabels } from './MessageVariantSwitcher'\n\nexport interface ChatMessageAction {\n key: string\n label: string\n icon?: LucideIcon\n onClick: () => void\n danger?: boolean\n}\n\nexport interface ChatMessageActions {\n onCopy?: () => void\n onEdit?: () => void\n onRegenerate?: () => void\n onRetry?: () => void\n /** Extra icon actions (app-specific). */\n extras?: ChatMessageAction[]\n /**\n * Extra node rendered inside the error card next to the Retry button —\n * e.g. an \"Open AI settings\" link when the error is a not-configured\n * failure. Rendered only while the error card is visible.\n */\n errorExtra?: React.ReactNode\n}\n\nexport interface ChatMessageLabels {\n copy: string\n edit: string\n regenerate: string\n retry: string\n interrupted: string\n error: string\n}\n\nexport interface ChatMessageProps {\n role: ChatRole\n /** Usually a `MarkdownSurface`; any node graph renders here. */\n content: React.ReactNode\n /** Thinking block slot (usually `ChatReasoning`). */\n reasoning?: React.ReactNode\n /** Tool cards / HITL cards below the content. */\n children?: React.ReactNode\n status?: 'streaming' | 'done' | 'error' | 'interrupted'\n error?: ChatError\n actions?: ChatMessageActions\n /** Edit-and-resend mode for user messages (renders `ChatMessageEditor`). */\n editing?: false | { value: string; onValueChange: (value: string) => void; onSubmit: () => void; onCancel: () => void; submitDisabled?: boolean }\n variants?: ChatMessageVariants\n onSelectVariant?: (messageId: string) => void\n /** Badge row (model, latency, tokens — app-defined). */\n meta?: React.ReactNode\n /** Deep-link chip row (career references, health citations). */\n chips?: React.ReactNode\n /** Attachment rail above the content. */\n attachments?: React.ReactNode\n labels?: Partial<ChatMessageLabels & ChatMessageEditorLabels & MessageVariantSwitcherLabels>\n compact?: boolean\n className?: string\n}\n\nconst actionButtonClass =\n 'rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]'\n\n/**\n * The family chat bubble: role-aligned, hover action row (copy / edit /\n * regenerate + app extras), variant switcher under branched messages,\n * reasoning and below-content slots, inline edit-and-resend, error +\n * interrupted states. Presentational — state lives app-side.\n */\nexport const ChatMessage = React.forwardRef<HTMLDivElement, ChatMessageProps>(\n function ChatMessage(\n {\n role,\n content,\n reasoning,\n children,\n status = 'done',\n error,\n actions,\n editing = false,\n variants,\n onSelectVariant,\n meta,\n chips,\n attachments,\n labels,\n compact = false,\n className,\n },\n ref,\n ) {\n const isUser = role === 'user'\n const isSystem = role === 'system'\n const showActions =\n actions !== undefined && (actions.onCopy !== undefined || actions.onEdit !== undefined || actions.onRegenerate !== undefined || (actions.extras?.length ?? 0) > 0)\n const editorLabels = { save: labels?.save, cancel: labels?.cancel, ariaLabel: labels?.ariaLabel }\n const switcherLabels = { group: labels?.group, variantOf: labels?.variantOf, previous: labels?.previous, next: labels?.next }\n\n if (isSystem) {\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n className={cn('flex justify-center', className)}\n >\n <div className=\"rounded-full border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-1 text-xs text-[var(--as-muted-fg)]\">\n {content}\n </div>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n data-status={status}\n className={cn('group/message flex w-full flex-col', isUser ? 'items-end' : 'items-start', className)}\n >\n {attachments ? <div className=\"mb-1.5 flex w-full flex-wrap justify-end gap-1.5\">{attachments}</div> : null}\n <div\n className={cn(\n 'relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius)] px-3 py-2 text-sm',\n compact && 'px-2.5 py-1.5',\n isUser\n ? 'rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'rounded-bl-[var(--as-radius-sm)] bg-[var(--as-muted)] text-[var(--as-fg)]',\n )}\n >\n {reasoning ? <div className=\"not-prose\">{reasoning}</div> : null}\n {editing ? (\n <ChatMessageEditor\n value={editing.value}\n onValueChange={editing.onValueChange}\n onSubmit={editing.onSubmit}\n onCancel={editing.onCancel}\n submitDisabled={editing.submitDisabled}\n labels={editorLabels}\n />\n ) : (\n <div className=\"min-w-0\">{content}</div>\n )}\n {status === 'interrupted' ? (\n <span className=\"self-start rounded-full bg-[var(--as-warning)]/15 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-[var(--as-warning)]\">\n {labels?.interrupted ?? 'Stopped'}\n </span>\n ) : null}\n {status === 'error' && error ? (\n <div\n role=\"alert\"\n className=\"flex flex-col gap-1 rounded-[var(--as-radius)] border border-[var(--as-danger)]/40 bg-[var(--as-danger)]/10 px-2.5 py-1.5 text-xs text-[var(--as-danger)]\"\n >\n <span className=\"font-mono font-semibold uppercase tracking-wide\">{error.code}</span>\n <span className=\"whitespace-pre-wrap break-words\">{error.message}</span>\n {error.retryable && actions?.onRetry ? (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <Button variant=\"outline\" size=\"sm\" className=\"self-start\" onClick={actions.onRetry}>\n {labels?.retry ?? 'Retry'}\n </Button>\n {actions?.errorExtra}\n </div>\n ) : actions?.errorExtra ? (\n <div className=\"self-start\">{actions.errorExtra}</div>\n ) : null}\n </div>\n ) : null}\n {chips ? <div className=\"flex flex-wrap gap-1.5\">{chips}</div> : null}\n {children ? <div className=\"flex flex-col gap-1.5\">{children}</div> : null}\n {showActions && !editing ? (\n <div\n className={cn(\n 'absolute top-1 flex items-center gap-0.5 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-0.5 opacity-0 shadow-[var(--as-shadow-1)] transition-opacity focus-within:opacity-100 group-hover/message:opacity-100',\n isUser ? '-left-9' : '-right-9',\n )}\n >\n {actions?.onCopy ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.copy ?? 'Copy'} title={labels?.copy ?? 'Copy'} onClick={actions.onCopy}>\n <Copy className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onEdit ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.edit ?? 'Edit'} title={labels?.edit ?? 'Edit'} onClick={actions.onEdit}>\n <Pencil className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onRegenerate ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.regenerate ?? 'Regenerate'} title={labels?.regenerate ?? 'Regenerate'} onClick={actions.onRegenerate}>\n <RotateCcw className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.extras?.map((extra) => (\n <button\n key={extra.key}\n type=\"button\"\n className={cn(actionButtonClass, extra.danger && 'hover:text-[var(--as-danger)]')}\n aria-label={extra.label}\n title={extra.label}\n onClick={extra.onClick}\n >\n {extra.icon ? <extra.icon className=\"size-3.5\" aria-hidden /> : <TriangleAlert className=\"size-3.5\" aria-hidden />}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n {meta ? <div className=\"mt-0.5 flex flex-wrap items-center gap-1.5 px-1 text-[10px] text-[var(--as-muted-fg)]\">{meta}</div> : null}\n {variants && variants.count > 1 && onSelectVariant ? (\n <MessageVariantSwitcher variants={variants} onSelect={onSelectVariant} labels={switcherLabels} className=\"mt-0.5 px-1\" />\n ) : null}\n </div>\n )\n },\n)\n"]}
@@ -44,9 +44,6 @@ var statusToneClass = {
44
44
  var UserMenu = React.forwardRef(
45
45
  function UserMenu2({
46
46
  user,
47
- name,
48
- email,
49
- avatarUrl,
50
47
  initials,
51
48
  roleBadge,
52
49
  switcher,
@@ -54,6 +51,7 @@ var UserMenu = React.forwardRef(
54
51
  theme,
55
52
  onThemeChange,
56
53
  themeLabels,
54
+ themeLayout = "auto",
57
55
  language,
58
56
  onLanguageChange,
59
57
  languages,
@@ -68,15 +66,16 @@ var UserMenu = React.forwardRef(
68
66
  triggerClassName,
69
67
  contentClassName
70
68
  }, ref) {
71
- const identityName = user?.name ?? name;
72
- const identityEmail = user?.email ?? email;
73
- const identityAvatarUrl = user?.avatarUrl ?? avatarUrl;
69
+ const identityName = user?.name;
70
+ const identityEmail = user?.email;
71
+ const identityAvatarUrl = user?.avatarUrl;
74
72
  const badge = roleBadge ?? user?.role;
75
73
  const hasIdentity = Boolean(identityName || identityEmail || badge);
76
74
  const disc = initials ?? deriveInitials(identityName);
77
75
  const menuItemList = items ?? [];
78
76
  const languageRows = language !== void 0 && onLanguageChange !== void 0 ? languages ?? [] : [];
79
77
  const themeRows = theme !== void 0 && onThemeChange !== void 0 ? THEME_ORDER.filter((id) => themeLabels?.[id] !== void 0) : [];
78
+ const themeAsSubmenu = themeLayout === "submenu" || themeLayout === "auto" && themeRows.length >= 3;
80
79
  const resolvedIcons = {
81
80
  logout: icons?.logout ?? LogOut,
82
81
  themeLight: icons?.themeLight ?? Sun,
@@ -88,9 +87,14 @@ var UserMenu = React.forwardRef(
88
87
  if (isTypingTarget(event.target)) event.stopPropagation();
89
88
  };
90
89
  const themeIcon = (id) => id === "light" ? resolvedIcons.themeLight : id === "dark" ? resolvedIcons.themeDark : resolvedIcons.themeSystem;
90
+ const toggleCounterpart = {
91
+ light: "dark",
92
+ dark: "light",
93
+ system: "light"
94
+ };
91
95
  const selectTheme = (id) => {
92
96
  if (themeRows.length === 1) {
93
- onThemeChange?.(theme === id ? id === "dark" ? "light" : "dark" : id);
97
+ onThemeChange?.(theme === id ? toggleCounterpart[id] : id);
94
98
  } else {
95
99
  onThemeChange?.(id);
96
100
  }
@@ -198,7 +202,7 @@ var UserMenu = React.forwardRef(
198
202
  `lang-${lang.id}`
199
203
  )) })
200
204
  ] }) : null,
201
- themeRows.length >= 3 ? /* @__PURE__ */ jsxs(MenuSub, { children: [
205
+ themeAsSubmenu ? /* @__PURE__ */ jsxs(MenuSub, { children: [
202
206
  /* @__PURE__ */ jsxs(MenuSubTrigger, { children: [
203
207
  labels?.theme ?? "Theme",
204
208
  /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-[var(--as-muted-fg)]", children: theme !== void 0 ? themeLabels?.[theme] : void 0 })
@@ -262,5 +266,5 @@ var UserMenu = React.forwardRef(
262
266
  );
263
267
 
264
268
  export { UserMenu };
265
- //# sourceMappingURL=chunk-AIFKK377.js.map
266
- //# sourceMappingURL=chunk-AIFKK377.js.map
269
+ //# sourceMappingURL=chunk-CYK3ZLAB.js.map
270
+ //# sourceMappingURL=chunk-CYK3ZLAB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/user-menu/UserMenu.tsx"],"names":["UserMenu"],"mappings":"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","file":"chunk-CYK3ZLAB.js","sourcesContent":["import * as React from 'react'\nimport { Globe, LogOut, Monitor, Moon, Sun, type LucideIcon } from 'lucide-react'\nimport {\n Menu,\n MenuCheckboxItem,\n MenuContent,\n MenuItem,\n MenuLabel,\n MenuSeparator,\n MenuTrigger,\n MenuSub,\n MenuSubContent,\n MenuSubTrigger,\n} from '../menu/Menu'\nimport { cn } from '../../lib/utils'\n\n/** Theme preference for the built-in appearance section. */\nexport type UserMenuTheme = 'light' | 'dark' | 'system'\n\n/** Tone of the status pill. */\nexport type UserMenuStatusTone = 'success' | 'info' | 'warning'\n\nexport interface UserMenuUser {\n name?: string\n email?: string\n /** Raw role (e.g. `'ADMIN'`) — badge text comes from `roleBadge`. */\n role?: string\n avatarUrl?: string\n}\n\nexport interface UserMenuLanguage {\n id: string\n label: string\n}\n\nexport interface UserMenuStatus {\n label: string\n tone?: UserMenuStatusTone\n}\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. toggles. */\n checked?: boolean\n}\n\nexport interface UserMenuIcons {\n logout?: LucideIcon\n language?: LucideIcon\n themeLight?: LucideIcon\n themeDark?: LucideIcon\n themeSystem?: LucideIcon\n}\n\nexport interface UserMenuProps {\n /** Structured identity — rendered in the trigger and the panel header. */\n user?: UserMenuUser\n /** Fallback disc text when no avatarUrl; derived from `user.name` when omitted. */\n initials?: string\n /** Badge text for the role (app-translated); falls back to `user.role`. */\n roleBadge?: string\n /**\n * App-composed switcher (ProfileSwitcher / TenantSwitcher). Rendered in\n * its own top section, above the identity block.\n */\n switcher?: React.ReactNode\n /** Status pill rendered under the identity block (e.g. \"Synced\"). */\n status?: UserMenuStatus\n /** Controlled theme preference — checked state of the theme items. */\n theme?: UserMenuTheme\n onThemeChange?: (theme: UserMenuTheme) => void\n /**\n * Labels for the theme options — with all three (`light`/`dark`/`system`)\n * they render as a checkable \"Theme\" submenu whose trigger shows the\n * active theme (mirroring the Languages submenu); fewer labels render as\n * inline checkable rows (provide `dark` only for a single \"Dark Mode\"\n * toggle row).\n */\n themeLabels?: { light?: string; dark?: string; system?: string }\n /**\n * Layout of the theme section. `'auto'` (default) renders the submenu\n * when all three labels are present and inline rows otherwise;\n * `'submenu'` / `'inline'` force the layout regardless of label count.\n */\n themeLayout?: 'auto' | 'submenu' | 'inline'\n /** Controlled language — checked state of the language rows. */\n language?: string\n onLanguageChange?: (id: string) => void\n /** Rendered as checkable rows when `language`/`onLanguageChange` are set. */\n languages?: UserMenuLanguage[]\n /** App entries (My Profile, Setup, Settings, Integrations, About…). */\n items?: UserMenuItem[]\n onItemSelect?: (id: string) => void\n /** When set, renders the danger logout row at the bottom. */\n onLogout?: () => void\n logoutLabel?: string\n align?: 'start' | 'end'\n labels?: { openMenu?: string; account?: string; language?: string; theme?: string }\n icons?: UserMenuIcons\n className?: string\n triggerClassName?: string\n /** Extra classes for the panel — e.g. width or `overflow-visible` when a\n * switcher slot renders its own floating panel. */\n contentClassName?: string\n}\n\nconst THEME_ORDER: UserMenuTheme[] = ['light', 'dark', 'system']\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\nfunction isTypingTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false\n return (\n target.isContentEditable ||\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.tagName === 'SELECT'\n )\n}\n\nconst statusToneClass: Record<UserMenuStatusTone, string> = {\n success: 'bg-[var(--as-success)]/10 text-[var(--as-success)]',\n info: 'bg-[var(--as-primary)]/10 text-[var(--as-primary)]',\n warning: 'bg-[var(--as-warning)]/10 text-[var(--as-warning)]',\n}\n\nexport const UserMenu = React.forwardRef<HTMLDivElement, UserMenuProps>(\n function UserMenu(\n {\n user,\n initials,\n roleBadge,\n switcher,\n status,\n theme,\n onThemeChange,\n themeLabels,\n themeLayout = 'auto',\n language,\n onLanguageChange,\n languages,\n items,\n onItemSelect,\n onLogout,\n logoutLabel,\n align = 'end',\n labels,\n icons,\n className,\n triggerClassName,\n contentClassName,\n },\n ref,\n ) {\n const identityName = user?.name\n const identityEmail = user?.email\n const identityAvatarUrl = user?.avatarUrl\n const badge = roleBadge ?? user?.role\n const hasIdentity = Boolean(identityName || identityEmail || badge)\n\n const disc = initials ?? deriveInitials(identityName)\n const menuItemList = items ?? []\n const languageRows = language !== undefined && onLanguageChange !== undefined ? (languages ?? []) : []\n const themeRows =\n theme !== undefined && onThemeChange !== undefined\n ? THEME_ORDER.filter((id) => themeLabels?.[id] !== undefined)\n : []\n const themeAsSubmenu =\n themeLayout === 'submenu' || (themeLayout === 'auto' && themeRows.length >= 3)\n\n const resolvedIcons = {\n logout: icons?.logout ?? LogOut,\n language: icons?.language ?? Globe,\n themeLight: icons?.themeLight ?? Sun,\n themeDark: icons?.themeDark ?? Moon,\n themeSystem: icons?.themeSystem ?? Monitor,\n }\n\n // Switcher slots (tenant/profile pickers) embed real form controls.\n // Radix menu content runs typeahead on every character keydown, which\n // would yank focus out of those inputs — swallow keydowns from typing\n // targets (Escape and Tab keep their menu semantics: close / trap).\n const handleNestedKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === 'Escape' || event.key === 'Tab') return\n if (isTypingTarget(event.target)) event.stopPropagation()\n }\n\n const themeIcon = (id: UserMenuTheme) =>\n id === 'light' ? resolvedIcons.themeLight : id === 'dark' ? resolvedIcons.themeDark : resolvedIcons.themeSystem\n\n /** Counterpart reported when the active single-option row is toggled off. */\n const toggleCounterpart: Record<UserMenuTheme, UserMenuTheme> = {\n light: 'dark',\n dark: 'light',\n system: 'light',\n }\n\n const selectTheme = (id: UserMenuTheme) => {\n if (themeRows.length === 1) {\n // Single toggle row (\"Dark Mode\"): selecting the active option\n // means turning it off.\n onThemeChange?.(theme === id ? toggleCounterpart[id] : id)\n } else {\n onThemeChange?.(id)\n }\n }\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={cn(\n '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 triggerClassName,\n )}\n >\n {identityAvatarUrl ? (\n <img\n src={identityAvatarUrl}\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 {identityName || identityEmail ? (\n <span className=\"flex min-w-0 flex-col text-left leading-tight\">\n {identityName ? (\n <span className=\"max-w-40 truncate text-sm font-medium text-[var(--as-fg)]\">\n {identityName}\n </span>\n ) : null}\n {identityEmail ? (\n <span className=\"max-w-40 truncate text-xs text-[var(--as-muted-fg)]\">\n {identityEmail}\n </span>\n ) : null}\n </span>\n ) : null}\n </MenuTrigger>\n <MenuContent\n align={align}\n sideOffset={8}\n className={cn('w-60 min-w-48', contentClassName)}\n >\n <div className=\"contents\" onKeyDown={handleNestedKeyDown}>\n {switcher !== undefined ? (\n <>\n <div role=\"group\" className=\"p-1.5 pb-2\">\n {switcher}\n </div>\n <MenuSeparator />\n </>\n ) : null}\n\n {hasIdentity ? (\n <>\n {labels?.account ? <MenuLabel>{labels.account}</MenuLabel> : null}\n <div className=\"flex items-center gap-2.5 px-2 py-1.5\">\n {identityAvatarUrl ? (\n <img\n src={identityAvatarUrl}\n alt=\"\"\n className=\"size-9 shrink-0 rounded-full object-cover\"\n />\n ) : (\n <span\n aria-hidden\n className=\"flex size-9 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 <span className=\"flex min-w-0 flex-col\">\n {identityName ? (\n <span className=\"truncate text-sm font-bold text-[var(--as-fg)]\">\n {identityName}\n </span>\n ) : null}\n {identityEmail ? (\n <span className=\"truncate text-xs text-[var(--as-muted-fg)]\">\n {identityEmail}\n </span>\n ) : null}\n {badge ? (\n <span className=\"mt-0.5 inline-flex w-fit items-center rounded-full bg-[var(--as-secondary)] px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {badge}\n </span>\n ) : null}\n </span>\n </div>\n </>\n ) : null}\n\n {status !== undefined ? (\n <>\n {(switcher !== undefined || hasIdentity) && <MenuSeparator />}\n <div className=\"px-2 py-1.5\">\n <span\n data-as=\"user-menu-status\"\n className={cn(\n 'flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-xs font-medium',\n statusToneClass[status.tone ?? 'info'],\n )}\n >\n <span aria-hidden className=\"size-1.5 shrink-0 rounded-full bg-current\" />\n <span className=\"truncate\">{status.label}</span>\n </span>\n </div>\n </>\n ) : null}\n\n {(languageRows.length > 0 || themeRows.length > 0) && <MenuSeparator />}\n\n {languageRows.length > 0 ? (\n <MenuSub>\n <MenuSubTrigger>\n {labels?.language ?? 'Language'}\n <span className=\"ml-auto text-xs text-[var(--as-muted-fg)]\">\n {languageRows.find((lang) => lang.id === language)?.label}\n </span>\n </MenuSubTrigger>\n <MenuSubContent>\n {languageRows.map((lang) => (\n <MenuCheckboxItem\n key={`lang-${lang.id}`}\n checked={language === lang.id}\n onSelect={() => onLanguageChange?.(lang.id)}\n >\n {lang.label}\n </MenuCheckboxItem>\n ))}\n </MenuSubContent>\n </MenuSub>\n ) : null}\n\n {themeAsSubmenu ? (\n <MenuSub>\n <MenuSubTrigger>\n {labels?.theme ?? 'Theme'}\n <span className=\"ml-auto text-xs text-[var(--as-muted-fg)]\">\n {theme !== undefined ? themeLabels?.[theme] : undefined}\n </span>\n </MenuSubTrigger>\n <MenuSubContent>\n {themeRows.map((id) => (\n <MenuCheckboxItem\n key={`theme-${id}`}\n icon={themeIcon(id)}\n checked={theme === id}\n onSelect={() => selectTheme(id)}\n >\n {themeLabels?.[id]}\n </MenuCheckboxItem>\n ))}\n </MenuSubContent>\n </MenuSub>\n ) : (\n themeRows.map((id) => (\n <MenuCheckboxItem\n key={`theme-${id}`}\n icon={themeIcon(id)}\n checked={theme === id}\n onSelect={() => selectTheme(id)}\n >\n {themeLabels?.[id]}\n </MenuCheckboxItem>\n ))\n )}\n\n {menuItemList.length > 0 &&\n (switcher !== undefined || hasIdentity || status !== undefined || languageRows.length > 0 || themeRows.length > 0) ? (\n <MenuSeparator />\n ) : null}\n\n {menuItemList.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\n {onLogout ? (\n <>\n <MenuSeparator />\n <MenuItem danger icon={resolvedIcons.logout} onSelect={() => onLogout()}>\n {logoutLabel ?? 'Log out'}\n </MenuItem>\n </>\n ) : null}\n </div>\n </MenuContent>\n </Menu>\n </div>\n )\n },\n)\n"]}