@neuronection/assistant-ui 0.28.1 → 0.29.1

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 (38) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/about.d.ts +2 -1
  3. package/dist/badge.d.ts +1 -1
  4. package/dist/button.d.ts +1 -1
  5. package/dist/chat-branch-tree.d.ts +2 -2
  6. package/dist/chat-core.d.ts +3 -3
  7. package/dist/chat-message.d.ts +1 -1
  8. package/dist/chat-session-list.js +1 -1
  9. package/dist/chat-tools-catalog.js +1 -1
  10. package/dist/chat-trace-timeline.d.ts +12 -0
  11. package/dist/chat-trace-timeline.js +1 -1
  12. package/dist/chat-transcript.d.ts +1 -1
  13. package/dist/{chunk-NKYPNEOX.js → chunk-66K355IZ.js} +3 -3
  14. package/dist/chunk-66K355IZ.js.map +1 -0
  15. package/dist/chunk-HC6DBXYJ.js +236 -0
  16. package/dist/chunk-HC6DBXYJ.js.map +1 -0
  17. package/dist/{chunk-ZSCL34WT.js → chunk-TC4NQL3H.js} +12 -5
  18. package/dist/chunk-TC4NQL3H.js.map +1 -0
  19. package/dist/chunk-U5WSPZLX.js +133 -0
  20. package/dist/chunk-U5WSPZLX.js.map +1 -0
  21. package/dist/index.d.ts +2526 -91
  22. package/dist/index.js +7 -137
  23. package/dist/index.js.map +1 -1
  24. package/dist/logo.d.ts +9 -3
  25. package/dist/styles.css +1 -1
  26. package/dist/task-assignment-picker.d.ts +14 -1
  27. package/dist/theme-scope.d.ts +7 -1
  28. package/dist/{tree-YaIz73vo.d.ts → tree-4Hv9stjF.d.ts} +2 -2
  29. package/dist/{types-DZwzmyKE.d.ts → types-LhE1KFBf.d.ts} +1 -1
  30. package/dist/upload-dropzone.d.ts +24 -0
  31. package/dist/upload-dropzone.js +3 -0
  32. package/dist/upload-dropzone.js.map +1 -0
  33. package/package.json +3 -2
  34. package/dist/Logo-D8DEBUoL.d.ts +0 -11
  35. package/dist/chunk-5KFOQEOG.js +0 -154
  36. package/dist/chunk-5KFOQEOG.js.map +0 -1
  37. package/dist/chunk-NKYPNEOX.js.map +0 -1
  38. package/dist/chunk-ZSCL34WT.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @neuronection/assistant-ui
2
2
 
