@neuronection/assistant-ui 0.28.1 → 0.29.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,26 @@
1
1
  # @neuronection/assistant-ui
2
2
 
3
+ ## 0.29.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`e73579a`](https://github.com/neuronection/assistant-ui/commit/e73579ad0ab9795e7111a309d852b6042f2f5c2f) Thanks [@constLiakos](https://github.com/constLiakos)! - ChatTraceTimeline: full tool-call observability. `ChatTraceTimelineEntry`
8
+ gains optional `status` (`'ok' | 'error'` — renders a status dot after
9
+ the duration), `args` and `response` (pretty-printed strings) — `tool`
10
+ rows with `args`/`response` render a labelled, expandable detail region
11
+ (`Arguments` / `Response` blocks, `chat-trace-detail-toggle` button +
12
+ `chat-trace-detail` region, accessible name `"<tool> details"`). Labels
13
+ gain `arguments`, `response`, `details`. All fields optional — existing
14
+ usage renders unchanged.
15
+
16
+ ### Patch Changes
17
+
18
+ - [`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.
19
+
20
+ - [`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.
21
+
22
+ - [`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.
23
+
3
24
  ## 0.28.1
4
25
 
5
26
  ### 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 { ChatToolsCatalog } from './chunk-ZSCL34WT.js';
1
+ export { ChatToolsCatalog } from './chunk-N55WQMBA.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;
@@ -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"]}
@@ -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-N55WQMBA.js.map
156
+ //# sourceMappingURL=chunk-N55WQMBA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-tools-catalog/ChatToolsCatalog.tsx"],"names":["ChatToolsCatalog"],"mappings":"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","file":"chunk-N55WQMBA.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 <div role=\"list\" aria-label={labels?.tools ?? 'Tools'} className=\"flex flex-col gap-2\">\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 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 )}\n </div>\n </div>\n )\n },\n)\n"]}
@@ -0,0 +1,133 @@
1
+ import { cn } from './chunk-BLJWR4ZV.js';
2
+ import * as React from 'react';
3
+ import { Loader2, FolderUp, Upload } from 'lucide-react';
4
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
+
6
+ var UploadDropzone = React.forwardRef(
7
+ function UploadDropzone2({
8
+ onFiles,
9
+ uploading = false,
10
+ variant = "block",
11
+ label = "Drop files here or click to browse",
12
+ hint,
13
+ browseLabel = "Choose files",
14
+ folderLabel = "Choose folder",
15
+ accept,
16
+ multiple = true,
17
+ allowFolders = false,
18
+ disabled = false,
19
+ className
20
+ }, ref) {
21
+ const [dragging, setDragging] = React.useState(false);
22
+ const fileInput = React.useRef(null);
23
+ const folderInput = React.useRef(null);
24
+ const active = !disabled && !uploading;
25
+ const emit = (list) => {
26
+ if (!active || !list || list.length === 0) return;
27
+ onFiles(Array.from(list));
28
+ };
29
+ const input = allowFolders ? /* @__PURE__ */ jsxs(Fragment, { children: [
30
+ /* @__PURE__ */ jsx(
31
+ "input",
32
+ {
33
+ ref: fileInput,
34
+ type: "file",
35
+ accept,
36
+ multiple,
37
+ className: "hidden",
38
+ "aria-hidden": true,
39
+ tabIndex: -1,
40
+ onChange: (event) => {
41
+ emit(event.target.files);
42
+ event.target.value = "";
43
+ }
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsx(
47
+ "input",
48
+ {
49
+ ref: folderInput,
50
+ type: "file",
51
+ multiple: true,
52
+ className: "hidden",
53
+ webkitdirectory: "",
54
+ "aria-hidden": true,
55
+ tabIndex: -1,
56
+ onChange: (event) => {
57
+ emit(event.target.files);
58
+ event.target.value = "";
59
+ }
60
+ }
61
+ )
62
+ ] }) : /* @__PURE__ */ jsx(
63
+ "input",
64
+ {
65
+ ref: fileInput,
66
+ type: "file",
67
+ accept,
68
+ multiple,
69
+ className: "hidden",
70
+ "aria-hidden": true,
71
+ tabIndex: -1,
72
+ onChange: (event) => {
73
+ emit(event.target.files);
74
+ event.target.value = "";
75
+ }
76
+ }
77
+ );
78
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
79
+ input,
80
+ /* @__PURE__ */ jsxs(
81
+ "div",
82
+ {
83
+ ref,
84
+ "data-as": "upload-dropzone",
85
+ "data-variant": variant,
86
+ "data-dragging": dragging || void 0,
87
+ role: "button",
88
+ tabIndex: active ? 0 : -1,
89
+ "aria-label": label,
90
+ "aria-disabled": disabled || uploading || void 0,
91
+ onClick: () => active && fileInput.current?.click(),
92
+ onKeyDown: (event) => {
93
+ if (active && (event.key === "Enter" || event.key === " ")) {
94
+ event.preventDefault();
95
+ fileInput.current?.click();
96
+ }
97
+ },
98
+ onDragOver: (event) => {
99
+ event.preventDefault();
100
+ if (active) setDragging(true);
101
+ },
102
+ onDragLeave: () => setDragging(false),
103
+ onDrop: (event) => {
104
+ event.preventDefault();
105
+ setDragging(false);
106
+ emit(event.dataTransfer.files);
107
+ },
108
+ className: cn(
109
+ "flex cursor-pointer items-center justify-center gap-3 border border-dashed border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-muted-fg)] transition-colors",
110
+ "hover:border-[var(--as-primary)] hover:text-[var(--as-fg)]",
111
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
112
+ variant === "block" ? "rounded-[var(--as-radius-lg)] p-8" : "rounded-[var(--as-radius)] px-4 py-3",
113
+ dragging && "border-[var(--as-primary)] bg-[color-mix(in_srgb,var(--as-primary)_8%,transparent)] text-[var(--as-fg)]",
114
+ uploading && "cursor-progress opacity-70",
115
+ disabled && "pointer-events-none opacity-50",
116
+ className
117
+ ),
118
+ children: [
119
+ uploading ? /* @__PURE__ */ jsx(Loader2, { className: "size-5 shrink-0 animate-spin", "aria-hidden": true }) : allowFolders ? /* @__PURE__ */ jsx(FolderUp, { className: "size-5 shrink-0", "aria-hidden": true }) : /* @__PURE__ */ jsx(Upload, { className: "size-5 shrink-0", "aria-hidden": true }),
120
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
121
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", role: uploading ? "status" : void 0, children: uploading ? `${browseLabel}\u2026` : dragging ? folderLabel : label }),
122
+ hint ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs", children: hint }) : null
123
+ ] })
124
+ ]
125
+ }
126
+ )
127
+ ] });
128
+ }
129
+ );
130
+
131
+ export { UploadDropzone };
132
+ //# sourceMappingURL=chunk-U5WSPZLX.js.map
133
+ //# sourceMappingURL=chunk-U5WSPZLX.js.map