@tangle-network/sandbox-ui 0.14.0 → 0.15.2

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 (106) hide show
  1. package/dist/auth.d.ts +1 -74
  2. package/dist/auth.js +1 -4
  3. package/dist/chat.d.ts +1 -136
  4. package/dist/chat.js +2 -15
  5. package/dist/chunk-2BUPSB7O.js +0 -0
  6. package/dist/chunk-3J6FG3FJ.js +18 -0
  7. package/dist/chunk-76IQLPW2.js +206 -0
  8. package/dist/chunk-7ZA5SEK3.js +239 -0
  9. package/dist/chunk-AZ3AWMTM.js +8 -0
  10. package/dist/chunk-CMY7W45U.js +380 -0
  11. package/dist/chunk-EI44GEQ5.js +6 -0
  12. package/dist/chunk-ENMWGVDL.js +858 -0
  13. package/dist/{chunk-5OQ27N57.js → chunk-GPT7VKK6.js} +34 -38
  14. package/dist/chunk-HLZTKSGT.js +2652 -0
  15. package/dist/chunk-JBGKGLD7.js +16 -0
  16. package/dist/chunk-NJNME4J4.js +14 -0
  17. package/dist/chunk-QPAJR74X.js +20 -0
  18. package/dist/chunk-TK46XFLM.js +28 -0
  19. package/dist/chunk-WID73FPH.js +89 -0
  20. package/dist/chunk-YVXK4XRO.js +30 -0
  21. package/dist/dashboard.d.ts +450 -4
  22. package/dist/dashboard.js +20 -891
  23. package/dist/editor.d.ts +1 -120
  24. package/dist/editor.js +1 -5
  25. package/dist/files.d.ts +1 -129
  26. package/dist/files.js +2 -7
  27. package/dist/globals.css +2 -1265
  28. package/dist/hooks.d.ts +114 -11
  29. package/dist/hooks.js +17 -88
  30. package/dist/index.d.ts +24 -99
  31. package/dist/index.js +251 -256
  32. package/dist/markdown.d.ts +1 -29
  33. package/dist/markdown.js +2 -2
  34. package/dist/openui.d.ts +8 -115
  35. package/dist/openui.js +1 -6
  36. package/dist/pages.d.ts +13 -12
  37. package/dist/pages.js +91 -115
  38. package/dist/primitives.d.ts +14 -49
  39. package/dist/primitives.js +69 -77
  40. package/dist/run.d.ts +1 -14
  41. package/dist/run.js +2 -22
  42. package/dist/sdk-hooks.d.ts +3 -283
  43. package/dist/sdk-hooks.js +10 -14
  44. package/dist/stores.d.ts +2 -14
  45. package/dist/stores.js +11 -39
  46. package/dist/styles.css +2 -1265
  47. package/dist/template-card-DStb8boW.d.ts +183 -0
  48. package/dist/types.d.ts +11 -8
  49. package/dist/types.js +1 -0
  50. package/dist/utils.d.ts +1 -44
  51. package/dist/utils.js +6 -12
  52. package/dist/workspace.d.ts +5 -10
  53. package/dist/workspace.js +3 -19
  54. package/package.json +19 -54
  55. package/dist/active-sessions-store-CeOmXgv5.d.ts +0 -85
  56. package/dist/artifact-pane-Bh45Ssco.d.ts +0 -24
  57. package/dist/branding-DCi5VEik.d.ts +0 -13
  58. package/dist/button-CMQuQEW_.d.ts +0 -17
  59. package/dist/chat-container-f4yEs6KN.d.ts +0 -106
  60. package/dist/chunk-34A66VBG.js +0 -214
  61. package/dist/chunk-34I7UFSX.js +0 -92
  62. package/dist/chunk-36QY2W5G.js +0 -802
  63. package/dist/chunk-4CLN43XT.js +0 -45
  64. package/dist/chunk-54SQQMMM.js +0 -156
  65. package/dist/chunk-66EZOYZR.js +0 -102
  66. package/dist/chunk-BX6AQMUS.js +0 -183
  67. package/dist/chunk-DI3NZ5ZX.js +0 -192
  68. package/dist/chunk-DPGIXDAI.js +0 -220
  69. package/dist/chunk-DXMIEK4K.js +0 -1426
  70. package/dist/chunk-GSZA3TSY.js +0 -79
  71. package/dist/chunk-HB5Y37YU.js +0 -54
  72. package/dist/chunk-LQNEZDRM.js +0 -109
  73. package/dist/chunk-MA7YKRUP.js +0 -131
  74. package/dist/chunk-MKTSMWVD.js +0 -109
  75. package/dist/chunk-MQXABZTB.js +0 -1348
  76. package/dist/chunk-MT5FJ3ZT.js +0 -186
  77. package/dist/chunk-NKUPJC34.js +0 -2070
  78. package/dist/chunk-OEX7NZE3.js +0 -321
  79. package/dist/chunk-OKLQVY3Y.js +0 -139
  80. package/dist/chunk-Q56BYXQF.js +0 -61
  81. package/dist/chunk-QD4QE5P5.js +0 -40
  82. package/dist/chunk-QDH5GEGY.js +0 -630
  83. package/dist/chunk-QID2OOMG.js +0 -133
  84. package/dist/chunk-QMU2PWOU.js +0 -493
  85. package/dist/chunk-RQHJBTEU.js +0 -10
  86. package/dist/chunk-T7HMZEVO.js +0 -216
  87. package/dist/chunk-U6QTHMY6.js +0 -1290
  88. package/dist/chunk-US6JKJKH.js +0 -124
  89. package/dist/chunk-VX3XOUEB.js +0 -63
  90. package/dist/chunk-XLG757B6.js +0 -933
  91. package/dist/chunk-ZMNSRDMH.js +0 -127
  92. package/dist/chunk-ZNCEM5CD.js +0 -316
  93. package/dist/document-editor-pane-A70-EhdQ.d.ts +0 -124
  94. package/dist/document-editor-pane-TLPVRBBU.js +0 -11
  95. package/dist/expanded-tool-detail-Dh99mcbY.d.ts +0 -63
  96. package/dist/file-tabs-BLfxfmAH.d.ts +0 -51
  97. package/dist/parts-CyGkM6Fp.d.ts +0 -50
  98. package/dist/run-CtFZ6s-D.d.ts +0 -41
  99. package/dist/sidebar-drop-zone-tDBsuOH5.d.ts +0 -301
  100. package/dist/sidecar-CFU2W9j1.d.ts +0 -8
  101. package/dist/template-card-BAtvcAkU.d.ts +0 -18
  102. package/dist/tool-call-feed-Bs3MyQMT.d.ts +0 -68
  103. package/dist/tool-display-Ct9nFAzJ.d.ts +0 -32
  104. package/dist/usage-chart-CPTcNlGs.d.ts +0 -73
  105. package/dist/use-sandbox-metrics-DWc0k9Xm.d.ts +0 -153
  106. package/dist/variant-list-BrHYcBCk.d.ts +0 -540