3
+ ## 0.29.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`6c8882c`](https://github.com/neuronection/assistant-ui/commit/6c8882cfc951a167b728b3977b5e1cffdda87a0b) Thanks [@constLiakos](https://github.com/constLiakos)! - fix(a11y): ChatSessionList and ChatToolsCatalog no longer render their empty/no-matches `<p>` states as children of `role="list"` (aria-required-children, critical — a list expects listitem children). The `role="list"` container now renders only when there are items to show; empty and no-results states render standalone with unchanged copy and styling. Found by desktop-assistant's new exclusion-free axe scans of the chat desktop window; regression axe tests for both components and a SessionListEmpty Ladle story added.
8
+
9
+ ## 0.29.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`e73579a`](https://github.com/neuronection/assistant-ui/commit/e73579ad0ab9795e7111a309d852b6042f2f5c2f) Thanks [@constLiakos](https://github.com/constLiakos)! - ChatTraceTimeline: full tool-call observability. `ChatTraceTimelineEntry`
14
+ gains optional `status` (`'ok' | 'error'` — renders a status dot after
15
+ the duration), `args` and `response` (pretty-printed strings) — `tool`
16
+ rows with `args`/`response` render a labelled, expandable detail region
17
+ (`Arguments` / `Response` blocks, `chat-trace-detail-toggle` button +
18
+ `chat-trace-detail` region, accessible name `"<tool> details"`). Labels
19
+ gain `arguments`, `response`, `details`. All fields optional — existing
20
+ usage renders unchanged.
21
+
22
+ ### Patch Changes
23
+
24
+ - [`5f06c3e`](https://github.com/neuronection/assistant-ui/commit/5f06c3e7fd9ca2cf419ad7dc5b79148edff4022d) Thanks [@constLiakos](https://github.com/constLiakos)! - fix(chat-tools-catalog): sentence-length scope values can no longer break the header row — the scope chip is capped at 40% of the row width with an ellipsis and exposes the full text via a native `title` tooltip, and the name/title slot wraps (`break-words`) instead of painting over the chip and chevron. Docs page, accessibility row, LongScopeAndName story, and a regression test added.
25
+
26
+ - [`732f29e`](https://github.com/neuronection/assistant-ui/commit/732f29e91edad217b7aa47edcdbd20ead7af75cc) Thanks [@constLiakos](https://github.com/constLiakos)! - fix(build): un-block the release gate — `pnpm build` no longer dies with `ERR_WORKER_OUT_OF_MEMORY` in tsup's dts worker (CI's 7 GB runner and memory-capped machines both hit it once the entry count reached ~80). Declaration emission now runs as sequential `dts: { only: true }` shards (`build:dts`, one tsup invocation + one dts worker each, `DTS_SHARD=0..N` over `tsup.dts.config.ts`, ~10 entries per shard) instead of one worker accumulating all 80 entries; JS output still builds as a single config, so chunk sharing is unchanged. tsup is `pnpm patch`ed to give the dts worker an explicit 3 GB heap (`resourceLimits.maxOldGenerationSizeMb`, overridable via `TSUP_DTS_WORKER_HEAP_MB`) because tsup spawns it without limits and Node sizes the default from total RAM, not cgroup/runner limits. Verified end-to-end under a 7 GB memory cap.
27
+
28
+ - [`487a853`](https://github.com/neuronection/assistant-ui/commit/487a853ded7d93440851392d00acafcba157da31) Thanks [@constLiakos](https://github.com/constLiakos)! - Fix the `./upload-dropzone` subpath export: the module shipped in the main barrel and was listed in the package `exports` map, but had no tsup entry, so `dist/upload-dropzone.*` was never built and subpath imports failed to resolve in consumer apps. Career-assistant imports it from the main barrel until the next release.
29
+
3
30
  ## 0.28.1
4
31
 
5
32
  ### Patch Changes
package/dist/about.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import * as class_variance_authority_types from 'class-variance-authority/types';
3
3
  import { VariantProps } from 'class-variance-authority';
4
- import { a as LogoTheme } from './Logo-D8DEBUoL.js';
5
4
 
6
5
  interface AboutCardProps extends Omit<React.ComponentProps<'div'>, 'title'> {
7
6
  icon?: React.ReactNode;
@@ -43,6 +42,8 @@ interface AboutFooterLineProps extends React.ComponentProps<'p'> {
43
42
  }
44
43
  declare const AboutFooterLine: React.ForwardRefExoticComponent<Omit<AboutFooterLineProps, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
45
44
 
45
+ type LogoTheme = 'light' | 'dark';
46
+
46
47
  type FamilyApp = 'health' | 'career' | 'study';
47
48
  interface FamilyMember {
48
49
  app: FamilyApp;
package/dist/badge.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const badgeVariants: (props?: ({
6
- variant?: "default" | "warning" | "success" | "danger" | "secondary" | "outline" | "ai" | null | undefined;
6
+ variant?: "warning" | "success" | "danger" | "default" | "secondary" | "outline" | "ai" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  interface BadgeProps extends React.ComponentProps<'span'>, VariantProps<typeof badgeVariants> {
9
9
  }
package/dist/button.d.ts CHANGED
@@ -4,7 +4,7 @@ import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
6
  variant?: "default" | "secondary" | "outline" | "ghost" | "destructive" | null | undefined;
7
- size?: "default" | "icon" | "sm" | "lg" | null | undefined;
7
+ size?: "sm" | "lg" | "default" | "icon" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  interface ButtonProps extends React.ComponentProps<'button'>, VariantProps<typeof buttonVariants> {
10
10
  asChild?: boolean;
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { LucideIcon } from 'lucide-react';
3
- import { b as BranchTree } from './tree-YaIz73vo.js';
4
- import './types-DZwzmyKE.js';
3
+ import { B as BranchTree } from './tree-4Hv9stjF.js';
4
+ import './types-LhE1KFBf.js';
5
5
 
6
6
  interface ChatBranchTreeIcons {
7
7
  user: LucideIcon;
@@ -1,6 +1,6 @@
1
- import { b as ChatError } from './types-DZwzmyKE.js';
2
- export { a as ChatAttachmentView, c as ChatMessageStatus, d as ChatMessageVariants, e as ChatMessageView, C as ChatRole } from './types-DZwzmyKE.js';
3
- export { B as BranchNode, a as BranchNodeInput, b as BranchTree, c as activePathSet, d as buildBranchTree, l as linearTree, v as variantInfo, w as walkActivePath } from './tree-YaIz73vo.js';
1
+ import { C as ChatError } from './types-LhE1KFBf.js';
2
+ export { a as ChatAttachmentView, b as ChatMessageStatus, c as ChatMessageVariants, d as ChatMessageView, e as ChatRole } from './types-LhE1KFBf.js';
3
+ export { a as BranchNode, b as BranchNodeInput, B as BranchTree, c as activePathSet, d as buildBranchTree, l as linearTree, v as variantInfo, w as walkActivePath } from './tree-4Hv9stjF.js';
4
4
 
5
5
  interface FlowStepInfo {
6
6
  id: string;
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { LucideIcon } from 'lucide-react';
3
- import { d as ChatMessageVariants, C as ChatRole, b as ChatError } from './types-DZwzmyKE.js';
3
+ import { c as ChatMessageVariants, e as ChatRole, C as ChatError } from './types-LhE1KFBf.js';
4
4
 
5
5
  interface ChatMessageEditorLabels {
6
6
  save: string;
@@ -1,3 +1,3 @@
1
- export { ChatSessionList } from './chunk-NKYPNEOX.js';
1
+ export { ChatSessionList } from './chunk-66K355IZ.js';
2
2
  //# sourceMappingURL=chat-session-list.js.map
3
3
  //# sourceMappingURL=chat-session-list.js.map
@@ -1,3 +1,3 @@
1
- export { ChatToolsCatalog } from './chunk-ZSCL34WT.js';
1
+ export { ChatToolsCatalog } from './chunk-TC4NQL3H.js';
2
2
  //# sourceMappingURL=chat-tools-catalog.js.map
3
3
  //# sourceMappingURL=chat-tools-catalog.js.map
@@ -9,6 +9,12 @@ interface ChatTraceTimelineEntry {
9
9
  detail?: string | null;
10
10
  startMs?: number | null;
11
11
  durationMs?: number | null;
12
+ /** Outcome of a tool execution (`tool` rows); renders a status dot. */
13
+ status?: 'ok' | 'error' | null;
14
+ /** Pretty-printed call arguments (expandable block on `tool` rows). */
15
+ args?: string | null;
16
+ /** Tool response text (expandable block on `tool` rows). */
17
+ response?: string | null;
12
18
  }
13
19
  interface ChatTraceTimelineTrace {
14
20
  model?: string | null;
@@ -29,6 +35,12 @@ interface ChatTraceTimelineLabels {
29
35
  /** Suffix of the token count, e.g. `tokens`. */
30
36
  tokens: string;
31
37
  reasoning: string;
38
+ /** Header of the expandable arguments block. */
39
+ arguments: string;
40
+ /** Header of the expandable response block. */
41
+ response: string;
42
+ /** Accessible name of a tool row's detail disclosure. */
43
+ details: string;
32
44
  }
33
45
  interface ChatTraceTimelineProps {
34
46
  trace: ChatTraceTimelineTrace;
@@ -1,3 +1,3 @@
1
- export { ChatTraceTimeline } from './chunk-5KFOQEOG.js';
1
+ export { ChatTraceTimeline } from './chunk-HC6DBXYJ.js';
2
2
  //# sourceMappingURL=chat-trace-timeline.js.map
3
3
  //# sourceMappingURL=chat-trace-timeline.js.map
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { e as ChatMessageView } from './types-DZwzmyKE.js';
2
+ import { d as ChatMessageView } from './types-LhE1KFBf.js';
3
3
 
4
4
  interface ChatTranscriptLabels {
5
5
  log: string;
@@ -111,7 +111,7 @@ var ChatSessionList = React.forwardRef(
111
111
  }
112
112
  ) : null
113
113
  ] }),
114
- /* @__PURE__ */ jsx("div", { role: "list", "aria-label": labels?.search ?? "Conversations", className: "min-h-0 flex-1 overflow-y-auto", children: sessions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-2 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.empty ?? "No conversations yet" }) : filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-2 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.noResults ?? "No matches" }) : groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
114
+ sessions.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-2 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.empty ?? "No conversations yet" }) : filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-2 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.noResults ?? "No matches" }) : /* @__PURE__ */ jsx("div", { role: "list", "aria-label": labels?.search ?? "Conversations", className: "min-h-0 flex-1 overflow-y-auto", children: groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
115
115
  group.label ? /* @__PURE__ */ jsx("p", { className: "px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]", children: group.label }) : null,
116
116
  group.sessions.map((session) => {
117
117
  const isActive = session.id === activeId;
@@ -194,5 +194,5 @@ var ChatSessionList = React.forwardRef(
194
194
  );
195
195
 
196
196
  export { ChatSessionList };
197
- //# sourceMappingURL=chunk-NKYPNEOX.js.map
198
- //# sourceMappingURL=chunk-NKYPNEOX.js.map
197
+ //# sourceMappingURL=chunk-66K355IZ.js.map
198
+ //# sourceMappingURL=chunk-66K355IZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-session-list/ChatSessionList.tsx"],"names":["ChatSessionList"],"mappings":"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","file":"chunk-66K355IZ.js","sourcesContent":["import * as React from 'react'\nimport { Download, MessageSquarePlus, Pencil, Search, Trash2 } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { formatDistanceToNow, isToday, isYesterday, parseISO } from 'date-fns'\n\nimport { cn } from '../../lib/utils'\nimport { searchScore } from '../../lib/fuzzy'\n\nexport interface ChatSessionView {\n id: string\n title: string\n updatedAt?: string | number | Date\n meta?: Record<string, unknown>\n}\n\nexport interface ChatSessionListLabels {\n search: string\n searchPlaceholder: string\n newChat: string\n rename: string\n delete: string\n export: string\n today: string\n yesterday: string\n earlier: string\n empty: string\n noResults: string\n}\n\nexport interface ChatSessionListIcons {\n newChat: LucideIcon\n rename: LucideIcon\n delete: LucideIcon\n export: LucideIcon\n}\n\nexport interface ChatSessionListProps {\n sessions: ChatSessionView[]\n activeId?: string | null\n onSelect: (id: string) => void\n onNew?: () => void\n onRename?: (id: string) => void\n onDelete?: (id: string) => void\n onExport?: (id: string) => void\n searchable?: boolean\n groupByDate?: boolean\n labels?: Partial<ChatSessionListLabels>\n icons?: Partial<ChatSessionListIcons>\n className?: string\n}\n\nfunction toDate(value: string | number | Date | undefined): Date | undefined {\n if (value === undefined) {\n return undefined\n }\n if (value instanceof Date) {\n return value\n }\n if (typeof value === 'number') {\n return new Date(value)\n }\n return parseISO(value)\n}\n\nfunction bucketOf(date: Date | undefined): 'today' | 'yesterday' | 'earlier' | 'none' {\n if (date === undefined) {\n return 'none'\n }\n if (isToday(date)) {\n return 'today'\n }\n if (isYesterday(date)) {\n return 'yesterday'\n }\n return 'earlier'\n}\n\n/**\n * The session list beside a full-page chat (study's `ChatSessionList` +\n * health's history overlay, generalized): fuzzy search, date grouping,\n * active marking, per-session actions (rename/delete/export) via the\n * menu when handlers are provided. All actions stay app-side.\n */\nexport const ChatSessionList = React.forwardRef<HTMLDivElement, ChatSessionListProps>(\n function ChatSessionList(\n {\n sessions,\n activeId,\n onSelect,\n onNew,\n onRename,\n onDelete,\n onExport,\n searchable = true,\n groupByDate = true,\n labels,\n icons,\n className,\n },\n ref,\n ) {\n const [query, setQuery] = React.useState('')\n const NewIcon = icons?.newChat ?? MessageSquarePlus\n const RenameIcon = icons?.rename ?? Pencil\n const DeleteIcon = icons?.delete ?? Trash2\n const ExportIcon = icons?.export ?? Download\n\n const filtered = React.useMemo(() => {\n const trimmed = query.trim()\n if (trimmed === '') {\n return sessions\n }\n return sessions\n .map((session) => ({\n session,\n score: searchScore(trimmed, session.title),\n }))\n .filter((entry) => entry.score !== null)\n .sort((a, b) => (b.score ?? 0) - (a.score ?? 0))\n .map((entry) => entry.session)\n }, [sessions, query])\n\n const groups = React.useMemo(() => {\n if (!groupByDate) {\n return [{ key: 'all', label: null as string | null, sessions: filtered }]\n }\n const ordered = [...filtered].sort((a, b) => {\n const dateA = toDate(a.updatedAt)?.getTime() ?? 0\n const dateB = toDate(b.updatedAt)?.getTime() ?? 0\n return dateB - dateA\n })\n const byBucket = new Map<string, ChatSessionView[]>()\n for (const session of ordered) {\n const bucket = bucketOf(toDate(session.updatedAt))\n const list = byBucket.get(bucket) ?? []\n list.push(session)\n byBucket.set(bucket, list)\n }\n const order = ['today', 'yesterday', 'earlier', 'none']\n return order\n .filter((bucket) => byBucket.has(bucket))\n .map((bucket) => ({\n key: bucket,\n label:\n bucket === 'today'\n ? (labels?.today ?? 'Today')\n : bucket === 'yesterday'\n ? (labels?.yesterday ?? 'Yesterday')\n : bucket === 'earlier'\n ? (labels?.earlier ?? 'Earlier')\n : null,\n sessions: byBucket.get(bucket) ?? [],\n }))\n }, [filtered, groupByDate, labels])\n\n return (\n <div ref={ref} data-as=\"chat-session-list\" className={cn('flex h-full min-h-0 w-full flex-col gap-2', className)}>\n <div className=\"flex items-center gap-2\">\n {searchable ? (\n <div className=\"relative min-w-0 flex-1\">\n <Search className=\"pointer-events-none absolute start-2 top-1/2 size-3.5 -translate-y-1/2 text-[var(--as-muted-fg)]\" aria-hidden />\n <input\n type=\"search\"\n role=\"searchbox\"\n value={query}\n aria-label={labels?.search ?? 'Search conversations'}\n placeholder={labels?.searchPlaceholder ?? 'Search…'}\n onChange={(event) => setQuery(event.target.value)}\n className=\"w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] py-1.5 pe-2 ps-8 text-xs text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n />\n </div>\n ) : null}\n {onNew ? (\n <button\n type=\"button\"\n onClick={onNew}\n aria-label={labels?.newChat ?? 'New conversation'}\n title={labels?.newChat ?? 'New conversation'}\n className=\"flex size-7 shrink-0 items-center justify-center rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <NewIcon className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n {sessions.length === 0 ? (\n <p className=\"px-2 py-3 text-xs text-[var(--as-muted-fg)]\">{labels?.empty ?? 'No conversations yet'}</p>\n ) : filtered.length === 0 ? (\n <p className=\"px-2 py-3 text-xs text-[var(--as-muted-fg)]\">{labels?.noResults ?? 'No matches'}</p>\n ) : (\n <div role=\"list\" aria-label={labels?.search ?? 'Conversations'} className=\"min-h-0 flex-1 overflow-y-auto\">\n {groups.map((group) => (\n <div key={group.key} className=\"mb-2\">\n {group.label ? (\n <p className=\"px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {group.label}\n </p>\n ) : null}\n {group.sessions.map((session) => {\n const isActive = session.id === activeId\n const date = toDate(session.updatedAt)\n const actions =\n onRename || onDelete || onExport\n ? [\n onRename\n ? {\n key: 'rename',\n label: labels?.rename ?? 'Rename',\n icon: <RenameIcon className=\"size-3.5\" aria-hidden />,\n onSelect: () => onRename(session.id),\n }\n : null,\n onExport\n ? {\n key: 'export',\n label: labels?.export ?? 'Export',\n icon: <ExportIcon className=\"size-3.5\" aria-hidden />,\n onSelect: () => onExport(session.id),\n }\n : null,\n onDelete\n ? {\n key: 'delete',\n label: labels?.delete ?? 'Delete',\n icon: <DeleteIcon className=\"size-3.5\" aria-hidden />,\n onSelect: () => onDelete(session.id),\n danger: true,\n }\n : null,\n ].filter((item): item is NonNullable<typeof item> => item !== null)\n : []\n return (\n <div\n key={session.id}\n role=\"listitem\"\n aria-current={isActive ? 'true' : undefined}\n data-active={isActive || undefined}\n className={cn(\n 'group/session flex cursor-pointer items-center gap-2 rounded-[var(--as-radius)] px-2 py-1.5 text-xs transition-colors hover:bg-[var(--as-surface-raised)]',\n isActive && 'bg-[var(--as-surface-raised)] font-medium text-[var(--as-fg)]',\n )}\n onClick={() => onSelect(session.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onSelect(session.id)\n }\n }}\n tabIndex={0}\n >\n <span className=\"min-w-0 flex-1 truncate text-[var(--as-fg)]\">{session.title}</span>\n {date ? (\n <span className=\"shrink-0 text-[10px] text-[var(--as-muted-fg)]\">\n {formatDistanceToNow(date, { addSuffix: true })}\n </span>\n ) : null}\n {actions.length > 0 ? (\n <span\n role=\"group\"\n aria-label={labels?.rename ?? 'Conversation actions'}\n className=\"flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity focus-within:opacity-100 group-hover/session:opacity-100\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => event.stopPropagation()}\n >\n {actions.map((action) => (\n <button\n key={action.key}\n type=\"button\"\n aria-label={action.label}\n title={action.label}\n onClick={action.onSelect}\n className={cn(\n 'rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-surface)] 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 action.danger && 'hover:text-[var(--as-danger)]',\n )}\n >\n {action.icon}\n </button>\n ))}\n </span>\n ) : null}\n </div>\n )\n })}\n </div>\n ))\n }\n </div>\n )}\n </div>\n )\n },\n)\n"]}
@@ -0,0 +1,236 @@
1
+ import { cn } from './chunk-BLJWR4ZV.js';
2
+ import * as React from 'react';
3
+ import { Timer, ChevronDown, ChevronRight, Brain } from 'lucide-react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ function formatDuration(ms) {
7
+ if (ms < 1e3) {
8
+ return `${Math.round(ms)} ms`;
9
+ }
10
+ return `${(ms / 1e3).toFixed(1)} s`;
11
+ }
12
+ var ChatTraceTimeline = React.forwardRef(
13
+ function ChatTraceTimeline2({ trace, entries, defaultOpen = false, labels, className }, ref) {
14
+ const [open, setOpen] = React.useState(defaultOpen);
15
+ const totalMs = Math.max(1, trace.latencyMs ?? 0);
16
+ const duration = formatDuration(trace.latencyMs ?? 0);
17
+ const toolsLabel = labels?.tools ?? "tools";
18
+ const toolCount = entries.filter((entry) => entry.kind === "tool").length;
19
+ const items = [...entries].sort(
20
+ (a, b) => (a.startMs ?? 0) - (b.startMs ?? 0)
21
+ );
22
+ return /* @__PURE__ */ jsxs(
23
+ "div",
24
+ {
25
+ ref,
26
+ "data-as": "chat-trace-timeline",
27
+ className: cn("w-full max-w-[92%]", className),
28
+ children: [
29
+ /* @__PURE__ */ jsxs(
30
+ "button",
31
+ {
32
+ type: "button",
33
+ onClick: () => setOpen((value) => !value),
34
+ "aria-expanded": open,
35
+ "aria-label": labels?.toggle ?? "Show response trace",
36
+ className: "flex items-center gap-1.5 text-[11px] text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
37
+ children: [
38
+ /* @__PURE__ */ jsx(Timer, { className: "size-3.5 shrink-0", "aria-hidden": true }),
39
+ /* @__PURE__ */ jsx("span", { children: `${duration} \xB7 ${toolCount} ${toolsLabel}` }),
40
+ trace.model ? /* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px]", children: [
41
+ "\xB7 ",
42
+ trace.model
43
+ ] }) : null,
44
+ /* @__PURE__ */ jsx(
45
+ ChevronDown,
46
+ {
47
+ className: cn(
48
+ "size-3.5 shrink-0 transition-transform duration-150",
49
+ open && "rotate-180"
50
+ ),
51
+ "aria-hidden": true
52
+ }
53
+ )
54
+ ]
55
+ }
56
+ ),
57
+ open ? /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-col gap-1.5 overflow-hidden", children: [
58
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-[11px] text-[var(--as-muted-fg)]", children: [
59
+ /* @__PURE__ */ jsx("span", { children: `${labels?.total ?? "Total"} ${duration}` }),
60
+ /* @__PURE__ */ jsx("span", { children: `${trace.outputTokens ?? 0} ${labels?.tokens ?? "tokens"}` })
61
+ ] }),
62
+ items.map((item, index) => /* @__PURE__ */ jsx(TraceRow, { item, totalMs, labels }, `${item.kind}-${index}`)),
63
+ trace.thinking ? /* @__PURE__ */ jsx(
64
+ ThinkingDisclosure,
65
+ {
66
+ thinking: trace.thinking,
67
+ label: labels?.reasoning ?? "Reasoning"
68
+ }
69
+ ) : null
70
+ ] }) : null
71
+ ]
72
+ }
73
+ );
74
+ }
75
+ );
76
+ function TraceRow({
77
+ item,
78
+ totalMs,
79
+ labels
80
+ }) {
81
+ const [open, setOpen] = React.useState(false);
82
+ const regionId = React.useId();
83
+ const expandable = Boolean(item.args || item.response);
84
+ const width = Math.max(2, Math.min(100, (item.durationMs ?? 0) / totalMs * 100));
85
+ return /* @__PURE__ */ jsxs("div", { "data-kind": item.kind, "data-status": item.status ?? void 0, children: [
86
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[11px]", children: [
87
+ expandable ? /* @__PURE__ */ jsx(
88
+ "button",
89
+ {
90
+ type: "button",
91
+ "aria-expanded": open,
92
+ "aria-controls": regionId,
93
+ title: item.detail ?? void 0,
94
+ className: "w-16 shrink-0 truncate text-left text-[11px] text-[var(--as-fg)] transition-colors hover:text-[var(--as-primary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
95
+ onClick: () => setOpen((value) => !value),
96
+ children: item.label
97
+ }
98
+ ) : /* @__PURE__ */ jsx(
99
+ "span",
100
+ {
101
+ className: "w-16 shrink-0 truncate text-[var(--as-muted-fg)]",
102
+ title: item.detail ?? void 0,
103
+ children: item.label
104
+ }
105
+ ),
106
+ /* @__PURE__ */ jsx("div", { className: "h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-[var(--as-surface-raised)]", children: /* @__PURE__ */ jsx(
107
+ "div",
108
+ {
109
+ className: cn(
110
+ "h-full rounded-full",
111
+ item.kind === "phase" ? "bg-[var(--as-primary)]" : "bg-[var(--as-warning)]"
112
+ ),
113
+ style: { width: `${width}%` }
114
+ }
115
+ ) }),
116
+ /* @__PURE__ */ jsx("span", { className: "w-12 shrink-0 text-right font-mono text-[10px] text-[var(--as-muted-fg)]", children: formatDuration(item.durationMs ?? 0) }),
117
+ item.status ? /* @__PURE__ */ jsx(
118
+ "span",
119
+ {
120
+ "data-as": "chat-trace-status",
121
+ title: item.status,
122
+ className: cn(
123
+ "size-2 shrink-0 rounded-full",
124
+ item.status === "error" ? "bg-[var(--as-danger)]" : "bg-[var(--as-success)]"
125
+ )
126
+ }
127
+ ) : /* @__PURE__ */ jsx("span", { className: "size-2 shrink-0" }),
128
+ expandable ? /* @__PURE__ */ jsx(
129
+ "button",
130
+ {
131
+ type: "button",
132
+ "data-as": "chat-trace-detail-toggle",
133
+ "aria-expanded": open,
134
+ "aria-controls": regionId,
135
+ "aria-label": `${item.label} ${labels?.details ?? "details"}`,
136
+ onClick: () => setOpen((value) => !value),
137
+ className: "flex size-5 shrink-0 items-center justify-center rounded text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
138
+ children: open ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-3", "aria-hidden": true }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-3", "aria-hidden": true })
139
+ }
140
+ ) : /* @__PURE__ */ jsx("span", { className: "w-5 shrink-0" })
141
+ ] }),
142
+ expandable && open ? /* @__PURE__ */ jsxs(
143
+ "div",
144
+ {
145
+ id: regionId,
146
+ "data-as": "chat-trace-detail",
147
+ "data-open": "true",
148
+ className: "mb-1.5 ml-[4.5rem] border-l border-[var(--as-border)] pl-2",
149
+ children: [
150
+ item.args ? /* @__PURE__ */ jsx(DetailBlock, { label: labels?.arguments ?? "Arguments", text: item.args }) : null,
151
+ item.response ? /* @__PURE__ */ jsx(DetailBlock, { label: labels?.response ?? "Response", text: item.response }) : null
152
+ ]
153
+ }
154
+ ) : null
155
+ ] });
156
+ }
157
+ function detailEntries(text) {
158
+ try {
159
+ const parsed = JSON.parse(text);
160
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
161
+ const entries = Object.entries(parsed).map(([key, value]) => ({
162
+ key,
163
+ value: typeof value === "string" ? value : JSON.stringify(value)
164
+ }));
165
+ return entries.length > 0 ? entries : null;
166
+ }
167
+ return null;
168
+ } catch {
169
+ return null;
170
+ }
171
+ }
172
+ function DetailBlock({
173
+ label,
174
+ text
175
+ }) {
176
+ const entries = detailEntries(text);
177
+ return /* @__PURE__ */ jsxs("div", { className: "pt-1", children: [
178
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]", children: label }),
179
+ entries ? /* @__PURE__ */ jsx("dl", { className: "mt-0.5 space-y-0.5", children: entries.map((entry) => /* @__PURE__ */ jsxs(
180
+ "div",
181
+ {
182
+ className: "flex items-baseline justify-between gap-2 rounded-md bg-[var(--as-muted)] px-2 py-1",
183
+ children: [
184
+ /* @__PURE__ */ jsx("dt", { className: "text-[10px] uppercase tracking-wide text-[var(--as-muted-fg)]", children: entry.key }),
185
+ /* @__PURE__ */ jsx("dd", { className: "min-w-0 break-words text-right text-[11px] font-medium text-[var(--as-fg)]", children: entry.value })
186
+ ]
187
+ },
188
+ entry.key
189
+ )) }) : /* @__PURE__ */ jsx("p", { className: "mt-0.5 whitespace-pre-wrap break-words text-[11px] text-[var(--as-fg)]", children: text })
190
+ ] });
191
+ }
192
+ function ThinkingDisclosure({
193
+ thinking,
194
+ label
195
+ }) {
196
+ const [open, setOpen] = React.useState(false);
197
+ const regionId = React.useId();
198
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)]", children: [
199
+ /* @__PURE__ */ jsxs(
200
+ "button",
201
+ {
202
+ type: "button",
203
+ onClick: () => setOpen((value) => !value),
204
+ "aria-expanded": open,
205
+ "aria-controls": regionId,
206
+ className: "flex w-full items-center gap-1.5 rounded-[var(--as-radius)] px-2 py-1.5 text-left text-[11px] text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
207
+ children: [
208
+ /* @__PURE__ */ jsx(Brain, { className: "size-3.5 shrink-0", "aria-hidden": true }),
209
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: label }),
210
+ /* @__PURE__ */ jsx(
211
+ ChevronDown,
212
+ {
213
+ className: cn(
214
+ "size-3.5 shrink-0 transition-transform duration-150",
215
+ open && "rotate-180"
216
+ ),
217
+ "aria-hidden": true
218
+ }
219
+ )
220
+ ]
221
+ }
222
+ ),
223
+ open ? /* @__PURE__ */ jsx(
224
+ "p",
225
+ {
226
+ id: regionId,
227
+ className: "whitespace-pre-wrap px-2.5 pb-2 text-[11px] text-[var(--as-muted-fg)]",
228
+ children: thinking
229
+ }
230
+ ) : null
231
+ ] });
232
+ }
233
+
234
+ export { ChatTraceTimeline };
235
+ //# sourceMappingURL=chunk-HC6DBXYJ.js.map
236
+ //# sourceMappingURL=chunk-HC6DBXYJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-trace-timeline/ChatTraceTimeline.tsx"],"names":["ChatTraceTimeline"],"mappings":"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","file":"chunk-HC6DBXYJ.js","sourcesContent":["import * as React from 'react'\nimport { Brain, ChevronDown, ChevronRight, Timer } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatTraceTimelineEntry {\n /** `phase` = model/flow round (primary bar); `tool` = tool call (warning bar). */\n kind: 'phase' | 'tool'\n /** Phase label or tool name (translated at the call site). */\n label: string\n /** Secondary detail, e.g. the tool arguments summary (title tooltip). */\n detail?: string | null\n startMs?: number | null\n durationMs?: number | null\n /** Outcome of a tool execution (`tool` rows); renders a status dot. */\n status?: 'ok' | 'error' | null\n /** Pretty-printed call arguments (expandable block on `tool` rows). */\n args?: string | null\n /** Tool response text (expandable block on `tool` rows). */\n response?: string | null\n}\n\nexport interface ChatTraceTimelineTrace {\n model?: string | null\n /** Total turn latency the bars are proportional to. */\n latencyMs?: number | null\n inputTokens?: number | null\n outputTokens?: number | null\n /** Raw model reasoning, shown in a nested disclosure. */\n thinking?: string | null\n}\n\nexport interface ChatTraceTimelineLabels {\n /** Accessible name of the toggle (summary is composed from data). */\n toggle: string\n /** Word appended to the tool count, e.g. `tools`. */\n tools: string\n /** Prefix of the expanded total row, e.g. `Total`. */\n total: string\n /** Suffix of the token count, e.g. `tokens`. */\n tokens: string\n reasoning: string\n /** Header of the expandable arguments block. */\n arguments: string\n /** Header of the expandable response block. */\n response: string\n /** Accessible name of a tool row's detail disclosure. */\n details: string\n}\n\nexport interface ChatTraceTimelineProps {\n trace: ChatTraceTimelineTrace\n entries: ChatTraceTimelineEntry[]\n defaultOpen?: boolean\n labels?: Partial<ChatTraceTimelineLabels>\n className?: string\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`\n }\n return `${(ms / 1000).toFixed(1)} s`\n}\n\n/**\n * Collapsible per-turn trace timeline (study's `TraceTimeline`,\n * generalized): collapsed summary (duration · N tools · model), expanded\n * duration-proportional bar rows for flow phases and tool calls plus a\n * raw-reasoning disclosure. Presentational — persisted or live trace data\n * is mapped to `entries` app-side.\n */\nexport const ChatTraceTimeline = React.forwardRef<HTMLDivElement, ChatTraceTimelineProps>(\n function ChatTraceTimeline(\n { trace, entries, defaultOpen = false, labels, className },\n ref,\n ) {\n const [open, setOpen] = React.useState(defaultOpen)\n const totalMs = Math.max(1, trace.latencyMs ?? 0)\n const duration = formatDuration(trace.latencyMs ?? 0)\n const toolsLabel = labels?.tools ?? 'tools'\n const toolCount = entries.filter((entry) => entry.kind === 'tool').length\n const items = [...entries].sort(\n (a, b) => (a.startMs ?? 0) - (b.startMs ?? 0),\n )\n\n return (\n <div\n ref={ref}\n data-as=\"chat-trace-timeline\"\n className={cn('w-full max-w-[92%]', className)}\n >\n <button\n type=\"button\"\n onClick={() => setOpen((value) => !value)}\n aria-expanded={open}\n aria-label={labels?.toggle ?? 'Show response trace'}\n className=\"flex items-center gap-1.5 text-[11px] text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <Timer className=\"size-3.5 shrink-0\" aria-hidden />\n <span>\n {`${duration} · ${toolCount} ${toolsLabel}`}\n </span>\n {trace.model ? (\n <span className=\"font-mono text-[10px]\">· {trace.model}</span>\n ) : null}\n <ChevronDown\n className={cn(\n 'size-3.5 shrink-0 transition-transform duration-150',\n open && 'rotate-180',\n )}\n aria-hidden\n />\n </button>\n {open ? (\n <div className=\"mt-2 flex flex-col gap-1.5 overflow-hidden\">\n <div className=\"flex items-center justify-between text-[11px] text-[var(--as-muted-fg)]\">\n <span>{`${labels?.total ?? 'Total'} ${duration}`}</span>\n <span>\n {`${trace.outputTokens ?? 0} ${labels?.tokens ?? 'tokens'}`}\n </span>\n </div>\n {items.map((item, index) => (\n <TraceRow key={`${item.kind}-${index}`} item={item} totalMs={totalMs} labels={labels} />\n ))}\n {trace.thinking ? (\n <ThinkingDisclosure\n thinking={trace.thinking}\n label={labels?.reasoning ?? 'Reasoning'}\n />\n ) : null}\n </div>\n ) : null}\n </div>\n )\n },\n)\n\nfunction TraceRow({\n item,\n totalMs,\n labels,\n}: {\n item: ChatTraceTimelineEntry\n totalMs: number\n labels?: Partial<ChatTraceTimelineLabels>\n}) {\n const [open, setOpen] = React.useState(false)\n const regionId = React.useId()\n const expandable = Boolean(item.args || item.response)\n const width = Math.max(2, Math.min(100, ((item.durationMs ?? 0) / totalMs) * 100))\n return (\n <div data-kind={item.kind} data-status={item.status ?? undefined}>\n <div className=\"flex items-center gap-2 text-[11px]\">\n {expandable ? (\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={regionId}\n title={item.detail ?? undefined}\n className=\"w-16 shrink-0 truncate text-left text-[11px] text-[var(--as-fg)] transition-colors hover:text-[var(--as-primary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n onClick={() => setOpen((value) => !value)}\n >\n {item.label}\n </button>\n ) : (\n <span\n className=\"w-16 shrink-0 truncate text-[var(--as-muted-fg)]\"\n title={item.detail ?? undefined}\n >\n {item.label}\n </span>\n )}\n <div className=\"h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-[var(--as-surface-raised)]\">\n <div\n className={cn(\n 'h-full rounded-full',\n item.kind === 'phase'\n ? 'bg-[var(--as-primary)]'\n : 'bg-[var(--as-warning)]',\n )}\n style={{ width: `${width}%` }}\n />\n </div>\n <span className=\"w-12 shrink-0 text-right font-mono text-[10px] text-[var(--as-muted-fg)]\">\n {formatDuration(item.durationMs ?? 0)}\n </span>\n {item.status ? (\n <span\n data-as=\"chat-trace-status\"\n title={item.status}\n className={cn(\n 'size-2 shrink-0 rounded-full',\n item.status === 'error'\n ? 'bg-[var(--as-danger)]'\n : 'bg-[var(--as-success)]',\n )}\n />\n ) : (\n <span className=\"size-2 shrink-0\" />\n )}\n {expandable ? (\n <button\n type=\"button\"\n data-as=\"chat-trace-detail-toggle\"\n aria-expanded={open}\n aria-controls={regionId}\n aria-label={`${item.label} ${labels?.details ?? 'details'}`}\n onClick={() => setOpen((value) => !value)}\n className=\"flex size-5 shrink-0 items-center justify-center rounded text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-muted)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n {open ? (\n <ChevronDown className=\"size-3\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3\" aria-hidden />\n )}\n </button>\n ) : (\n <span className=\"w-5 shrink-0\" />\n )}\n </div>\n {expandable && open ? (\n <div\n id={regionId}\n data-as=\"chat-trace-detail\"\n data-open=\"true\"\n className=\"mb-1.5 ml-[4.5rem] border-l border-[var(--as-border)] pl-2\"\n >\n {item.args ? (\n <DetailBlock label={labels?.arguments ?? 'Arguments'} text={item.args} />\n ) : null}\n {item.response ? (\n <DetailBlock label={labels?.response ?? 'Response'} text={item.response} />\n ) : null}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction detailEntries(text: string): Array<{ key: string; value: string }> | null {\n try {\n const parsed = JSON.parse(text) as unknown\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const entries = Object.entries(parsed as Record<string, unknown>).map(([key, value]) => ({\n key,\n value: typeof value === 'string' ? value : JSON.stringify(value),\n }))\n return entries.length > 0 ? entries : null\n }\n return null\n } catch {\n return null\n }\n}\n\nfunction DetailBlock({\n label,\n text,\n}: {\n label: string\n text: string\n}) {\n const entries = detailEntries(text)\n return (\n <div className=\"pt-1\">\n <p className=\"text-[10px] font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {label}\n </p>\n {entries ? (\n <dl className=\"mt-0.5 space-y-0.5\">\n {entries.map((entry) => (\n <div\n key={entry.key}\n className=\"flex items-baseline justify-between gap-2 rounded-md bg-[var(--as-muted)] px-2 py-1\"\n >\n <dt className=\"text-[10px] uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {entry.key}\n </dt>\n <dd className=\"min-w-0 break-words text-right text-[11px] font-medium text-[var(--as-fg)]\">\n {entry.value}\n </dd>\n </div>\n ))}\n </dl>\n ) : (\n <p className=\"mt-0.5 whitespace-pre-wrap break-words text-[11px] text-[var(--as-fg)]\">\n {text}\n </p>\n )}\n </div>\n )\n}\n\nfunction ThinkingDisclosure({\n thinking,\n label,\n}: {\n thinking: string\n label: string\n}) {\n const [open, setOpen] = React.useState(false)\n const regionId = React.useId()\n return (\n <div className=\"rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)]\">\n <button\n type=\"button\"\n onClick={() => setOpen((value) => !value)}\n aria-expanded={open}\n aria-controls={regionId}\n className=\"flex w-full items-center gap-1.5 rounded-[var(--as-radius)] px-2 py-1.5 text-left text-[11px] text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <Brain className=\"size-3.5 shrink-0\" aria-hidden />\n <span className=\"flex-1\">{label}</span>\n <ChevronDown\n className={cn(\n 'size-3.5 shrink-0 transition-transform duration-150',\n open && 'rotate-180',\n )}\n aria-hidden\n />\n </button>\n {open ? (\n <p\n id={regionId}\n className=\"whitespace-pre-wrap px-2.5 pb-2 text-[11px] text-[var(--as-muted-fg)]\"\n >\n {thinking}\n </p>\n ) : null}\n </div>\n )\n}\n"]}
@@ -58,7 +58,7 @@ var ChatToolsCatalog = React.forwardRef(
58
58
  }