@@ -1,933 +0,0 @@
1
- import {
2
- useAutoScroll,
3
- useRunCollapseState,
4
- useRunGroups
5
- } from "./chunk-54SQQMMM.js";
6
- import {
7
- InlineThinkingItem,
8
- RunGroup
9
- } from "./chunk-MQXABZTB.js";
10
- import {
11
- ToolCallGroup,
12
- ToolCallStep
13
- } from "./chunk-MT5FJ3ZT.js";
14
- import {
15
- getToolDisplayMetadata
16
- } from "./chunk-BX6AQMUS.js";
17
- import {
18
- OpenUIArtifactRenderer
19
- } from "./chunk-ZNCEM5CD.js";
20
- import {
21
- Markdown
22
- } from "./chunk-T7HMZEVO.js";
23
- import {
24
- cn
25
- } from "./chunk-RQHJBTEU.js";
26
-
27
- // src/chat/user-message.tsx
28
- import { memo } from "react";
29
- import { jsx, jsxs } from "react/jsx-runtime";
30
- var UserMessage = memo(({ message, parts, actions }) => {
31
- const textContent = parts.filter((p) => p.type === "text").map((p) => p.text).join("\n");
32
- if (!textContent.trim()) return null;
33
- return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs("div", { className: "flex max-w-[78%] flex-col items-end gap-2", children: [
34
- /* @__PURE__ */ jsxs("div", { className: "w-full rounded-[26px] rounded-br-[12px] bg-[var(--brand-primary)] px-4 py-3 text-white shadow-[0_8px_20px_rgba(15,23,42,0.12)]", children: [
35
- /* @__PURE__ */ jsx("div", { className: "mb-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-white/60", children: "You" }),
36
- /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap text-[15px] leading-6.5 text-white", children: textContent })
37
- ] }),
38
- actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center justify-end gap-1.5 text-xs text-muted-foreground", children: actions }) : null
39
- ] }) });
40
- });
41
- UserMessage.displayName = "UserMessage";
42
-
43
- // src/chat/message-list.tsx
44
- import { memo as memo2 } from "react";
45
- import { jsx as jsx2 } from "react/jsx-runtime";
46
- var MessageList = memo2(
47
- ({
48
- groups,
49
- partMap,
50
- isCollapsed,
51
- onToggleCollapse,
52
- branding,
53
- renderToolDetail,
54
- renderRunActions,
55
- renderUserMessageActions,
56
- renderToolActions
57
- }) => {
58
- return /* @__PURE__ */ jsx2("div", { className: "space-y-4", children: groups.map((group) => {
59
- if (group.type === "user") {
60
- const messageParts = partMap[group.message.id] ?? [];
61
- return /* @__PURE__ */ jsx2(
62
- UserMessage,
63
- {
64
- message: group.message,
65
- parts: messageParts,
66
- actions: renderUserMessageActions?.(group.message, messageParts)
67
- },
68
- group.message.id
69
- );
70
- }
71
- return /* @__PURE__ */ jsx2(
72
- RunGroup,
73
- {
74
- run: group.run,
75
- partMap,
76
- collapsed: isCollapsed(group.run.id),
77
- onToggle: () => onToggleCollapse(group.run.id),
78
- branding,
79
- renderToolDetail,
80
- headerActions: renderRunActions?.(group.run),
81
- renderToolActions
82
- },
83
- group.run.id
84
- );
85
- }) });
86
- }
87
- );
88
- MessageList.displayName = "MessageList";
89
-
90
- // src/chat/thinking-indicator.tsx
91
- import { useEffect, useState } from "react";
92
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
93
- function ThinkingIndicator({ className }) {
94
- const [elapsed, setElapsed] = useState(0);
95
- useEffect(() => {
96
- const interval = window.setInterval(() => setElapsed((current) => current + 1), 1e3);
97
- return () => window.clearInterval(interval);
98
- }, []);
99
- return /* @__PURE__ */ jsxs2("div", { className: cn("flex items-center gap-2 px-3 py-1.5", className), children: [
100
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-[3px]", children: [
101
- /* @__PURE__ */ jsx3("span", { className: "h-[var(--indicator-dot-size)] w-[var(--indicator-dot-size)] animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "0ms" } }),
102
- /* @__PURE__ */ jsx3("span", { className: "h-[var(--indicator-dot-size)] w-[var(--indicator-dot-size)] animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "150ms" } }),
103
- /* @__PURE__ */ jsx3("span", { className: "h-[var(--indicator-dot-size)] w-[var(--indicator-dot-size)] animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "300ms" } })
104
- ] }),
105
- elapsed > 3 && /* @__PURE__ */ jsxs2("span", { className: "text-[var(--font-size-xs)] tabular-nums text-[var(--text-dim)]", children: [
106
- elapsed,
107
- "s"
108
- ] })
109
- ] });
110
- }
111
-
112
- // src/chat/agent-timeline.tsx
113
- import {
114
- AlertTriangle,
115
- CheckCircle2,
116
- CircleDot,
117
- FileText,
118
- Info
119
- } from "lucide-react";
120
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
121
- var TONE_STYLES = {
122
- default: {
123
- dot: "bg-[var(--border-hover)]",
124
- card: "border-border bg-card",
125
- text: "text-foreground",
126
- icon: CircleDot
127
- },
128
- info: {
129
- dot: "bg-[var(--surface-info-text)]",
130
- card: "border-[var(--surface-info-border)] bg-[var(--surface-info-bg)]",
131
- text: "text-[var(--surface-info-text)]",
132
- icon: Info
133
- },
134
- success: {
135
- dot: "bg-[var(--surface-success-text)]",
136
- card: "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)]",
137
- text: "text-[var(--surface-success-text)]",
138
- icon: CheckCircle2
139
- },
140
- warning: {
141
- dot: "bg-[var(--surface-warning-text)]",
142
- card: "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)]",
143
- text: "text-[var(--surface-warning-text)]",
144
- icon: AlertTriangle
145
- },
146
- error: {
147
- dot: "bg-[var(--surface-danger-text)]",
148
- card: "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)]",
149
- text: "text-[var(--surface-danger-text)]",
150
- icon: AlertTriangle
151
- }
152
- };
153
- function formatTime(date) {
154
- return date.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
155
- }
156
- function AgentTimelineRow({ isLast, accentClassName, children }) {
157
- return /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-[1.25rem_minmax(0,1fr)] gap-x-4", children: [
158
- /* @__PURE__ */ jsxs3("div", { className: "relative flex justify-center", children: [
159
- !isLast && /* @__PURE__ */ jsx4("span", { className: "absolute top-4 bottom-[-0.75rem] left-1/2 w-px -translate-x-1/2 bg-border" }),
160
- /* @__PURE__ */ jsx4("span", { className: cn("relative mt-2 h-[var(--timeline-dot-size)] w-[var(--timeline-dot-size)] rounded-full ring-4 ring-[var(--bg-root)]", accentClassName) })
161
- ] }),
162
- /* @__PURE__ */ jsx4("div", { className: "min-w-0 pb-3", children })
163
- ] });
164
- }
165
- function UserMessage2({ item }) {
166
- return /* @__PURE__ */ jsx4("div", { className: "mb-3 flex justify-end", children: /* @__PURE__ */ jsx4("div", { className: "max-w-[72%]", children: /* @__PURE__ */ jsxs3("div", { className: "rounded-2xl border border-border bg-muted/50 px-4 py-3", children: [
167
- item.timestamp && /* @__PURE__ */ jsx4("div", { className: "mb-1.5 text-right text-[var(--font-size-xs)] text-muted-foreground", children: formatTime(item.timestamp) }),
168
- /* @__PURE__ */ jsx4("div", { className: "whitespace-pre-wrap text-[var(--font-size-base)] leading-[var(--line-height-base)] text-foreground", children: item.content })
169
- ] }) }) });
170
- }
171
- function AssistantMessage({ item }) {
172
- return /* @__PURE__ */ jsxs3("div", { className: "-mt-0.5", children: [
173
- item.timestamp && /* @__PURE__ */ jsx4("div", { className: "mb-2 text-[var(--font-size-xs)] text-muted-foreground", children: formatTime(item.timestamp) }),
174
- item.content && /* @__PURE__ */ jsx4(Markdown, { className: "tangle-prose text-[var(--font-size-base)] leading-[var(--line-height-base)]", children: item.content }),
175
- item.isStreaming && /* @__PURE__ */ jsx4("span", { className: "ml-0.5 inline-block h-4 w-2 animate-pulse rounded-sm bg-primary align-text-bottom" }),
176
- item.toolCalls && /* @__PURE__ */ jsx4("div", { className: "mt-3", children: item.toolCalls }),
177
- item.after && /* @__PURE__ */ jsx4("div", { className: "mt-3 border-t border-border pt-3", children: item.after })
178
- ] });
179
- }
180
- function StatusCard({ item }) {
181
- const tone = TONE_STYLES[item.tone ?? "default"];
182
- const Icon = tone.icon;
183
- return /* @__PURE__ */ jsx4("div", { className: cn("rounded-[var(--radius-lg)] border px-4 py-3", tone.card), children: /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-3", children: [
184
- /* @__PURE__ */ jsx4(Icon, { className: cn("mt-0.5 h-4 w-4 shrink-0", tone.text) }),
185
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
186
- /* @__PURE__ */ jsx4("div", { className: cn("text-sm font-medium", tone.text), children: item.label }),
187
- item.detail && /* @__PURE__ */ jsx4("div", { className: "mt-0.5 text-sm text-muted-foreground", children: item.detail })
188
- ] })
189
- ] }) });
190
- }
191
- function ArtifactCard({ item }) {
192
- const tone = TONE_STYLES[item.tone ?? "default"];
193
- const content = /* @__PURE__ */ jsx4("div", { className: cn("rounded-[var(--radius-lg)] border px-4 py-3", tone.card), children: /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-3", children: [
194
- /* @__PURE__ */ jsx4("div", { className: "mt-0.5 flex h-[var(--avatar-size)] w-[var(--avatar-size)] shrink-0 items-center justify-center rounded-[var(--radius-md)] bg-muted/50 text-foreground", children: item.icon ?? /* @__PURE__ */ jsx4(FileText, { className: "h-4 w-4" }) }),
195
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
196
- /* @__PURE__ */ jsx4("div", { className: "text-sm font-medium text-foreground", children: item.title }),
197
- item.description && /* @__PURE__ */ jsx4("div", { className: "mt-1 text-sm text-muted-foreground", children: item.description }),
198
- item.meta && /* @__PURE__ */ jsx4("div", { className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-muted-foreground", children: item.meta })
199
- ] }),
200
- item.action && /* @__PURE__ */ jsx4("div", { className: "shrink-0", children: item.action })
201
- ] }) });
202
- if (!item.onClick) return content;
203
- return /* @__PURE__ */ jsx4(
204
- "div",
205
- {
206
- role: "button",
207
- tabIndex: 0,
208
- onClick: item.onClick,
209
- onKeyDown: (event) => {
210
- if (event.key === "Enter" || event.key === " ") {
211
- event.preventDefault();
212
- item.onClick?.();
213
- }
214
- },
215
- className: "block w-full text-left transition-transform hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
216
- children: content
217
- }
218
- );
219
- }
220
- function AgentTimeline({
221
- items,
222
- isThinking,
223
- emptyState,
224
- className
225
- }) {
226
- if (items.length === 0 && !isThinking) {
227
- return emptyState ? /* @__PURE__ */ jsx4("div", { className: cn("flex h-full items-center justify-center p-4", className), children: emptyState }) : null;
228
- }
229
- const renderedItems = isThinking ? [...items, { id: "__thinking__", kind: "custom", content: /* @__PURE__ */ jsx4(ThinkingIndicator, {}) }] : items;
230
- const timelineItems = renderedItems.filter((item) => !(item.kind === "message" && item.role === "user"));
231
- return /* @__PURE__ */ jsx4("div", { className: cn("mx-auto w-full max-w-5xl px-4 py-4", className), children: renderedItems.map((item, index) => {
232
- if (item.kind === "message" && item.role === "user") {
233
- return /* @__PURE__ */ jsx4(UserMessage2, { item }, item.id);
234
- }
235
- const timelineIndex = timelineItems.indexOf(item);
236
- const isLast = timelineIndex === timelineItems.length - 1;
237
- if (item.kind === "message") {
238
- return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--brand-glow)]", children: /* @__PURE__ */ jsx4(AssistantMessage, { item }) }, item.id);
239
- }
240
- if (item.kind === "tool") {
241
- return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: /* @__PURE__ */ jsx4(
242
- ToolCallStep,
243
- {
244
- type: item.call.type,
245
- label: item.call.label,
246
- status: item.call.status,
247
- detail: item.call.detail,
248
- output: item.call.output,
249
- duration: item.call.duration
250
- }
251
- ) }, item.id);
252
- }
253
- if (item.kind === "tool_group") {
254
- return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: /* @__PURE__ */ jsx4(ToolCallGroup, { title: item.title, children: item.calls.map((call) => /* @__PURE__ */ jsx4(
255
- ToolCallStep,
256
- {
257
- type: call.type,
258
- label: call.label,
259
- status: call.status,
260
- detail: call.detail,
261
- output: call.output,
262
- duration: call.duration
263
- },
264
- call.id
265
- )) }) }, item.id);
266
- }
267
- if (item.kind === "status") {
268
- return /* @__PURE__ */ jsx4(
269
- AgentTimelineRow,
270
- {
271
- isLast,
272
- accentClassName: TONE_STYLES[item.tone ?? "default"].dot,
273
- children: /* @__PURE__ */ jsx4(StatusCard, { item })
274
- },
275
- item.id
276
- );
277
- }
278
- if (item.kind === "artifact") {
279
- return /* @__PURE__ */ jsx4(
280
- AgentTimelineRow,
281
- {
282
- isLast,
283
- accentClassName: TONE_STYLES[item.tone ?? "default"].dot,
284
- children: /* @__PURE__ */ jsx4(ArtifactCard, { item })
285
- },
286
- item.id
287
- );
288
- }
289
- return /* @__PURE__ */ jsx4(AgentTimelineRow, { isLast, accentClassName: "bg-[var(--border-hover)]", children: item.content }, item.id);
290
- }) });
291
- }
292
-
293
- // src/chat/chat-input.tsx
294
- import { useState as useState2, useRef, useCallback } from "react";
295
- import { Send, Square, Paperclip, FolderUp, X, Upload } from "lucide-react";
296
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
297
- function ChatInput({
298
- onSend,
299
- onCancel,
300
- isStreaming,
301
- disabled,
302
- placeholder = "Ask the agent to inspect files, run commands, or explain results\u2026",
303
- modelLabel,
304
- onModelClick,
305
- pendingFiles = [],
306
- onRemoveFile,
307
- onAttach,
308
- onAttachFolder,
309
- accept,
310
- dropTitle = "Drop files to add context",
311
- dropDescription = "Files will be attached to your next message.",
312
- className,
313
- inputLabel = "Agent Command Deck",
314
- idleStatus = "Ready for next instruction",
315
- streamingStatus = "Streaming response",
316
- hideShortcutHint
317
- }) {
318
- const [value, setValue] = useState2("");
319
- const [dragOver, setDragOver] = useState2(false);
320
- const dragCounter = useRef(0);
321
- const textareaRef = useRef(null);
322
- const fileInputRef = useRef(null);
323
- const folderInputRef = useRef(null);
324
- const handleSend = useCallback(() => {
325
- const trimmed = value.trim();
326
- if (!trimmed || isStreaming || disabled) return;
327
- onSend(trimmed);
328
- setValue("");
329
- if (textareaRef.current) {
330
- textareaRef.current.style.height = "auto";
331
- }
332
- }, [value, isStreaming, disabled, onSend]);
333
- const handleKeyDown = (e) => {
334
- if (e.key === "Enter" && !e.shiftKey) {
335
- e.preventDefault();
336
- handleSend();
337
- }
338
- };
339
- const handleChange = (e) => {
340
- setValue(e.target.value);
341
- const el = e.target;
342
- el.style.height = "auto";
343
- el.style.height = `${Math.min(el.scrollHeight, 160)}px`;
344
- };
345
- const handleAttachClick = () => {
346
- fileInputRef.current?.click();
347
- };
348
- const handleFolderClick = () => {
349
- folderInputRef.current?.click();
350
- };
351
- const handleFileChange = (e) => {
352
- if (e.target.files?.length) {
353
- onAttach?.(e.target.files);
354
- e.target.value = "";
355
- }
356
- };
357
- const handleFolderChange = (e) => {
358
- if (e.target.files?.length) {
359
- (onAttachFolder ?? onAttach)?.(e.target.files);
360
- e.target.value = "";
361
- }
362
- };
363
- const handleDragEnter = useCallback((e) => {
364
- e.preventDefault();
365
- e.stopPropagation();
366
- dragCounter.current++;
367
- if (e.dataTransfer?.types.includes("Files")) {
368
- setDragOver(true);
369
- }
370
- }, []);
371
- const handleDragLeave = useCallback((e) => {
372
- e.preventDefault();
373
- e.stopPropagation();
374
- dragCounter.current--;
375
- if (dragCounter.current === 0) {
376
- setDragOver(false);
377
- }
378
- }, []);
379
- const handleDragOver = useCallback((e) => {
380
- e.preventDefault();
381
- e.stopPropagation();
382
- e.dataTransfer.dropEffect = "copy";
383
- }, []);
384
- const handleDrop = useCallback((e) => {
385
- e.preventDefault();
386
- e.stopPropagation();
387
- dragCounter.current = 0;
388
- setDragOver(false);
389
- const files = e.dataTransfer?.files;
390
- if (files?.length && onAttach) {
391
- onAttach(files);
392
- }
393
- }, [onAttach]);
394
- const fileChips = pendingFiles.filter((f) => f.type === "file" || !f.type);
395
- const folderChips = pendingFiles.filter((f) => f.type === "folder");
396
- return /* @__PURE__ */ jsxs4(
397
- "div",
398
- {
399
- className: cn("relative", className),
400
- onDragEnter: onAttach ? handleDragEnter : void 0,
401
- onDragLeave: onAttach ? handleDragLeave : void 0,
402
- onDragOver: onAttach ? handleDragOver : void 0,
403
- onDrop: onAttach ? handleDrop : void 0,
404
- children: [
405
- dragOver && /* @__PURE__ */ jsx5("div", { className: "absolute inset-0 z-10 flex items-center justify-center rounded-[var(--radius-xl)] border-2 border-dashed border-border bg-card pointer-events-none", children: /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
406
- /* @__PURE__ */ jsx5("div", { className: "mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-xl bg-[var(--accent-surface-soft)]", children: /* @__PURE__ */ jsx5(Upload, { className: "h-6 w-6 text-primary" }) }),
407
- /* @__PURE__ */ jsx5("p", { className: "text-sm font-semibold text-foreground", children: dropTitle }),
408
- /* @__PURE__ */ jsx5("p", { className: "mt-1 text-xs text-muted-foreground", children: dropDescription })
409
- ] }) }),
410
- pendingFiles.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "mb-3 flex flex-wrap gap-2", children: [
411
- folderChips.map((f) => /* @__PURE__ */ jsxs4(
412
- "span",
413
- {
414
- className: cn(
415
- "inline-flex items-center gap-1.5 rounded-[var(--radius-full)] border px-3 py-1.5 text-xs",
416
- "border-border bg-muted/50",
417
- f.status === "error" && "border-[var(--code-error)]/30 text-[var(--code-error)]",
418
- f.status !== "error" && "text-foreground"
419
- ),
420
- children: [
421
- /* @__PURE__ */ jsx5(FolderUp, { className: "h-3 w-3 shrink-0" }),
422
- /* @__PURE__ */ jsx5("span", { className: "truncate max-w-[150px]", children: f.name }),
423
- f.fileCount !== void 0 && /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground", children: [
424
- "(",
425
- f.fileCount,
426
- ")"
427
- ] }),
428
- f.status === "uploading" && /* @__PURE__ */ jsx5("span", { className: "w-3 h-3 border-2 border-primary border-t-transparent rounded-full animate-spin" }),
429
- onRemoveFile && /* @__PURE__ */ jsx5(
430
- "button",
431
- {
432
- type: "button",
433
- "aria-label": `Remove ${f.name}`,
434
- onClick: () => onRemoveFile(f.id),
435
- className: "rounded p-0.5 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
436
- children: /* @__PURE__ */ jsx5(X, { className: "h-3 w-3" })
437
- }
438
- )
439
- ]
440
- },
441
- f.id
442
- )),
443
- fileChips.map((f) => /* @__PURE__ */ jsxs4(
444
- "span",
445
- {
446
- className: cn(
447
- "inline-flex items-center gap-1.5 rounded-[var(--radius-full)] border px-3 py-1.5 text-xs",
448
- "border-border bg-muted/50",
449
- f.status === "error" && "border-[var(--code-error)]/30 text-[var(--code-error)]",
450
- f.status !== "error" && "text-foreground"
451
- ),
452
- children: [
453
- /* @__PURE__ */ jsx5(Paperclip, { className: "h-3 w-3 shrink-0" }),
454
- /* @__PURE__ */ jsx5("span", { className: "truncate max-w-[150px]", children: f.name }),
455
- f.status === "uploading" && /* @__PURE__ */ jsx5("span", { className: "w-3 h-3 border-2 border-primary border-t-transparent rounded-full animate-spin" }),
456
- onRemoveFile && /* @__PURE__ */ jsx5(
457
- "button",
458
- {
459
- type: "button",
460
- "aria-label": `Remove ${f.name}`,
461
- onClick: () => onRemoveFile(f.id),
462
- className: "rounded p-0.5 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
463
- children: /* @__PURE__ */ jsx5(X, { className: "h-3 w-3" })
464
- }
465
- )
466
- ]
467
- },
468
- f.id
469
- ))
470
- ] }),
471
- /* @__PURE__ */ jsx5("div", { className: "rounded-[24px] border border-[var(--chat-input-border,var(--border-default))] [background:var(--chat-input-bg,var(--bg-card))] shadow-[var(--chat-input-shadow,0_1px_2px_rgba(15,23,42,0.05))] transition-all focus-within:border-[var(--chat-input-focus-border,var(--border-accent))] focus-within:shadow-[var(--chat-input-focus-shadow,0_10px_30px_rgba(15,23,42,0.08))]", children: /* @__PURE__ */ jsxs4("div", { className: "rounded-[24px] px-4 py-[var(--chat-input-py)]", children: [
472
- (inputLabel !== null || idleStatus !== null || streamingStatus !== null) && /* @__PURE__ */ jsxs4("div", { className: "mb-1.5 flex items-center justify-between gap-3 px-1", children: [
473
- inputLabel !== null && /* @__PURE__ */ jsx5("div", { className: "text-[var(--chat-label-size,11px)] font-[var(--chat-label-weight,600)] uppercase tracking-[var(--chat-label-tracking,0.16em)] text-[var(--text-muted)]", children: inputLabel }),
474
- (idleStatus !== null || streamingStatus !== null) && /* @__PURE__ */ jsx5("div", { className: "text-[var(--chat-label-size,11px)] text-[var(--text-muted)]", children: isStreaming ? streamingStatus ?? "" : idleStatus ?? "" })
475
- ] }),
476
- /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2.5", children: [
477
- onAttach && /* @__PURE__ */ jsxs4(Fragment, { children: [
478
- /* @__PURE__ */ jsx5(
479
- "button",
480
- {
481
- type: "button",
482
- onClick: handleAttachClick,
483
- disabled: isStreaming,
484
- "aria-label": "Attach files",
485
- title: "Attach files",
486
- className: "mb-0.5 shrink-0 rounded-[var(--radius-md)] border border-transparent p-2 text-muted-foreground transition-colors hover:border-border hover:bg-accent hover:text-foreground disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
487
- children: /* @__PURE__ */ jsx5(Paperclip, { className: "h-4 w-4" })
488
- }
489
- ),
490
- /* @__PURE__ */ jsx5(
491
- "input",
492
- {
493
- ref: fileInputRef,
494
- type: "file",
495
- multiple: true,
496
- className: "hidden",
497
- onChange: handleFileChange,
498
- accept: accept ?? ".pdf,.csv,.xlsx,.xls,.jpg,.jpeg,.png,.gif,.txt,.json,.yaml,.yml"
499
- }
500
- )
501
- ] }),
502
- (onAttachFolder ?? onAttach) && /* @__PURE__ */ jsxs4(Fragment, { children: [
503
- /* @__PURE__ */ jsx5(
504
- "button",
505
- {
506
- type: "button",
507
- onClick: handleFolderClick,
508
- disabled: isStreaming,
509
- "aria-label": "Attach folder",
510
- title: "Attach folder",
511
- className: "mb-0.5 shrink-0 rounded-[var(--radius-md)] border border-transparent p-2 text-muted-foreground transition-colors hover:border-border hover:bg-accent hover:text-foreground disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
512
- children: /* @__PURE__ */ jsx5(FolderUp, { className: "h-4 w-4" })
513
- }
514
- ),
515
- /* @__PURE__ */ jsx5(
516
- "input",
517
- {
518
- ref: folderInputRef,
519
- type: "file",
520
- multiple: true,
521
- className: "hidden",
522
- onChange: handleFolderChange,
523
- webkitdirectory: ""
524
- }
525
- )
526
- ] }),
527
- /* @__PURE__ */ jsx5(
528
- "textarea",
529
- {
530
- ref: textareaRef,
531
- value,
532
- onChange: handleChange,
533
- onKeyDown: handleKeyDown,
534
- placeholder,
535
- disabled: isStreaming || disabled,
536
- rows: 1,
537
- "aria-label": "Message input",
538
- className: "min-h-[42px] max-h-[160px] flex-1 resize-none bg-transparent py-2 text-[15px] leading-6 text-foreground placeholder:text-muted-foreground disabled:opacity-50 focus-visible:outline-none"
539
- }
540
- ),
541
- isStreaming ? /* @__PURE__ */ jsx5(
542
- "button",
543
- {
544
- type: "button",
545
- onClick: onCancel,
546
- "aria-label": "Stop response",
547
- className: "mb-0.5 shrink-0 rounded-[var(--radius-lg)] border border-[var(--code-error)]/20 bg-[var(--code-error)]/14 p-2.5 text-[var(--code-error)] transition-colors hover:bg-[var(--code-error)]/24 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--code-error)]/50",
548
- children: /* @__PURE__ */ jsx5(Square, { className: "h-4 w-4" })
549
- }
550
- ) : /* @__PURE__ */ jsxs4(
551
- "button",
552
- {
553
- type: "button",
554
- onClick: handleSend,
555
- disabled: !value.trim() || disabled,
556
- "aria-label": "Send message",
557
- className: "mb-0.5 inline-flex shrink-0 items-center gap-1.5 rounded-full border border-[var(--chat-send-border,var(--border-accent))] [background:var(--chat-send-bg,var(--brand-primary))] px-3.5 py-2.5 text-sm font-medium text-[var(--chat-send-color,white)] shadow-[var(--chat-send-shadow,0_6px_16px_rgba(15,23,42,0.12))] transition-all hover:translate-y-[-1px] hover:[background:var(--chat-send-hover-bg,var(--brand-strong))] disabled:opacity-30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--chat-send-ring,var(--border-accent))]",
558
- children: [
559
- /* @__PURE__ */ jsx5(Send, { className: "h-4 w-4" }),
560
- /* @__PURE__ */ jsx5("span", { children: "Send" })
561
- ]
562
- }
563
- )
564
- ] })
565
- ] }) }),
566
- (modelLabel || !hideShortcutHint) && /* @__PURE__ */ jsxs4("div", { className: "mt-2 flex items-center justify-between px-1", children: [
567
- /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-2", children: modelLabel && /* @__PURE__ */ jsxs4(
568
- "button",
569
- {
570
- type: "button",
571
- onClick: onModelClick,
572
- "aria-label": `Select model, current model ${modelLabel}`,
573
- className: "inline-flex items-center gap-1.5 rounded-[var(--radius-full)] border border-border bg-[linear-gradient(180deg,rgba(255,255,255,0.04),transparent)] px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:border-primary/20 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
574
- children: [
575
- /* @__PURE__ */ jsx5("span", { className: "w-1.5 h-1.5 rounded-full bg-[var(--code-success)]" }),
576
- modelLabel
577
- ]
578
- }
579
- ) }),
580
- !hideShortcutHint && /* @__PURE__ */ jsxs4("span", { className: "text-xs text-muted-foreground", children: [
581
- /* @__PURE__ */ jsx5("kbd", { className: "px-1 py-0.5 bg-background rounded border border-border text-[10px]", children: "Cmd" }),
582
- /* @__PURE__ */ jsx5("kbd", { className: "px-1 py-0.5 bg-background rounded border border-border text-[10px] ml-0.5", children: "L" }),
583
- /* @__PURE__ */ jsx5("span", { className: "ml-1", children: "to focus" })
584
- ] })
585
- ] })
586
- ]
587
- }
588
- );
589
- }
590
-
591
- // src/chat/chat-container.tsx
592
- import {
593
- memo as memo3,
594
- useCallback as useCallback2,
595
- useMemo,
596
- useRef as useRef2
597
- } from "react";
598
- import { ArrowDown } from "lucide-react";
599
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
600
- var OPENUI_NODE_TYPES = /* @__PURE__ */ new Set([
601
- "heading",
602
- "text",
603
- "badge",
604
- "stat",
605
- "key_value",
606
- "code",
607
- "markdown",
608
- "table",
609
- "actions",
610
- "separator",
611
- "stack",
612
- "grid",
613
- "card"
614
- ]);
615
- function isOpenUINode(value) {
616
- return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" && OPENUI_NODE_TYPES.has(value.type);
617
- }
618
- function extractOpenUISchema(output) {
619
- if (output == null) return null;
620
- if (isOpenUINode(output)) return [output];
621
- if (Array.isArray(output) && output.length > 0 && output.every(isOpenUINode)) {
622
- return output;
623
- }
624
- if (typeof output === "object" && !Array.isArray(output)) {
625
- const obj = output;
626
- for (const key of ["openui", "schema", "ui"]) {
627
- if (obj[key]) {
628
- const inner = obj[key];
629
- if (isOpenUINode(inner)) return [inner];
630
- if (Array.isArray(inner) && inner.length > 0 && inner.every(isOpenUINode)) {
631
- return inner;
632
- }
633
- }
634
- }
635
- }
636
- if (typeof output === "string") {
637
- try {
638
- const parsed = JSON.parse(output);
639
- return extractOpenUISchema(parsed);
640
- } catch {
641
- return null;
642
- }
643
- }
644
- return null;
645
- }
646
- function formatUnknown(value) {
647
- if (value == null) return void 0;
648
- if (typeof value === "string") return value;
649
- try {
650
- return JSON.stringify(value, null, 2);
651
- } catch {
652
- return String(value);
653
- }
654
- }
655
- function createdAtFromMessage(message) {
656
- return message.time?.created ? new Date(message.time.created) : void 0;
657
- }
658
- function mapToolPartToTimelineType(part) {
659
- const name = part.tool.toLowerCase().replace(/^tool:/, "");
660
- switch (name) {
661
- case "bash":
662
- case "shell":
663
- case "command":
664
- case "execute":
665
- return "bash";
666
- case "write":
667
- case "write_file":
668
- case "create_file":
669
- return "write";
670
- case "read":
671
- case "read_file":
672
- case "cat":
673
- return "read";
674
- case "edit":
675
- case "patch":
676
- case "sed":
677
- return "edit";
678
- case "glob":
679
- case "find":
680
- return "glob";
681
- case "ls":
682
- return "list";
683
- case "grep":
684
- case "search":
685
- case "rg":
686
- return "grep";
687
- case "inspect":
688
- return "inspect";
689
- default:
690
- return "unknown";
691
- }
692
- }
693
- function buildTimelineItems(messages, partMap, isStreaming, onOpenUIAction, enableOpenUI = true) {
694
- const items = [];
695
- const lastAssistantMessage = [...messages].reverse().find((message) => message.role === "assistant");
696
- const toToolCall = (part) => {
697
- const meta = getToolDisplayMetadata(part);
698
- const start = part.state.time?.start;
699
- const end = part.state.time?.end;
700
- return {
701
- id: part.id,
702
- type: mapToolPartToTimelineType(part),
703
- label: meta.description ? `${meta.title}: ${meta.description}` : meta.title,
704
- status: part.state.status === "completed" ? "success" : part.state.status === "error" ? "error" : "running",
705
- detail: formatUnknown(part.state.input),
706
- output: formatUnknown(part.state.output),
707
- duration: start && end ? end - start : void 0
708
- };
709
- };
710
- for (const message of messages) {
711
- const parts = partMap[message.id] ?? [];
712
- if (message.role === "user") {
713
- const content = parts.filter((part) => part.type === "text").map((part) => part.text).join("\n").trim();
714
- if (!content) continue;
715
- items.push({
716
- id: message.id,
717
- kind: "message",
718
- role: "user",
719
- content,
720
- timestamp: createdAtFromMessage(message)
721
- });
722
- continue;
723
- }
724
- const toolBuffer = [];
725
- const flushToolBuffer = (index) => {
726
- if (toolBuffer.length === 0) return;
727
- if (toolBuffer.length === 1) {
728
- items.push({
729
- id: `${message.id}-tool-${toolBuffer[0].id}`,
730
- kind: "tool",
731
- call: toToolCall(toolBuffer[0])
732
- });
733
- } else {
734
- items.push({
735
- id: `${message.id}-tool-group-${index}`,
736
- kind: "tool_group",
737
- title: "Tool activity",
738
- calls: toolBuffer.map((part) => toToolCall(part))
739
- });
740
- }
741
- if (enableOpenUI) {
742
- for (const part of toolBuffer) {
743
- if (part.state.status !== "completed" || !part.state.output) continue;
744
- const schema = extractOpenUISchema(part.state.output);
745
- if (!schema) continue;
746
- items.push({
747
- id: `${message.id}-openui-${part.id}`,
748
- kind: "custom",
749
- content: /* @__PURE__ */ jsx6("div", { className: "my-2 rounded-[var(--radius-lg)] border border-border bg-card p-4 shadow-[var(--shadow-card)]", children: /* @__PURE__ */ jsx6(OpenUIArtifactRenderer, { schema, onAction: onOpenUIAction }) })
750
- });
751
- }
752
- }
753
- toolBuffer.length = 0;
754
- };
755
- parts.forEach((part, index) => {
756
- const itemId = `${message.id}-${index}`;
757
- if (part.type === "tool") {
758
- toolBuffer.push(part);
759
- return;
760
- }
761
- flushToolBuffer(index);
762
- if (part.type === "text" && !part.synthetic && part.text.trim()) {
763
- if (enableOpenUI) {
764
- const jsonMatch = part.text.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
765
- if (jsonMatch) {
766
- const schema = extractOpenUISchema(jsonMatch[1]);
767
- if (schema) {
768
- const beforeJson = part.text.slice(0, part.text.indexOf("```")).trim();
769
- if (beforeJson) {
770
- items.push({
771
- id: `${itemId}-text`,
772
- kind: "message",
773
- role: "assistant",
774
- content: beforeJson,
775
- timestamp: createdAtFromMessage(message)
776
- });
777
- }
778
- items.push({
779
- id: `${itemId}-openui`,
780
- kind: "custom",
781
- content: /* @__PURE__ */ jsx6("div", { className: "my-2 rounded-[var(--radius-lg)] border border-border bg-card p-4 shadow-[var(--shadow-card)]", children: /* @__PURE__ */ jsx6(OpenUIArtifactRenderer, { schema, onAction: onOpenUIAction }) })
782
- });
783
- const afterJson = part.text.slice(part.text.lastIndexOf("```") + 3).trim();
784
- if (afterJson) {
785
- items.push({
786
- id: `${itemId}-after`,
787
- kind: "message",
788
- role: "assistant",
789
- content: afterJson,
790
- timestamp: createdAtFromMessage(message)
791
- });
792
- }
793
- return;
794
- }
795
- }
796
- }
797
- items.push({
798
- id: itemId,
799
- kind: "message",
800
- role: "assistant",
801
- content: part.text,
802
- timestamp: createdAtFromMessage(message),
803
- isStreaming: isStreaming && lastAssistantMessage?.id === message.id && index === parts.length - 1
804
- });
805
- return;
806
- }
807
- if (part.type === "reasoning") {
808
- items.push({
809
- id: itemId,
810
- kind: "custom",
811
- content: /* @__PURE__ */ jsx6(InlineThinkingItem, { part, defaultOpen: isStreaming && lastAssistantMessage?.id === message.id })
812
- });
813
- return;
814
- }
815
- });
816
- flushToolBuffer(parts.length);
817
- }
818
- const showThinking = isStreaming && lastAssistantMessage != null && !items.some(
819
- (item) => item.kind === "message" && item.role === "assistant" && item.id.startsWith(lastAssistantMessage.id)
820
- );
821
- return { items, showThinking };
822
- }
823
- var ChatContainer = memo3(
824
- ({
825
- messages,
826
- partMap,
827
- isStreaming,
828
- onSend,
829
- onCancel,
830
- branding,
831
- placeholder = "Type a message...",
832
- className,
833
- hideInput = false,
834
- renderToolDetail,
835
- presentation = "runs",
836
- modelLabel,
837
- onModelClick,
838
- pendingFiles,
839
- onRemoveFile,
840
- onAttach,
841
- disabled = false,
842
- onOpenUIAction,
843
- enableOpenUI = true,
844
- renderRunActions,
845
- renderUserMessageActions,
846
- renderToolActions
847
- }) => {
848
- const scrollRef = useRef2(null);
849
- const groups = useRunGroups({ messages, partMap, isStreaming });
850
- const runs = groups.filter((g) => g.type === "run").map((g) => g.run);
851
- const { isCollapsed, toggleCollapse } = useRunCollapseState(runs);
852
- const { isAtBottom, scrollToBottom } = useAutoScroll(scrollRef, [
853
- messages,
854
- partMap,
855
- isStreaming
856
- ]);
857
- const timeline = useMemo(
858
- () => buildTimelineItems(messages, partMap, isStreaming, onOpenUIAction, enableOpenUI),
859
- [messages, partMap, isStreaming, onOpenUIAction, enableOpenUI]
860
- );
861
- const handleSend = useCallback2(
862
- (text) => {
863
- onSend?.(text);
864
- },
865
- [onSend]
866
- );
867
- return /* @__PURE__ */ jsxs5("div", { className: cn("flex h-full flex-col", className), children: [
868
- /* @__PURE__ */ jsx6(
869
- "div",
870
- {
871
- ref: scrollRef,
872
- className: "flex-1 overflow-y-auto [scrollbar-gutter:stable]",
873
- children: messages.length === 0 ? /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx6("div", { className: "max-w-md text-center", children: /* @__PURE__ */ jsx6("div", { className: "text-sm font-medium text-muted-foreground", children: "Start a conversation." }) }) }) : presentation === "timeline" ? /* @__PURE__ */ jsx6("div", { className: "mx-auto flex min-h-full w-full max-w-3xl flex-col justify-end", children: /* @__PURE__ */ jsx6(AgentTimeline, { items: timeline.items, isThinking: timeline.showThinking }) }) : /* @__PURE__ */ jsx6("div", { className: "mx-auto flex min-h-full w-full max-w-3xl flex-col justify-end", children: /* @__PURE__ */ jsx6(
874
- MessageList,
875
- {
876
- groups,
877
- partMap,
878
- isCollapsed,
879
- onToggleCollapse: toggleCollapse,
880
- branding,
881
- renderToolDetail,
882
- renderRunActions,
883
- renderUserMessageActions,
884
- renderToolActions
885
- }
886
- ) })
887
- }
888
- ),
889
- !isAtBottom && /* @__PURE__ */ jsx6("div", { className: "relative z-10 -mt-10 flex justify-center", children: /* @__PURE__ */ jsxs5(
890
- "button",
891
- {
892
- onClick: scrollToBottom,
893
- className: cn(
894
- "flex items-center gap-1.5 px-3 py-1.5 rounded-full",
895
- "border border-border bg-card shadow-[0_6px_16px_rgba(15,23,42,0.08)]",
896
- "text-xs text-foreground transition-colors hover:bg-accent",
897
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
898
- ),
899
- children: [
900
- /* @__PURE__ */ jsx6(ArrowDown, { className: "w-3 h-3" }),
901
- "Scroll to bottom"
902
- ]
903
- }
904
- ) }),
905
- !hideInput && onSend && /* @__PURE__ */ jsx6(
906
- ChatInput,
907
- {
908
- onSend: handleSend,
909
- onCancel,
910
- isStreaming,
911
- placeholder,
912
- modelLabel,
913
- onModelClick,
914
- pendingFiles,
915
- onRemoveFile,
916
- onAttach,
917
- disabled,
918
- className: "shrink-0 border-t border-border bg-background"
919
- }
920
- )
921
- ] });
922
- }
923
- );
924
- ChatContainer.displayName = "ChatContainer";
925
-
926
- export {
927
- UserMessage,
928
- MessageList,
929
- ThinkingIndicator,
930
- AgentTimeline,
931
- ChatInput,
932
- ChatContainer
933
- };