59
59
  )
60
60
  ] }) : null,
61
- /* @__PURE__ */ jsx("div", { role: "list", "aria-label": labels?.tools ?? "Tools", className: "flex flex-col gap-2", children: tools.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-1 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.empty ?? "No tools available" }) : filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-1 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.noResults ?? "No matching tools" }) : filtered.map((tool) => {
61
+ tools.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-1 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.empty ?? "No tools available" }) : filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-1 py-3 text-xs text-[var(--as-muted-fg)]", children: labels?.noResults ?? "No matching tools" }) : /* @__PURE__ */ jsx("div", { role: "list", "aria-label": labels?.tools ?? "Tools", className: "flex flex-col gap-2", children: filtered.map((tool) => {
62
62
  const open = openOverrides[tool.name] ?? defaultOpen;
63
63
  const regionId = `${uid}-${tool.name}`;
64
64
  const toggle = () => setOpenOverrides((current) => ({ ...current, [tool.name]: !open }));
@@ -80,11 +80,18 @@ var ChatToolsCatalog = React.forwardRef(
80
80
  className: "flex w-full items-center gap-2 px-2.5 py-1.5 text-left focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
81
81
  children: [
82
82
  /* @__PURE__ */ jsx(Icon, { className: "size-3.5 shrink-0 text-[var(--as-muted-fg)]", "aria-hidden": true }),
83
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
83
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 break-words", children: [
84
84
  /* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-[var(--as-fg)]", children: tool.name }),
85
85
  tool.title ? /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-[var(--as-muted-fg)]", children: tool.title }) : null
86
86
  ] }),
87
- tool.scope ? /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-[var(--as-surface-raised)] px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-[var(--as-muted-fg)]", children: tool.scope }) : null,
87
+ tool.scope ? /* @__PURE__ */ jsx(
88
+ "span",
89
+ {
90
+ title: tool.scope,
91
+ className: "max-w-[40%] shrink-0 truncate rounded-full bg-[var(--as-surface-raised)] px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-[var(--as-muted-fg)]",
92
+ children: tool.scope
93
+ }
94
+ ) : null,
88
95
  /* @__PURE__ */ jsx(
89
96
  ChevronDown,
90
97
  {
@@ -145,5 +152,5 @@ var ChatToolsCatalog = React.forwardRef(
145
152
  );
146
153
 
147
154
  export { ChatToolsCatalog };
148
- //# sourceMappingURL=chunk-ZSCL34WT.js.map
149
- //# sourceMappingURL=chunk-ZSCL34WT.js.map
155
+ //# sourceMappingURL=chunk-TC4NQL3H.js.map
156
+ //# sourceMappingURL=chunk-TC4NQL3H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-tools-catalog/ChatToolsCatalog.tsx"],"names":["ChatToolsCatalog"],"mappings":";;;;;;AAuDO,IAAM,gBAAA,GAAyB,KAAA,CAAA,UAAA;AAAA,EACpC,SAASA,iBAAAA,CACP;AAAA,IACE,KAAA;AAAA,IACA,UAAA,GAAa,IAAA;AAAA,IACb,WAAA,GAAc,KAAA;AAAA,IACd,MAAA;AAAA,IACA,MAAM,IAAA,GAAO,MAAA;AAAA,IACb;AAAA,KAEF,GAAA,EACA;AACA,IAAA,MAAM,MAAY,KAAA,CAAA,KAAA,EAAM;AACxB,IAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAU,eAAS,EAAE,CAAA;AAC3C,IAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAU,KAAA,CAAA,QAAA,CAAkC,EAAE,CAAA;AAEpF,IAAA,MAAM,QAAA,GAAiB,cAAQ,MAAM;AACnC,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,MAAA,IAAI,YAAY,EAAA,EAAI;AAClB,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,OAAO,KAAA,CACJ,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,QACd,IAAA;AAAA,QACA,OAAO,IAAA,CAAK,GAAA;AAAA,UACV,YAAY,OAAA,EAAS,IAAA,CAAK,KAAA,IAAS,IAAA,CAAK,IAAI,CAAA,IAAK,EAAA;AAAA,UACjD,WAAA,CAAY,OAAA,EAAS,IAAA,CAAK,IAAI,CAAA,IAAK,EAAA;AAAA,UACnC,WAAA,CAAY,OAAA,EAAS,IAAA,CAAK,WAAA,IAAe,EAAE,CAAA,IAAK;AAAA;AAClD,OACF,CAAE,EACD,MAAA,CAAO,CAAC,UAAU,KAAA,CAAM,KAAA,IAAS,CAAC,CAAA,CAClC,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAK,EAChC,GAAA,CAAI,CAAC,KAAA,KAAU,KAAA,CAAM,IAAI,CAAA;AAAA,IAC9B,CAAA,EAAG,CAAC,KAAA,EAAO,KAAK,CAAC,CAAA;AAEjB,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,oBAAA;AAAA,QACR,SAAA,EAAW,EAAA,CAAG,oCAAA,EAAsC,SAAS,CAAA;AAAA,QAE5D,QAAA,EAAA;AAAA,UAAA,UAAA,mBACC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mBAAA,EACb,QAAA,EAAA;AAAA,4BAAA,GAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAU,kGAAA;AAAA,gBACV,aAAA,EAAW;AAAA;AAAA,aACb;AAAA,4BACA,GAAA;AAAA,cAAC,OAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,IAAA,EAAK,WAAA;AAAA,gBACL,KAAA,EAAO,KAAA;AAAA,gBACP,YAAA,EAAY,QAAQ,MAAA,IAAU,cAAA;AAAA,gBAC9B,WAAA,EAAa,QAAQ,iBAAA,IAAqB,cAAA;AAAA,gBAC1C,UAAU,CAAC,KAAA,KAAU,QAAA,CAAS,KAAA,CAAM,OAAO,KAAK,CAAA;AAAA,gBAChD,SAAA,EAAU;AAAA;AAAA;AACZ,WAAA,EACF,CAAA,GACE,IAAA;AAAA,UACH,KAAA,CAAM,MAAA,KAAW,CAAA,mBAChB,GAAA,CAAC,OAAE,SAAA,EAAU,6CAAA,EACV,QAAA,EAAA,MAAA,EAAQ,KAAA,IAAS,sBACpB,CAAA,GACE,QAAA,CAAS,MAAA,KAAW,CAAA,uBACrB,GAAA,EAAA,EAAE,SAAA,EAAU,6CAAA,EACV,QAAA,EAAA,MAAA,EAAQ,aAAa,mBAAA,EACxB,CAAA,mBAEA,GAAA,CAAC,KAAA,EAAA,EAAI,MAAK,MAAA,EAAO,YAAA,EAAY,MAAA,EAAQ,KAAA,IAAS,SAAS,SAAA,EAAU,qBAAA,EAC9D,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,IAAA,KAAS;AACtB,YAAA,MAAM,IAAA,GAAO,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA,IAAK,WAAA;AACzC,YAAA,MAAM,QAAA,GAAW,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,KAAK,IAAI,CAAA,CAAA;AACpC,YAAA,MAAM,MAAA,GAAS,MACb,gBAAA,CAAiB,CAAC,aAAa,EAAE,GAAG,OAAA,EAAS,CAAC,IAAA,CAAK,IAAI,GAAG,CAAC,MAAK,CAAE,CAAA;AACpE,YAAA,uBACE,IAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBAEC,IAAA,EAAK,UAAA;AAAA,gBACL,SAAA,EAAQ,0BAAA;AAAA,gBACR,aAAW,IAAA,IAAQ,MAAA;AAAA,gBACnB,SAAA,EAAU,4FAAA;AAAA,gBAEV,QAAA,EAAA;AAAA,kCAAA,IAAA;AAAA,oBAAC,QAAA;AAAA,oBAAA;AAAA,sBACC,IAAA,EAAK,QAAA;AAAA,sBACL,eAAA,EAAe,IAAA;AAAA,sBACf,eAAA,EAAe,QAAA;AAAA,sBACf,OAAA,EAAS,MAAA;AAAA,sBACT,SAAA,EAAU,4JAAA;AAAA,sBAEV,QAAA,EAAA;AAAA,wCAAA,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,6CAAA,EAA8C,aAAA,EAAW,IAAA,EAAC,CAAA;AAAA,wCAC1E,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,4BAAA,EACd,QAAA,EAAA;AAAA,0CAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,2CAAA,EAA6C,QAAA,EAAA,IAAA,CAAK,IAAA,EAAK,CAAA;AAAA,0BACtE,IAAA,CAAK,wBACJ,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,kCAAA,EAAoC,QAAA,EAAA,IAAA,CAAK,OAAM,CAAA,GAC7D;AAAA,yBAAA,EACN,CAAA;AAAA,wBACC,KAAK,KAAA,mBACJ,GAAA;AAAA,0BAAC,MAAA;AAAA,0BAAA;AAAA,4BACC,OAAO,IAAA,CAAK,KAAA;AAAA,4BACZ,SAAA,EAAU,sJAAA;AAAA,4BAET,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA,yBACR,GACE,IAAA;AAAA,wCACJ,GAAA;AAAA,0BAAC,WAAA;AAAA,0BAAA;AAAA,4BACC,SAAA,EAAW,EAAA;AAAA,8BACT,kEAAA;AAAA,8BACA,IAAA,IAAQ;AAAA,6BACV;AAAA,4BACA,aAAA,EAAW;AAAA;AAAA;AACb;AAAA;AAAA,mBACF;AAAA,kBACC,uBACC,IAAA,CAAC,KAAA,EAAA,EAAI,EAAA,EAAI,QAAA,EAAU,WAAU,oEAAA,EAC1B,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAK,8BACJ,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,qBAAA,EAAuB,QAAA,EAAA,IAAA,CAAK,aAAY,CAAA,GACnD,IAAA;AAAA,oBACH,KAAK,SAAA,IAAa,IAAA,CAAK,UAAU,MAAA,GAAS,CAAA,wBACxC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,sCAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,oEAAA,EACV,QAAA,EAAA,MAAA,EAAQ,aAAa,WAAA,EACxB,CAAA;AAAA,sCACA,GAAA,CAAC,QAAG,SAAA,EAAU,uBAAA,EACX,eAAK,SAAA,CAAU,GAAA,CAAI,CAAC,QAAA,qBACnB,IAAA;AAAA,wBAAC,IAAA;AAAA,wBAAA;AAAA,0BAEC,SAAA,EAAU,0GAAA;AAAA,0BAEV,QAAA,EAAA;AAAA,4CAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,6CAAA,EACb,QAAA,EAAA;AAAA,8CAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,2CAAA,EACb,QAAA,EAAA,QAAA,CAAS,IAAA,EACZ,CAAA;AAAA,8BACC,QAAA,CAAS,uBACR,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,6IAAA,EACb,QAAA,EAAA,QAAA,CAAS,MACZ,CAAA,GACE,IAAA;AAAA,8CACJ,GAAA;AAAA,gCAAC,MAAA;AAAA,gCAAA;AAAA,kCACC,SAAA,EAAW,EAAA;AAAA,oCACT,iDAAA;AAAA,oCACA,QAAA,CAAS,WACL,0BAAA,GACA;AAAA,mCACN;AAAA,kCAEC,mBAAS,QAAA,GACL,MAAA,EAAQ,QAAA,IAAY,UAAA,GACpB,QAAQ,QAAA,IAAY;AAAA;AAAA;AAC3B,6BAAA,EACF,CAAA;AAAA,4BACC,QAAA,CAAS,8BACR,GAAA,CAAC,GAAA,EAAA,EAAE,WAAU,+CAAA,EACV,QAAA,EAAA,QAAA,CAAS,aACZ,CAAA,GACE;AAAA;AAAA,yBAAA;AAAA,wBA7BC,QAAA,CAAS;AAAA,uBA+BjB,CAAA,EACH;AAAA,qBAAA,EACF,CAAA,GACE,IAAA;AAAA,oBACH,IAAA,CAAK,0BACJ,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,8JAAA,EACZ,QAAA,EAAA,IAAA,CAAK,SACR,CAAA,GACE,IAAA;AAAA,oBACH,IAAA,CAAK,QAAA,mBACJ,IAAA,CAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,sCAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,oEAAA,EACV,QAAA,EAAA,MAAA,EAAQ,YAAY,UAAA,EACvB,CAAA;AAAA,sCACA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,qBAAA,EAAuB,eAAK,QAAA,EAAS;AAAA,qBAAA,EACpD,CAAA,GACE;AAAA,mBAAA,EACN,CAAA,GACE;AAAA;AAAA,eAAA;AAAA,cAlGC,IAAA,CAAK;AAAA,aAmGZ;AAAA,UAEJ,CAAC,CAAA,EACH;AAAA;AAAA;AAAA,KAEJ;AAAA,EAEJ;AACF","file":"chunk-TC4NQL3H.js","sourcesContent":["import * as React from 'react'\nimport { ChevronDown, Search, Wrench } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { searchScore } from '../../lib/fuzzy'\n\nexport interface ChatToolCatalogArgument {\n name: string\n type?: string\n required?: boolean\n description?: string | null\n}\n\nexport interface ChatToolCatalogEntry {\n /** Stable tool identifier (rendered mono). */\n name: string\n /** Human title; falls back to `name`. */\n title?: string\n description?: string\n arguments?: ChatToolCatalogArgument[]\n example?: string | null\n response?: string | null\n scope?: string | null\n}\n\nexport interface ChatToolsCatalogLabels {\n /** Accessible name of the tool list. */\n tools: string\n search: string\n searchPlaceholder: string\n arguments: string\n response: string\n required: string\n optional: string\n empty: string\n noResults: string\n}\n\nexport interface ChatToolsCatalogProps {\n tools: ChatToolCatalogEntry[]\n searchable?: boolean\n /** Expand every entry's details initially (uncontrolled per entry after that). */\n defaultOpen?: boolean\n labels?: Partial<ChatToolsCatalogLabels>\n icon?: LucideIcon\n className?: string\n}\n\n/**\n * Catalog of the tools an assistant can use (study's `ToolsDialog` body,\n * generalized): searchable disclosure cards with description, argument\n * list, example and response shape. Presentational only — fetching and\n * the surrounding modal/popover stay app-side.\n */\nexport const ChatToolsCatalog = React.forwardRef<HTMLDivElement, ChatToolsCatalogProps>(\n function ChatToolsCatalog(\n {\n tools,\n searchable = true,\n defaultOpen = false,\n labels,\n icon: Icon = Wrench,\n className,\n },\n ref,\n ) {\n const uid = React.useId()\n const [query, setQuery] = React.useState('')\n const [openOverrides, setOpenOverrides] = React.useState<Record<string, boolean>>({})\n\n const filtered = React.useMemo(() => {\n const trimmed = query.trim()\n if (trimmed === '') {\n return tools\n }\n return tools\n .map((tool) => ({\n tool,\n score: Math.max(\n searchScore(trimmed, tool.title ?? tool.name) ?? -1,\n searchScore(trimmed, tool.name) ?? -1,\n searchScore(trimmed, tool.description ?? '') ?? -1,\n ),\n }))\n .filter((entry) => entry.score >= 0)\n .sort((a, b) => b.score - a.score)\n .map((entry) => entry.tool)\n }, [tools, query])\n\n return (\n <div\n ref={ref}\n data-as=\"chat-tools-catalog\"\n className={cn('flex min-h-0 w-full flex-col gap-2', className)}\n >\n {searchable ? (\n <div className=\"relative shrink-0\">\n <Search\n className=\"pointer-events-none absolute start-2 top-1/2 size-3.5 -translate-y-1/2 text-[var(--as-muted-fg)]\"\n aria-hidden\n />\n <input\n type=\"search\"\n role=\"searchbox\"\n value={query}\n aria-label={labels?.search ?? 'Search tools'}\n placeholder={labels?.searchPlaceholder ?? 'Search…'}\n onChange={(event) => setQuery(event.target.value)}\n className=\"w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] py-1.5 pe-2 ps-8 text-xs text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n />\n </div>\n ) : null}\n {tools.length === 0 ? (\n <p className=\"px-1 py-3 text-xs text-[var(--as-muted-fg)]\">\n {labels?.empty ?? 'No tools available'}\n </p>\n ) : filtered.length === 0 ? (\n <p className=\"px-1 py-3 text-xs text-[var(--as-muted-fg)]\">\n {labels?.noResults ?? 'No matching tools'}\n </p>\n ) : (\n <div role=\"list\" aria-label={labels?.tools ?? 'Tools'} className=\"flex flex-col gap-2\">\n {filtered.map((tool) => {\n const open = openOverrides[tool.name] ?? defaultOpen\n const regionId = `${uid}-${tool.name}`\n const toggle = () =>\n setOpenOverrides((current) => ({ ...current, [tool.name]: !open }))\n return (\n <div\n key={tool.name}\n role=\"listitem\"\n data-as=\"chat-tools-catalog-entry\"\n data-open={open || undefined}\n className=\"rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-xs\"\n >\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={regionId}\n onClick={toggle}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-left focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <Icon className=\"size-3.5 shrink-0 text-[var(--as-muted-fg)]\" aria-hidden />\n <span className=\"min-w-0 flex-1 break-words\">\n <span className=\"font-mono font-medium text-[var(--as-fg)]\">{tool.name}</span>\n {tool.title ? (\n <span className=\"ml-1.5 text-[var(--as-muted-fg)]\">{tool.title}</span>\n ) : null}\n </span>\n {tool.scope ? (\n <span\n title={tool.scope}\n className=\"max-w-[40%] shrink-0 truncate rounded-full bg-[var(--as-surface-raised)] px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-[var(--as-muted-fg)]\"\n >\n {tool.scope}\n </span>\n ) : null}\n <ChevronDown\n className={cn(\n 'size-3.5 shrink-0 text-[var(--as-muted-fg)] transition-transform',\n open && 'rotate-180',\n )}\n aria-hidden\n />\n </button>\n {open ? (\n <div id={regionId} className=\"flex flex-col gap-2 border-t border-[var(--as-border)] px-2.5 py-2\">\n {tool.description ? (\n <p className=\"text-[var(--as-fg)]\">{tool.description}</p>\n ) : null}\n {tool.arguments && tool.arguments.length > 0 ? (\n <div>\n <p className=\"mb-1 font-medium uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {labels?.arguments ?? 'Arguments'}\n </p>\n <ul className=\"flex flex-col gap-1.5\">\n {tool.arguments.map((argument) => (\n <li\n key={argument.name}\n className=\"rounded-[var(--as-radius-sm)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] px-2 py-1.5\"\n >\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <span className=\"font-mono font-medium text-[var(--as-fg)]\">\n {argument.name}\n </span>\n {argument.type ? (\n <span className=\"rounded-full border border-[var(--as-border)] bg-[var(--as-surface)] px-1.5 py-px font-mono text-[10px] leading-4 text-[var(--as-muted-fg)]\">\n {argument.type}\n </span>\n ) : null}\n <span\n className={cn(\n 'text-[10px] font-medium uppercase tracking-wide',\n argument.required\n ? 'text-[var(--as-warning)]'\n : 'text-[var(--as-muted-fg)]',\n )}\n >\n {argument.required\n ? (labels?.required ?? 'required')\n : (labels?.optional ?? 'optional')}\n </span>\n </div>\n {argument.description ? (\n <p className=\"mt-0.5 leading-snug text-[var(--as-muted-fg)]\">\n {argument.description}\n </p>\n ) : null}\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n {tool.example ? (\n <pre className=\"max-h-40 overflow-auto whitespace-pre-wrap break-words rounded-[var(--as-radius-sm)] bg-[var(--as-surface-raised)] p-2 font-mono text-[11px] leading-relaxed\">\n {tool.example}\n </pre>\n ) : null}\n {tool.response ? (\n <div>\n <p className=\"mb-1 font-medium uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {labels?.response ?? 'Response'}\n </p>\n <p className=\"text-[var(--as-fg)]\">{tool.response}</p>\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n },\n)\n"]}