@lantos1618/better-ui 0.3.1 → 0.4.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.
@@ -0,0 +1,1977 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/components/index.ts
32
+ var components_exports = {};
33
+ __export(components_exports, {
34
+ Chat: () => Chat,
35
+ ChatPanel: () => ChatPanel,
36
+ ChatProvider: () => ChatProvider,
37
+ CodeBlockView: () => CodeBlockView,
38
+ Composer: () => Composer,
39
+ DataTableView: () => DataTableView,
40
+ FileUploadView: () => FileUploadView,
41
+ FormView: () => FormView,
42
+ Markdown: () => Markdown,
43
+ MediaDisplayView: () => MediaDisplayView,
44
+ Message: () => Message,
45
+ Panel: () => Panel,
46
+ ProgressView: () => ProgressView,
47
+ QuestionView: () => QuestionView,
48
+ ThemeProvider: () => ThemeProvider,
49
+ Thread: () => Thread,
50
+ ToastProvider: () => ToastProvider,
51
+ ToolResult: () => ToolResult,
52
+ createToolStateStore: () => createToolStateStore,
53
+ useChatContext: () => useChatContext,
54
+ useChatToolOutput: () => useChatToolOutput,
55
+ useToast: () => useToast,
56
+ useToolEffect: () => useToolEffect,
57
+ useToolOutput: () => useToolOutput,
58
+ useToolState: () => useToolState
59
+ });
60
+ module.exports = __toCommonJS(components_exports);
61
+
62
+ // src/components/ChatProvider.tsx
63
+ var import_react2 = require("react");
64
+ var import_react3 = require("@ai-sdk/react");
65
+ var import_ai = require("ai");
66
+
67
+ // src/components/useToolStateStore.ts
68
+ var import_react = require("react");
69
+ function createToolStateStore() {
70
+ let state = /* @__PURE__ */ new Map();
71
+ const keyListeners = /* @__PURE__ */ new Map();
72
+ const globalListeners = /* @__PURE__ */ new Set();
73
+ let seqCounter = 0;
74
+ function notifyKey(toolCallId) {
75
+ const listeners = keyListeners.get(toolCallId);
76
+ if (listeners) {
77
+ for (const l of listeners) l();
78
+ }
79
+ for (const l of globalListeners) l();
80
+ }
81
+ return {
82
+ get(toolCallId) {
83
+ return state.get(toolCallId);
84
+ },
85
+ set(toolCallId, entry) {
86
+ state = new Map(state);
87
+ const existing = state.get(toolCallId);
88
+ const seqNo = existing?.seqNo ?? ++seqCounter;
89
+ state.set(toolCallId, { ...entry, seqNo });
90
+ notifyKey(toolCallId);
91
+ },
92
+ clear() {
93
+ state = /* @__PURE__ */ new Map();
94
+ seqCounter = 0;
95
+ for (const l of globalListeners) l();
96
+ },
97
+ subscribe(toolCallId, listener) {
98
+ let listeners = keyListeners.get(toolCallId);
99
+ if (!listeners) {
100
+ listeners = /* @__PURE__ */ new Set();
101
+ keyListeners.set(toolCallId, listeners);
102
+ }
103
+ listeners.add(listener);
104
+ return () => {
105
+ listeners.delete(listener);
106
+ if (listeners.size === 0) {
107
+ keyListeners.delete(toolCallId);
108
+ }
109
+ };
110
+ },
111
+ subscribeAll(listener) {
112
+ globalListeners.add(listener);
113
+ return () => {
114
+ globalListeners.delete(listener);
115
+ };
116
+ },
117
+ getSnapshot() {
118
+ return state;
119
+ },
120
+ findAnchor(entityId) {
121
+ let oldest;
122
+ for (const [toolCallId, entry] of state) {
123
+ if (entry.entityId === entityId) {
124
+ if (!oldest || (entry.seqNo ?? 0) < (oldest.entry.seqNo ?? 0)) {
125
+ oldest = { toolCallId, entry };
126
+ }
127
+ }
128
+ }
129
+ return oldest;
130
+ },
131
+ getLatestPerEntity() {
132
+ const result = /* @__PURE__ */ new Map();
133
+ const entityLatest = /* @__PURE__ */ new Map();
134
+ for (const [toolCallId, entry] of state) {
135
+ if (entry.entityId) {
136
+ const current = entityLatest.get(entry.entityId);
137
+ if (!current || (entry.seqNo ?? 0) > (current.entry.seqNo ?? 0)) {
138
+ entityLatest.set(entry.entityId, { toolCallId, entry });
139
+ }
140
+ } else {
141
+ result.set(toolCallId, entry);
142
+ }
143
+ }
144
+ for (const [, { toolCallId, entry }] of entityLatest) {
145
+ result.set(toolCallId, entry);
146
+ }
147
+ return result;
148
+ }
149
+ };
150
+ }
151
+ function useToolState(store, toolCallId) {
152
+ const subscribe = (0, import_react.useCallback)(
153
+ (listener) => store.subscribe(toolCallId, listener),
154
+ [store, toolCallId]
155
+ );
156
+ const getSnapshot = (0, import_react.useCallback)(
157
+ () => store.get(toolCallId),
158
+ [store, toolCallId]
159
+ );
160
+ return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
161
+ }
162
+
163
+ // src/components/ChatProvider.tsx
164
+ var import_jsx_runtime = require("react/jsx-runtime");
165
+ var ChatContext = (0, import_react2.createContext)(null);
166
+ function useChatContext() {
167
+ const ctx = (0, import_react2.useContext)(ChatContext);
168
+ if (!ctx) {
169
+ throw new Error("useChatContext must be used within a <ChatProvider>");
170
+ }
171
+ return ctx;
172
+ }
173
+ function ChatProvider({ endpoint = "/api/chat", tools, toolStateStore: externalStore, persistence, threadId, children }) {
174
+ const transportRef = (0, import_react2.useRef)(new import_ai.DefaultChatTransport({ api: endpoint }));
175
+ const { messages, setMessages, sendMessage, status, addToolOutput } = (0, import_react3.useChat)({
176
+ transport: transportRef.current
177
+ });
178
+ const isLoading = status === "streaming" || status === "submitted";
179
+ const [threads, setThreads] = (0, import_react2.useState)([]);
180
+ const [activeThreadId, setActiveThreadId] = (0, import_react2.useState)(threadId);
181
+ (0, import_react2.useEffect)(() => {
182
+ if (persistence) {
183
+ persistence.listThreads().then(setThreads).catch((err) => console.warn("[better-ui] persistence error:", err));
184
+ }
185
+ }, [persistence]);
186
+ (0, import_react2.useEffect)(() => {
187
+ if (persistence && activeThreadId) {
188
+ persistence.getMessages(activeThreadId).then((msgs) => {
189
+ setMessages(msgs);
190
+ }).catch((err) => console.warn("[better-ui] persistence error:", err));
191
+ }
192
+ }, [persistence, activeThreadId, setMessages]);
193
+ const prevStatusRef = (0, import_react2.useRef)(status);
194
+ (0, import_react2.useEffect)(() => {
195
+ const wasStreaming = prevStatusRef.current === "streaming" || prevStatusRef.current === "submitted";
196
+ const isNowReady = status === "ready";
197
+ prevStatusRef.current = status;
198
+ if (persistence && activeThreadId && wasStreaming && isNowReady && messages.length > 0) {
199
+ persistence.saveMessages(activeThreadId, messages).catch((err) => console.warn("[better-ui] persistence error:", err));
200
+ }
201
+ }, [status, persistence, activeThreadId, messages]);
202
+ const internalStoreRef = (0, import_react2.useRef)(createToolStateStore());
203
+ const toolStateStore = externalStore || internalStoreRef.current;
204
+ const createThreadFn = (0, import_react2.useCallback)(async (title) => {
205
+ if (!persistence) throw new Error("Persistence not configured");
206
+ const thread = await persistence.createThread(title);
207
+ setThreads((prev) => [thread, ...prev]);
208
+ setActiveThreadId(thread.id);
209
+ setMessages([]);
210
+ toolStateStore.clear();
211
+ return thread;
212
+ }, [persistence, setMessages, toolStateStore]);
213
+ const switchThreadFn = (0, import_react2.useCallback)(async (id) => {
214
+ if (!persistence) throw new Error("Persistence not configured");
215
+ setActiveThreadId(id);
216
+ setMessages([]);
217
+ toolStateStore.clear();
218
+ }, [persistence, setMessages, toolStateStore]);
219
+ const deleteThreadFn = (0, import_react2.useCallback)(async (id) => {
220
+ if (!persistence) throw new Error("Persistence not configured");
221
+ await persistence.deleteThread(id);
222
+ setThreads((prev) => prev.filter((t) => t.id !== id));
223
+ if (activeThreadId === id) {
224
+ setActiveThreadId(void 0);
225
+ setMessages([]);
226
+ }
227
+ }, [persistence, activeThreadId, setMessages]);
228
+ const versionRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
229
+ const dirtyToolCallIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
230
+ const collectDirtyState = (0, import_react2.useCallback)(() => {
231
+ const dirty = dirtyToolCallIdsRef.current;
232
+ if (dirty.size === 0) return null;
233
+ const stateContext = {};
234
+ for (const id of dirty) {
235
+ const entry = toolStateStore.get(id);
236
+ if (entry?.output && entry.toolName) {
237
+ stateContext[entry.toolName] = entry.output;
238
+ }
239
+ }
240
+ dirty.clear();
241
+ return Object.keys(stateContext).length > 0 ? stateContext : null;
242
+ }, [toolStateStore]);
243
+ const executeToolDirect = (0, import_react2.useCallback)(async (toolName, toolInput, toolCallId) => {
244
+ const currentVersion = (versionRef.current.get(toolCallId) || 0) + 1;
245
+ versionRef.current.set(toolCallId, currentVersion);
246
+ const existing = toolStateStore.get(toolCallId);
247
+ toolStateStore.set(toolCallId, {
248
+ output: existing?.output ?? null,
249
+ loading: true,
250
+ error: null,
251
+ version: currentVersion,
252
+ toolName
253
+ });
254
+ try {
255
+ const response = await fetch("/api/tools/execute", {
256
+ method: "POST",
257
+ headers: { "Content-Type": "application/json" },
258
+ body: JSON.stringify({ tool: toolName, input: toolInput })
259
+ });
260
+ if (!response.ok) {
261
+ const errBody = await response.json().catch(() => ({}));
262
+ throw new Error(errBody.error || `Tool execution failed (${response.status})`);
263
+ }
264
+ const { result } = await response.json();
265
+ if (versionRef.current.get(toolCallId) === currentVersion) {
266
+ toolStateStore.set(toolCallId, {
267
+ output: result,
268
+ loading: false,
269
+ error: null,
270
+ version: currentVersion,
271
+ toolName
272
+ });
273
+ dirtyToolCallIdsRef.current.add(toolCallId);
274
+ const toolDef = tools[toolName];
275
+ if (toolDef?.autoRespond) {
276
+ dirtyToolCallIdsRef.current.delete(toolCallId);
277
+ const otherState = collectDirtyState();
278
+ const stateContext = { ...otherState, [toolName]: result };
279
+ const envelope = JSON.stringify({ text: "", stateContext, _meta: { hidden: true } });
280
+ sendMessage({ text: envelope });
281
+ }
282
+ }
283
+ } catch (error) {
284
+ console.error("Tool execution error:", error);
285
+ if (versionRef.current.get(toolCallId) === currentVersion) {
286
+ toolStateStore.set(toolCallId, {
287
+ output: existing?.output ?? null,
288
+ loading: false,
289
+ error: error instanceof Error ? error.message : "Tool execution failed",
290
+ version: currentVersion,
291
+ toolName
292
+ });
293
+ }
294
+ }
295
+ }, [toolStateStore, tools, sendMessage, collectDirtyState]);
296
+ const confirmTool = (0, import_react2.useCallback)(async (toolCallId, toolName, toolInput) => {
297
+ const currentVersion = (versionRef.current.get(toolCallId) || 0) + 1;
298
+ versionRef.current.set(toolCallId, currentVersion);
299
+ toolStateStore.set(toolCallId, {
300
+ output: null,
301
+ loading: true,
302
+ error: null,
303
+ version: currentVersion,
304
+ toolName,
305
+ status: "confirmed"
306
+ });
307
+ try {
308
+ const response = await fetch("/api/tools/confirm", {
309
+ method: "POST",
310
+ headers: { "Content-Type": "application/json" },
311
+ body: JSON.stringify({ tool: toolName, input: toolInput })
312
+ });
313
+ if (!response.ok) {
314
+ const errBody = await response.json().catch(() => ({}));
315
+ throw new Error(errBody.error || `Tool execution failed (${response.status})`);
316
+ }
317
+ const { result } = await response.json();
318
+ if (versionRef.current.get(toolCallId) === currentVersion) {
319
+ toolStateStore.set(toolCallId, {
320
+ output: result,
321
+ loading: false,
322
+ error: null,
323
+ version: currentVersion,
324
+ toolName,
325
+ status: "confirmed"
326
+ });
327
+ }
328
+ await addToolOutput({
329
+ state: "output-available",
330
+ tool: toolName,
331
+ toolCallId,
332
+ output: result
333
+ });
334
+ } catch (error) {
335
+ console.error("HITL tool execution error:", error);
336
+ if (versionRef.current.get(toolCallId) === currentVersion) {
337
+ toolStateStore.set(toolCallId, {
338
+ output: null,
339
+ loading: false,
340
+ error: error instanceof Error ? error.message : "Tool execution failed",
341
+ version: currentVersion,
342
+ toolName,
343
+ status: "confirmed"
344
+ });
345
+ }
346
+ }
347
+ }, [toolStateStore, addToolOutput]);
348
+ const rejectTool = (0, import_react2.useCallback)(async (toolCallId, toolName) => {
349
+ const currentVersion = (versionRef.current.get(toolCallId) || 0) + 1;
350
+ versionRef.current.set(toolCallId, currentVersion);
351
+ toolStateStore.set(toolCallId, {
352
+ output: null,
353
+ loading: false,
354
+ error: null,
355
+ version: currentVersion,
356
+ toolName,
357
+ status: "rejected"
358
+ });
359
+ await addToolOutput({
360
+ state: "output-error",
361
+ tool: toolName,
362
+ toolCallId,
363
+ errorText: "User rejected this action"
364
+ });
365
+ }, [toolStateStore, addToolOutput]);
366
+ const retryTool = (0, import_react2.useCallback)((toolCallId, toolName, toolInput) => {
367
+ executeToolDirect(toolName, toolInput ?? {}, toolCallId);
368
+ }, [executeToolDirect]);
369
+ const callbackCacheRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
370
+ const getOnAction = (0, import_react2.useCallback)((toolCallId, toolName) => {
371
+ const key = `${toolName}:${toolCallId}`;
372
+ let cb = callbackCacheRef.current.get(key);
373
+ if (!cb) {
374
+ cb = (input) => {
375
+ executeToolDirect(toolName, input, toolCallId);
376
+ };
377
+ callbackCacheRef.current.set(key, cb);
378
+ }
379
+ return cb;
380
+ }, [executeToolDirect]);
381
+ const handleSendMessage = (0, import_react2.useCallback)((text) => {
382
+ if (!text.trim()) return;
383
+ const stateContext = collectDirtyState();
384
+ if (stateContext) {
385
+ const envelope = JSON.stringify({ text, stateContext, _meta: { hidden: false } });
386
+ sendMessage({ text: envelope });
387
+ } else {
388
+ sendMessage({ text });
389
+ }
390
+ }, [sendMessage, collectDirtyState]);
391
+ const value = {
392
+ messages,
393
+ sendMessage: handleSendMessage,
394
+ isLoading,
395
+ status,
396
+ tools,
397
+ executeToolDirect,
398
+ getOnAction,
399
+ toolStateStore,
400
+ confirmTool,
401
+ rejectTool,
402
+ retryTool,
403
+ ...persistence ? {
404
+ threads,
405
+ threadId: activeThreadId,
406
+ createThread: createThreadFn,
407
+ switchThread: switchThreadFn,
408
+ deleteThread: deleteThreadFn
409
+ } : {}
410
+ };
411
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatContext.Provider, { value, children });
412
+ }
413
+
414
+ // src/components/Thread.tsx
415
+ var import_react6 = require("react");
416
+
417
+ // src/components/Message.tsx
418
+ var import_ai2 = require("ai");
419
+
420
+ // src/components/ToolResult.tsx
421
+ var import_react4 = require("react");
422
+ var import_jsx_runtime2 = require("react/jsx-runtime");
423
+ function useIsFollowup(toolStateStore, toolCallId) {
424
+ const subscribe = (0, import_react4.useCallback)(
425
+ (listener) => toolStateStore.subscribeAll(listener),
426
+ [toolStateStore]
427
+ );
428
+ const getSnapshot = (0, import_react4.useCallback)(() => {
429
+ const myEntry = toolStateStore.get(toolCallId);
430
+ if (!myEntry?.entityId) return false;
431
+ const anchor = toolStateStore.findAnchor(myEntry.entityId);
432
+ return !!anchor && anchor.toolCallId !== toolCallId;
433
+ }, [toolStateStore, toolCallId]);
434
+ return (0, import_react4.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
435
+ }
436
+ function ToolResult({
437
+ toolName,
438
+ toolCallId,
439
+ output,
440
+ toolInput,
441
+ hasResult,
442
+ toolPartState,
443
+ toolStateStore,
444
+ tools,
445
+ getOnAction,
446
+ onConfirm,
447
+ onReject,
448
+ onRetry,
449
+ className
450
+ }) {
451
+ const storeState = useToolState(toolStateStore, toolCallId);
452
+ const toolDef = tools[toolName];
453
+ (0, import_react4.useEffect)(() => {
454
+ const existing = toolStateStore.get(toolCallId);
455
+ const entityId = toolDef?.getGroupKey?.(toolInput);
456
+ const anchor = entityId ? toolStateStore.findAnchor(entityId) : void 0;
457
+ const isFollowup2 = anchor && anchor.toolCallId !== toolCallId;
458
+ if (!existing) {
459
+ if (isFollowup2) {
460
+ if (hasResult && output != null) {
461
+ toolStateStore.set(anchor.toolCallId, {
462
+ ...anchor.entry,
463
+ output,
464
+ loading: false
465
+ });
466
+ }
467
+ toolStateStore.set(toolCallId, {
468
+ output: null,
469
+ loading: false,
470
+ error: null,
471
+ version: 1,
472
+ toolName,
473
+ entityId,
474
+ toolInput
475
+ });
476
+ } else {
477
+ toolStateStore.set(toolCallId, {
478
+ output: hasResult ? output : null,
479
+ loading: !hasResult,
480
+ error: null,
481
+ version: 1,
482
+ toolName,
483
+ entityId,
484
+ toolInput
485
+ });
486
+ }
487
+ } else if (isFollowup2) {
488
+ if (hasResult && output != null && existing.output == null) {
489
+ toolStateStore.set(anchor.toolCallId, {
490
+ ...toolStateStore.get(anchor.toolCallId),
491
+ output,
492
+ loading: false
493
+ });
494
+ toolStateStore.set(toolCallId, {
495
+ ...existing,
496
+ output: null,
497
+ loading: false
498
+ });
499
+ }
500
+ } else if (hasResult && output != null && !existing.output && existing.loading) {
501
+ toolStateStore.set(toolCallId, {
502
+ ...existing,
503
+ output,
504
+ loading: false,
505
+ entityId: existing.entityId ?? entityId,
506
+ toolInput: existing.toolInput ?? toolInput
507
+ });
508
+ } else if (!existing.entityId && entityId) {
509
+ toolStateStore.set(toolCallId, {
510
+ ...existing,
511
+ entityId,
512
+ toolInput: existing.toolInput ?? toolInput
513
+ });
514
+ }
515
+ }, [hasResult, output, toolCallId, toolName, toolInput, toolStateStore, toolDef]);
516
+ const isFollowup = useIsFollowup(toolStateStore, toolCallId);
517
+ const isHITL = toolDef?.requiresConfirmation ?? false;
518
+ const needsUserConfirm = isHITL && toolDef.shouldConfirm(toolInput);
519
+ const hasOutput = hasResult || !!storeState?.output;
520
+ const isRejected = storeState?.status === "rejected";
521
+ const isConfirming = storeState?.loading && storeState?.status === "confirmed";
522
+ const argsComplete = toolPartState === "input-available" || toolPartState === "output-available";
523
+ const autoApprovedRef = (0, import_react4.useRef)(false);
524
+ (0, import_react4.useEffect)(() => {
525
+ if (isHITL && argsComplete && !needsUserConfirm && !hasOutput && !autoApprovedRef.current && toolInput != null) {
526
+ autoApprovedRef.current = true;
527
+ onConfirm?.(toolCallId, toolName, toolInput ?? {});
528
+ }
529
+ }, [isHITL, argsComplete, needsUserConfirm, hasOutput, toolInput, toolCallId, toolName, onConfirm]);
530
+ if (!toolDef) {
531
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `text-sm text-[var(--bui-fg-muted,#71717a)] px-4 py-2 bg-[var(--bui-bg-elevated,#27272a)]/50 rounded-lg ${className || ""}`, children: [
532
+ "Unknown tool: ",
533
+ toolName
534
+ ] });
535
+ }
536
+ if (isFollowup) {
537
+ return null;
538
+ }
539
+ if (argsComplete && needsUserConfirm && !hasOutput && !isRejected && !isConfirming) {
540
+ const inputObj = toolInput && typeof toolInput === "object" ? toolInput : null;
541
+ const simpleEntries = inputObj ? Object.entries(inputObj).filter(([, v]) => typeof v !== "object" || v === null) : [];
542
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-warning-border,rgba(180,83,9,0.5))] rounded-xl overflow-hidden ${className || ""}`, children: [
543
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 px-4 py-3 border-b border-[var(--bui-warning-border,rgba(180,83,9,0.5))]/50", children: [
544
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-2 h-2 bg-[var(--bui-warning-fg,#f59e0b)] rounded-full" }),
545
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium", children: [
546
+ toolName,
547
+ " requires confirmation"
548
+ ] })
549
+ ] }),
550
+ simpleEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "px-4 py-3 space-y-1", children: simpleEntries.map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
551
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs", children: [
552
+ key,
553
+ ": "
554
+ ] }),
555
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: String(value) })
556
+ ] }, key)) }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2 px-4 py-3 border-t border-[var(--bui-border,#27272a)]", children: [
558
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
559
+ "button",
560
+ {
561
+ onClick: () => onConfirm?.(toolCallId, toolName, toolInput ?? {}),
562
+ className: "px-4 py-2 bg-[var(--bui-success,#059669)] text-white text-sm rounded-lg hover:bg-[var(--bui-success,#059669)] transition-colors",
563
+ children: "Approve"
564
+ }
565
+ ),
566
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
567
+ "button",
568
+ {
569
+ onClick: () => onReject?.(toolCallId, toolName),
570
+ className: "px-4 py-2 bg-[var(--bui-bg-hover,#3f3f46)] text-[var(--bui-fg-secondary,#a1a1aa)] text-sm rounded-lg hover:bg-[var(--bui-bg-hover,#3f3f46)] transition-colors",
571
+ children: "Reject"
572
+ }
573
+ )
574
+ ] })
575
+ ] });
576
+ }
577
+ if (isHITL && isRejected) {
578
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-error-border,rgba(153,27,27,0.5))] rounded-xl p-4 ${className || ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
579
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs font-medium text-[var(--bui-error-fg,#f87171)] bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] px-2 py-0.5 rounded", children: "Rejected" }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: toolName })
581
+ ] }) });
582
+ }
583
+ const resolvedOutput = storeState?.output ?? output;
584
+ const resolvedLoading = storeState?.loading ?? !hasResult;
585
+ if (storeState?.error && !resolvedOutput && !resolvedLoading) {
586
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] border border-[var(--bui-error-border,rgba(153,27,27,0.5))] rounded-xl p-4 ${className || ""}`, children: [
587
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
588
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-4 h-4 text-[var(--bui-error-fg,#f87171)] shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" }) }),
589
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-[var(--bui-error-fg,#f87171)] text-sm font-medium", children: [
590
+ toolName,
591
+ " failed"
592
+ ] })
593
+ ] }),
594
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[var(--bui-error-fg,#f87171)]/70 text-xs mb-3", children: storeState.error }),
595
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
596
+ "button",
597
+ {
598
+ onClick: () => onRetry(toolCallId, toolName, storeState.toolInput ?? toolInput),
599
+ className: "px-3 py-1.5 bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] text-[var(--bui-error-fg,#f87171)] text-xs rounded-lg border border-[var(--bui-error-border,rgba(153,27,27,0.5))] hover:bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] transition-colors",
600
+ children: "Retry"
601
+ }
602
+ )
603
+ ] });
604
+ }
605
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: className || "", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
606
+ toolDef.View,
607
+ {
608
+ data: resolvedOutput,
609
+ loading: resolvedLoading,
610
+ onAction: getOnAction(toolCallId, toolName),
611
+ error: storeState?.error ? new Error(storeState.error) : null
612
+ }
613
+ ) });
614
+ }
615
+
616
+ // src/components/Markdown.tsx
617
+ var import_react5 = __toESM(require("react"));
618
+ var import_react_markdown = __toESM(require("react-markdown"));
619
+ var import_remark_gfm = __toESM(require("remark-gfm"));
620
+ var import_jsx_runtime3 = require("react/jsx-runtime");
621
+ function ShikiCode({ code, language }) {
622
+ const [html, setHtml] = (0, import_react5.useState)(null);
623
+ (0, import_react5.useEffect)(() => {
624
+ import("shiki").then(({ codeToHtml }) => {
625
+ codeToHtml(code, { lang: language, theme: "github-dark" }).then(setHtml).catch(() => setHtml(null));
626
+ });
627
+ }, [code, language]);
628
+ if (html) {
629
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { dangerouslySetInnerHTML: { __html: html } });
630
+ }
631
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { className: "text-[var(--bui-fg,#f4f4f5)]", children: code });
632
+ }
633
+ function CopyButton({ text }) {
634
+ const [copied, setCopied] = (0, import_react5.useState)(false);
635
+ const handleCopy = () => {
636
+ navigator.clipboard.writeText(text);
637
+ setCopied(true);
638
+ setTimeout(() => setCopied(false), 2e3);
639
+ };
640
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
641
+ "button",
642
+ {
643
+ onClick: handleCopy,
644
+ className: "absolute top-2 right-2 px-2 py-1 text-xs text-[var(--bui-fg-secondary,#a1a1aa)] hover:text-[var(--bui-fg,#f4f4f5)] bg-[var(--bui-bg-hover,#3f3f46)] hover:bg-[var(--bui-bg-hover,#3f3f46)] rounded transition-colors",
645
+ children: copied ? "Copied!" : "Copy"
646
+ }
647
+ );
648
+ }
649
+ var components = {
650
+ pre({ children, ...props }) {
651
+ let codeText = "";
652
+ import_react5.default.Children.forEach(children, (child) => {
653
+ if (import_react5.default.isValidElement(child) && child.props) {
654
+ const childProps = child.props;
655
+ if (typeof childProps.children === "string") {
656
+ codeText = childProps.children;
657
+ }
658
+ }
659
+ });
660
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative group my-3", children: [
661
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CopyButton, { text: codeText }),
662
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { className: "bg-[var(--bui-bg-surface,#18181b)] border border-[var(--bui-border-strong,#3f3f46)] rounded-lg p-4 overflow-x-auto text-sm", ...props, children })
663
+ ] });
664
+ },
665
+ code({ className, children, ...props }) {
666
+ const isBlock = className?.startsWith("language-");
667
+ if (isBlock) {
668
+ const language = className.replace("language-", "");
669
+ const codeText = typeof children === "string" ? children : String(children).replace(/\n$/, "");
670
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ShikiCode, { code: codeText, language });
671
+ }
672
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { className: "bg-[var(--bui-bg-hover,#3f3f46)] text-[var(--bui-fg,#f4f4f5)] px-1.5 py-0.5 rounded text-sm", ...props, children });
673
+ },
674
+ h1({ children, ...props }) {
675
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className: "text-xl font-bold text-[var(--bui-fg,#f4f4f5)] mt-4 mb-2", ...props, children });
676
+ },
677
+ h2({ children, ...props }) {
678
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "text-lg font-semibold text-[var(--bui-fg,#f4f4f5)] mt-3 mb-2", ...props, children });
679
+ },
680
+ h3({ children, ...props }) {
681
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "text-base font-semibold text-[var(--bui-fg,#f4f4f5)] mt-3 mb-1", ...props, children });
682
+ },
683
+ p({ children, ...props }) {
684
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mb-2 last:mb-0", ...props, children });
685
+ },
686
+ ul({ children, ...props }) {
687
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "list-disc list-inside mb-2 space-y-1", ...props, children });
688
+ },
689
+ ol({ children, ...props }) {
690
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ol", { className: "list-decimal list-inside mb-2 space-y-1", ...props, children });
691
+ },
692
+ li({ children, ...props }) {
693
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { className: "text-[var(--bui-fg-secondary,#a1a1aa)]", ...props, children });
694
+ },
695
+ blockquote({ children, ...props }) {
696
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("blockquote", { className: "border-l-2 border-[var(--bui-border-strong,#3f3f46)] pl-3 my-2 text-[var(--bui-fg-secondary,#a1a1aa)] italic", ...props, children });
697
+ },
698
+ table({ children, ...props }) {
699
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "overflow-x-auto my-3", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { className: "min-w-full border border-[var(--bui-border-strong,#3f3f46)] text-sm", ...props, children }) });
700
+ },
701
+ thead({ children, ...props }) {
702
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { className: "bg-[var(--bui-bg-elevated,#27272a)]", ...props, children });
703
+ },
704
+ th({ children, ...props }) {
705
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { className: "px-3 py-2 text-left text-[var(--bui-fg-secondary,#a1a1aa)] font-medium border-b border-[var(--bui-border-strong,#3f3f46)]", ...props, children });
706
+ },
707
+ td({ children, ...props }) {
708
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { className: "px-3 py-2 text-[var(--bui-fg-secondary,#a1a1aa)] border-b border-[var(--bui-border,#27272a)]", ...props, children });
709
+ },
710
+ a({ children, href, ...props }) {
711
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { href, className: "text-[var(--bui-primary-hover,#3b82f6)] hover:text-[var(--bui-primary-hover,#3b82f6)] underline", target: "_blank", rel: "noopener noreferrer", ...props, children });
712
+ },
713
+ hr(props) {
714
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("hr", { className: "border-[var(--bui-border-strong,#3f3f46)] my-4", ...props });
715
+ },
716
+ del({ children, ...props }) {
717
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("del", { className: "text-[var(--bui-fg-muted,#71717a)]", ...props, children });
718
+ }
719
+ };
720
+ function Markdown({ content, className }) {
721
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `prose-invert max-w-none ${className || ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components, children: content }) });
722
+ }
723
+
724
+ // src/components/Message.tsx
725
+ var import_jsx_runtime4 = require("react/jsx-runtime");
726
+ function Message({ message, tools, toolStateStore, getOnAction, onConfirm, onReject, onRetry, className }) {
727
+ const rawTextContent = message.parts.filter(import_ai2.isTextUIPart).map((part) => part.text).join("");
728
+ const isUser = message.role === "user";
729
+ let textContent = rawTextContent;
730
+ let isHidden = false;
731
+ if (isUser && textContent) {
732
+ try {
733
+ const envelope = JSON.parse(textContent);
734
+ if (envelope && typeof envelope === "object" && "_meta" in envelope) {
735
+ textContent = envelope.text || "";
736
+ isHidden = !!envelope._meta?.hidden;
737
+ }
738
+ } catch {
739
+ }
740
+ }
741
+ const toolParts = [];
742
+ for (const part of message.parts) {
743
+ if ((0, import_ai2.isToolOrDynamicToolUIPart)(part)) {
744
+ const toolName = (0, import_ai2.getToolOrDynamicToolName)(part);
745
+ toolParts.push({
746
+ toolName,
747
+ toolCallId: part.toolCallId,
748
+ state: part.state,
749
+ output: part.state === "output-available" ? part.output : null,
750
+ input: "input" in part ? part.input : void 0
751
+ });
752
+ }
753
+ }
754
+ if (isHidden && !textContent && toolParts.length === 0) return null;
755
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `space-y-3 ${className || ""}`, children: [
756
+ textContent && !isHidden && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `flex ${message.role === "user" ? "justify-end" : "justify-start"}`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
757
+ "div",
758
+ {
759
+ className: `max-w-[85%] px-4 py-2.5 rounded-2xl text-sm ${message.role === "user" ? "bg-[var(--bui-user-bg,#f4f4f5)] text-[var(--bui-user-fg,#18181b)]" : "bg-[var(--bui-bg-elevated,#27272a)] text-[var(--bui-fg,#f4f4f5)]"}`,
760
+ children: message.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Markdown, { content: textContent }) : textContent
761
+ }
762
+ ) }),
763
+ toolParts.map((toolPart) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
764
+ ToolResult,
765
+ {
766
+ toolName: toolPart.toolName,
767
+ toolCallId: toolPart.toolCallId,
768
+ output: toolPart.output,
769
+ toolInput: toolPart.input,
770
+ hasResult: toolPart.state === "output-available",
771
+ toolPartState: toolPart.state,
772
+ toolStateStore,
773
+ tools,
774
+ getOnAction,
775
+ onConfirm,
776
+ onReject,
777
+ onRetry
778
+ },
779
+ toolPart.toolCallId
780
+ ))
781
+ ] });
782
+ }
783
+
784
+ // src/components/Thread.tsx
785
+ var import_jsx_runtime5 = require("react/jsx-runtime");
786
+ function Thread({ className, emptyMessage, suggestions }) {
787
+ const { messages, isLoading, sendMessage, tools, toolStateStore, getOnAction, confirmTool, rejectTool, retryTool } = useChatContext();
788
+ const scrollRef = (0, import_react6.useRef)(null);
789
+ (0, import_react6.useEffect)(() => {
790
+ if (scrollRef.current) {
791
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
792
+ }
793
+ }, [messages, isLoading]);
794
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: scrollRef, className: `${className || ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-6 space-y-6", children: [
795
+ messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-center py-16", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-[var(--bui-fg-faint,#52525b)] text-sm", children: [
796
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] mb-4", children: emptyMessage || "Send a message to get started" }),
797
+ suggestions && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex flex-wrap justify-center gap-2 mt-4", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
798
+ "button",
799
+ {
800
+ onClick: () => sendMessage(suggestion),
801
+ className: "px-3 py-1.5 text-xs text-[var(--bui-fg-secondary,#a1a1aa)] bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-full hover:bg-[var(--bui-bg-hover,#3f3f46)] hover:text-[var(--bui-fg,#f4f4f5)] transition-colors",
802
+ children: suggestion
803
+ },
804
+ suggestion
805
+ )) })
806
+ ] }) }),
807
+ messages.map((message, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
808
+ Message,
809
+ {
810
+ message,
811
+ tools,
812
+ toolStateStore,
813
+ getOnAction,
814
+ onConfirm: confirmTool,
815
+ onReject: rejectTool,
816
+ onRetry: retryTool
817
+ },
818
+ `${message.id}-${i}`
819
+ )),
820
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-[var(--bui-fg-muted,#71717a)] text-sm", children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-1.5 h-1.5 bg-[var(--bui-fg-muted,#71717a)] rounded-full animate-pulse" }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Thinking" })
823
+ ] })
824
+ ] }) });
825
+ }
826
+
827
+ // src/components/Composer.tsx
828
+ var import_react7 = require("react");
829
+ var import_jsx_runtime6 = require("react/jsx-runtime");
830
+ function Composer({ className, placeholder = "Ask something..." }) {
831
+ const { sendMessage, isLoading } = useChatContext();
832
+ const [input, setInput] = (0, import_react7.useState)("");
833
+ const handleSubmit = (e) => {
834
+ e.preventDefault();
835
+ if (!input.trim() || isLoading) return;
836
+ sendMessage(input);
837
+ setInput("");
838
+ };
839
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className: `relative ${className || ""}`, children: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
841
+ "input",
842
+ {
843
+ type: "text",
844
+ value: input,
845
+ onChange: (e) => setInput(e.target.value),
846
+ placeholder,
847
+ className: "w-full bg-[var(--bui-bg-surface,#18181b)] border border-[var(--bui-border,#27272a)] rounded-xl px-4 py-3 pr-12 text-sm text-[var(--bui-fg,#f4f4f5)] placeholder-[var(--bui-fg-faint,#52525b)] focus:outline-none focus:border-[var(--bui-border-strong,#3f3f46)] transition-colors",
848
+ disabled: isLoading
849
+ }
850
+ ),
851
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
852
+ "button",
853
+ {
854
+ type: "submit",
855
+ disabled: isLoading || !input.trim(),
856
+ className: "absolute right-2 top-1/2 -translate-y-1/2 p-2 text-[var(--bui-fg-secondary,#a1a1aa)] hover:text-[var(--bui-fg,#f4f4f5)] disabled:opacity-30 disabled:cursor-not-allowed transition-colors",
857
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M14.5 1.5L7 9M14.5 1.5L10 14.5L7 9M14.5 1.5L1.5 6L7 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
858
+ }
859
+ )
860
+ ] });
861
+ }
862
+
863
+ // src/components/Chat.tsx
864
+ var import_jsx_runtime7 = require("react/jsx-runtime");
865
+ function Chat({
866
+ endpoint = "/api/chat",
867
+ tools,
868
+ className,
869
+ placeholder,
870
+ emptyMessage,
871
+ suggestions
872
+ }) {
873
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatProvider, { endpoint, tools, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `bg-[var(--bui-bg-surface,#18181b)] rounded-xl border border-[var(--bui-border,#27272a)] flex flex-col ${className || ""}`, children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
875
+ Thread,
876
+ {
877
+ className: "flex-1 overflow-y-auto",
878
+ emptyMessage,
879
+ suggestions
880
+ }
881
+ ),
882
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-4 pt-0", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Composer, { placeholder }) })
883
+ ] }) });
884
+ }
885
+
886
+ // src/components/Panel.tsx
887
+ var import_react8 = require("react");
888
+ var import_jsx_runtime8 = require("react/jsx-runtime");
889
+ function Panel({ toolStateStore, tools, getOnAction, className, tool: filterTool, excludeTools, maxItems = 5 }) {
890
+ const subscribe = (0, import_react8.useCallback)(
891
+ (listener) => toolStateStore.subscribeAll(listener),
892
+ [toolStateStore]
893
+ );
894
+ const getSnapshot = (0, import_react8.useCallback)(
895
+ () => toolStateStore.getSnapshot(),
896
+ [toolStateStore]
897
+ );
898
+ const snapshot = (0, import_react8.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
899
+ const entityLatest = /* @__PURE__ */ new Map();
900
+ const ungrouped = [];
901
+ for (const [toolCallId, entry] of snapshot) {
902
+ if (filterTool && entry.toolName !== filterTool) continue;
903
+ if (excludeTools && entry.toolName && excludeTools.includes(entry.toolName)) continue;
904
+ if (entry.output == null && !entry.loading) continue;
905
+ if (entry.entityId) {
906
+ const current = entityLatest.get(entry.entityId);
907
+ if (!current || (entry.seqNo ?? 0) > (current.entry.seqNo ?? 0)) {
908
+ entityLatest.set(entry.entityId, { toolCallId, entry });
909
+ }
910
+ } else {
911
+ ungrouped.push({ toolCallId, entry });
912
+ }
913
+ }
914
+ const items = [...entityLatest.values(), ...ungrouped].sort((a, b) => (b.entry.seqNo ?? 0) - (a.entry.seqNo ?? 0)).slice(0, maxItems);
915
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: `bg-[var(--bui-bg-surface,#18181b)] border border-[var(--bui-border,#27272a)] flex flex-col ${className || ""}`, children: items.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1 overflow-y-auto", children: items.map(({ toolCallId, entry }) => {
916
+ const toolName = entry.toolName;
917
+ const toolDef = toolName ? tools[toolName] : void 0;
918
+ if (!toolDef) return null;
919
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "border-b border-[var(--bui-border,#27272a)] last:border-b-0", children: [
920
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "px-4 py-3 border-b border-[var(--bui-border,#27272a)]/50 flex items-center gap-2", children: [
921
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-[var(--bui-success,#059669)]" }),
922
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-xs text-[var(--bui-fg-secondary,#a1a1aa)] uppercase tracking-wider", children: toolName })
923
+ ] }),
924
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
925
+ toolDef.View,
926
+ {
927
+ data: entry.output,
928
+ loading: entry.loading,
929
+ onAction: getOnAction(toolCallId, toolName),
930
+ error: entry.error ? new Error(entry.error) : null
931
+ }
932
+ ) })
933
+ ] }, toolCallId);
934
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex-1 flex flex-col items-center justify-center gap-4 p-6", children: [
935
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
936
+ "div",
937
+ {
938
+ className: "absolute inset-0 overflow-hidden rounded-xl pointer-events-none opacity-[0.03]",
939
+ style: {
940
+ backgroundImage: "radial-gradient(circle, currentColor 1px, transparent 1px)",
941
+ backgroundSize: "24px 24px"
942
+ }
943
+ }
944
+ ),
945
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative flex flex-col items-center gap-3 text-center", children: [
946
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-10 h-10 rounded-xl bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)]/50 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { className: "w-5 h-5 text-[var(--bui-fg-faint,#52525b)]", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9.53 16.122a3 3 0 0 0-5.78 1.128 2.25 2.25 0 0 1-2.4 2.245 4.5 4.5 0 0 0 8.4-2.245c0-.399-.078-.78-.22-1.128Zm0 0a15.998 15.998 0 0 0 3.388-1.62m-5.043-.025a15.994 15.994 0 0 1 1.622-3.395m3.42 3.42a15.995 15.995 0 0 0 4.764-4.648l3.876-5.814a1.151 1.151 0 0 0-1.597-1.597L14.146 6.32a15.996 15.996 0 0 0-4.649 4.763m3.42 3.42a6.776 6.776 0 0 0-3.42-3.42" }) }) }),
947
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[var(--bui-fg-muted,#71717a)] text-sm font-medium", children: "Canvas" }),
949
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs mt-1", children: "Tool results will render here as you chat" })
950
+ ] })
951
+ ] })
952
+ ] }) });
953
+ }
954
+ function ChatPanel({ className, tool, excludeTools, maxItems }) {
955
+ const { tools, toolStateStore, getOnAction } = useChatContext();
956
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
957
+ Panel,
958
+ {
959
+ toolStateStore,
960
+ tools,
961
+ getOnAction,
962
+ className,
963
+ tool,
964
+ excludeTools,
965
+ maxItems
966
+ }
967
+ );
968
+ }
969
+
970
+ // src/components/useToolEffect.ts
971
+ var import_react9 = require("react");
972
+ function useToolEffect(store, toolName, callback) {
973
+ const callbackRef = (0, import_react9.useRef)(callback);
974
+ callbackRef.current = callback;
975
+ const firedVersionsRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
976
+ (0, import_react9.useEffect)(() => {
977
+ return store.subscribeAll(() => {
978
+ const snapshot = store.getSnapshot();
979
+ for (const [toolCallId, entry] of snapshot) {
980
+ if (entry.toolName !== toolName) continue;
981
+ if (entry.loading) continue;
982
+ if (entry.error) continue;
983
+ if (entry.output == null) continue;
984
+ const lastFired = firedVersionsRef.current.get(toolCallId) ?? 0;
985
+ if (entry.version > lastFired) {
986
+ firedVersionsRef.current.set(toolCallId, entry.version);
987
+ callbackRef.current(entry, toolCallId);
988
+ }
989
+ }
990
+ });
991
+ }, [store, toolName]);
992
+ }
993
+
994
+ // src/components/useToolOutput.ts
995
+ var import_react10 = require("react");
996
+ function findBest(store, toolName) {
997
+ const snapshot = store.getSnapshot();
998
+ let best = null;
999
+ for (const [toolCallId, entry] of snapshot) {
1000
+ if (entry.toolName !== toolName) continue;
1001
+ if (entry.output == null && !entry.loading) continue;
1002
+ if (!best || entry.version >= best.entry.version) {
1003
+ best = { toolCallId, entry };
1004
+ }
1005
+ }
1006
+ return best;
1007
+ }
1008
+ function useToolOutput(store, toolName) {
1009
+ const [result, setResult] = (0, import_react10.useState)(() => {
1010
+ const best = findBest(store, toolName);
1011
+ if (!best) return { data: null, loading: false, error: null, toolCallId: null };
1012
+ return {
1013
+ data: best.entry.output,
1014
+ loading: best.entry.loading,
1015
+ error: best.entry.error,
1016
+ toolCallId: best.toolCallId
1017
+ };
1018
+ });
1019
+ (0, import_react10.useEffect)(() => {
1020
+ function update() {
1021
+ const best = findBest(store, toolName);
1022
+ setResult((prev) => {
1023
+ if (!best) {
1024
+ if (prev.data === null && !prev.loading && prev.error === null && prev.toolCallId === null) return prev;
1025
+ return { data: null, loading: false, error: null, toolCallId: null };
1026
+ }
1027
+ const next = {
1028
+ data: best.entry.output,
1029
+ loading: best.entry.loading,
1030
+ error: best.entry.error,
1031
+ toolCallId: best.toolCallId
1032
+ };
1033
+ if (prev.data === next.data && prev.loading === next.loading && prev.error === next.error && prev.toolCallId === next.toolCallId) {
1034
+ return prev;
1035
+ }
1036
+ return next;
1037
+ });
1038
+ }
1039
+ update();
1040
+ return store.subscribeAll(update);
1041
+ }, [store, toolName]);
1042
+ return result;
1043
+ }
1044
+
1045
+ // src/components/useChatToolOutput.ts
1046
+ function useChatToolOutput(toolName) {
1047
+ const { toolStateStore } = useChatContext();
1048
+ return useToolOutput(toolStateStore, toolName);
1049
+ }
1050
+
1051
+ // src/components/ThemeProvider.tsx
1052
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1053
+ function ThemeProvider({
1054
+ theme = "dark",
1055
+ variables,
1056
+ className,
1057
+ children
1058
+ }) {
1059
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-theme": theme, className, style: variables, children });
1060
+ }
1061
+
1062
+ // src/components/Question.tsx
1063
+ var import_react11 = require("react");
1064
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1065
+ function QuestionView({
1066
+ question,
1067
+ options,
1068
+ mode = "single",
1069
+ selected,
1070
+ onSelect,
1071
+ allowFreeform = false,
1072
+ loading = false,
1073
+ className
1074
+ }) {
1075
+ const [multiSelected, setMultiSelected] = (0, import_react11.useState)(
1076
+ () => new Set(Array.isArray(selected) ? selected : [])
1077
+ );
1078
+ const [freeformValue, setFreeformValue] = (0, import_react11.useState)("");
1079
+ const [showFreeform, setShowFreeform] = (0, import_react11.useState)(false);
1080
+ const isSelected = (value) => {
1081
+ if (mode === "multi") return multiSelected.has(value);
1082
+ return selected === value;
1083
+ };
1084
+ const handleSingleSelect = (value) => {
1085
+ if (loading) return;
1086
+ onSelect?.(value);
1087
+ };
1088
+ const handleMultiToggle = (value) => {
1089
+ if (loading) return;
1090
+ setMultiSelected((prev) => {
1091
+ const next = new Set(prev);
1092
+ if (next.has(value)) next.delete(value);
1093
+ else next.add(value);
1094
+ return next;
1095
+ });
1096
+ };
1097
+ const handleMultiSubmit = () => {
1098
+ if (loading || multiSelected.size === 0) return;
1099
+ onSelect?.(Array.from(multiSelected));
1100
+ };
1101
+ const handleFreeformSubmit = () => {
1102
+ if (loading || !freeformValue.trim()) return;
1103
+ onSelect?.(freeformValue.trim());
1104
+ };
1105
+ if (selected && !loading) {
1106
+ const display = Array.isArray(selected) ? selected.map((v) => options.find((o) => o.value === v)?.label || v).join(", ") : options.find((o) => o.value === selected)?.label || selected;
1107
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 ${className || ""}`, children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-xs uppercase tracking-wider mb-2", children: "Answered" }),
1109
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: question }),
1110
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mt-2 inline-flex items-center gap-1.5 px-3 py-1.5 bg-[var(--bui-success-muted,rgba(16,185,129,0.12))] border border-[var(--bui-success-border,rgba(4,120,87,0.3))] rounded-lg", children: [
1111
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-[var(--bui-success-fg,#6ee7b7)]" }),
1112
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-[var(--bui-success-fg,#6ee7b7)] text-sm", children: display })
1113
+ ] })
1114
+ ] });
1115
+ }
1116
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 transition-opacity ${loading ? "opacity-60" : ""} ${className || ""}`, children: [
1117
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-[var(--bui-fg,#f4f4f5)] text-sm font-medium mb-3", children: question }),
1118
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "space-y-2", children: options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1119
+ "button",
1120
+ {
1121
+ onClick: () => mode === "single" ? handleSingleSelect(opt.value) : handleMultiToggle(opt.value),
1122
+ disabled: loading,
1123
+ className: `w-full text-left px-3 py-2.5 rounded-lg border transition-colors ${isSelected(opt.value) ? "border-[var(--bui-primary-border,#2563eb80)] bg-[var(--bui-primary-muted,#1e3a5f)] text-[var(--bui-fg,#f4f4f5)]" : "border-[var(--bui-border-strong,#3f3f46)] bg-[var(--bui-bg-surface,#18181b)]/50 text-[var(--bui-fg-secondary,#a1a1aa)] hover:border-[var(--bui-border-strong,#3f3f46)] hover:bg-[var(--bui-bg-surface,#18181b)]"} disabled:opacity-50 disabled:cursor-not-allowed`,
1124
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-3", children: [
1125
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `shrink-0 w-4 h-4 rounded-${mode === "multi" ? "sm" : "full"} border ${isSelected(opt.value) ? "border-[var(--bui-primary,#2563eb)] bg-[var(--bui-primary-hover,#3b82f6)]" : "border-[var(--bui-border-strong,#3f3f46)]"} flex items-center justify-center`, children: isSelected(opt.value) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { className: "w-2.5 h-2.5 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }) }),
1126
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "min-w-0 flex-1", children: [
1127
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-sm", children: opt.label }),
1128
+ opt.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs mt-0.5", children: opt.description })
1129
+ ] })
1130
+ ] })
1131
+ },
1132
+ opt.value
1133
+ )) }),
1134
+ mode === "multi" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1135
+ "button",
1136
+ {
1137
+ onClick: handleMultiSubmit,
1138
+ disabled: loading || multiSelected.size === 0,
1139
+ className: "mt-3 px-4 py-2 bg-[var(--bui-primary,#2563eb)] text-white text-sm rounded-lg hover:bg-[var(--bui-primary-hover,#3b82f6)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
1140
+ children: [
1141
+ "Confirm (",
1142
+ multiSelected.size,
1143
+ " selected)"
1144
+ ]
1145
+ }
1146
+ ),
1147
+ allowFreeform && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-3 pt-3 border-t border-[var(--bui-border-strong,#3f3f46)]", children: !showFreeform ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1148
+ "button",
1149
+ {
1150
+ onClick: () => setShowFreeform(true),
1151
+ className: "text-[var(--bui-fg-muted,#71717a)] text-xs hover:text-[var(--bui-fg-secondary,#a1a1aa)] transition-colors",
1152
+ children: "Or type your own answer..."
1153
+ }
1154
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex gap-2", children: [
1155
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1156
+ "input",
1157
+ {
1158
+ type: "text",
1159
+ value: freeformValue,
1160
+ onChange: (e) => setFreeformValue(e.target.value),
1161
+ onKeyDown: (e) => e.key === "Enter" && handleFreeformSubmit(),
1162
+ placeholder: "Type your answer...",
1163
+ className: "flex-1 bg-[var(--bui-bg-surface,#18181b)] border border-[var(--bui-border-strong,#3f3f46)] rounded-lg px-3 py-2 text-sm text-[var(--bui-fg,#f4f4f5)] placeholder-[var(--bui-fg-faint,#52525b)] focus:outline-none focus:border-[var(--bui-border-strong,#3f3f46)]"
1164
+ }
1165
+ ),
1166
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1167
+ "button",
1168
+ {
1169
+ onClick: handleFreeformSubmit,
1170
+ disabled: loading || !freeformValue.trim(),
1171
+ className: "px-3 py-2 bg-[var(--bui-primary,#2563eb)] text-white text-sm rounded-lg hover:bg-[var(--bui-primary-hover,#3b82f6)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
1172
+ children: "Send"
1173
+ }
1174
+ )
1175
+ ] }) })
1176
+ ] });
1177
+ }
1178
+
1179
+ // src/components/Form.tsx
1180
+ var import_react12 = require("react");
1181
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1182
+ function FormView({
1183
+ title,
1184
+ description,
1185
+ fields,
1186
+ values: initialValues,
1187
+ submitted = false,
1188
+ submitLabel = "Submit",
1189
+ onSubmit,
1190
+ loading = false,
1191
+ className
1192
+ }) {
1193
+ const [values, setValues] = (0, import_react12.useState)(() => {
1194
+ const v = {};
1195
+ for (const f of fields) {
1196
+ v[f.name] = initialValues?.[f.name] ?? f.defaultValue ?? "";
1197
+ }
1198
+ return v;
1199
+ });
1200
+ const [errors, setErrors] = (0, import_react12.useState)({});
1201
+ const handleChange = (name, value) => {
1202
+ setValues((prev) => ({ ...prev, [name]: value }));
1203
+ if (errors[name]) {
1204
+ setErrors((prev) => {
1205
+ const next = { ...prev };
1206
+ delete next[name];
1207
+ return next;
1208
+ });
1209
+ }
1210
+ };
1211
+ const handleSubmit = (e) => {
1212
+ e.preventDefault();
1213
+ if (loading) return;
1214
+ const newErrors = {};
1215
+ for (const f of fields) {
1216
+ if (f.required && !values[f.name]?.trim()) {
1217
+ newErrors[f.name] = `${f.label} is required`;
1218
+ }
1219
+ }
1220
+ if (Object.keys(newErrors).length > 0) {
1221
+ setErrors(newErrors);
1222
+ return;
1223
+ }
1224
+ onSubmit?.(values);
1225
+ };
1226
+ if (submitted && initialValues) {
1227
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 ${className || ""}`, children: [
1228
+ title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-xs uppercase tracking-wider mb-2", children: "Submitted" }),
1229
+ title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium mb-3", children: title }),
1230
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-2", children: fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-baseline gap-2", children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs min-w-[80px]", children: [
1232
+ f.label,
1233
+ ":"
1234
+ ] }),
1235
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: initialValues[f.name] || "-" })
1236
+ ] }, f.name)) }),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mt-3 inline-flex items-center gap-1.5 px-2 py-1 bg-[var(--bui-success-muted,rgba(16,185,129,0.12))] rounded text-xs text-[var(--bui-success-fg,#6ee7b7)]", children: [
1238
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-[var(--bui-success-fg,#6ee7b7)]" }),
1239
+ "Submitted"
1240
+ ] })
1241
+ ] });
1242
+ }
1243
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1244
+ "form",
1245
+ {
1246
+ onSubmit: handleSubmit,
1247
+ className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 transition-opacity ${loading ? "opacity-60" : ""} ${className || ""}`,
1248
+ children: [
1249
+ title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-[var(--bui-fg,#f4f4f5)] text-sm font-medium mb-1", children: title }),
1250
+ description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs mb-4", children: description }),
1251
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-3", children: fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
1252
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: "block text-[var(--bui-fg-secondary,#a1a1aa)] text-xs mb-1", children: [
1253
+ field.label,
1254
+ field.required && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[var(--bui-error-fg,#f87171)] ml-0.5", children: "*" })
1255
+ ] }),
1256
+ field.type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1257
+ "textarea",
1258
+ {
1259
+ value: values[field.name] || "",
1260
+ onChange: (e) => handleChange(field.name, e.target.value),
1261
+ placeholder: field.placeholder,
1262
+ disabled: loading,
1263
+ rows: 3,
1264
+ className: `w-full bg-[var(--bui-bg-surface,#18181b)] border rounded-lg px-3 py-2 text-sm text-[var(--bui-fg,#f4f4f5)] placeholder-[var(--bui-fg-faint,#52525b)] focus:outline-none focus:border-[var(--bui-border-strong,#3f3f46)] resize-none ${errors[field.name] ? "border-[var(--bui-error-border,rgba(153,27,27,0.5))]" : "border-[var(--bui-border-strong,#3f3f46)]"}`
1265
+ }
1266
+ ) : field.type === "select" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1267
+ "select",
1268
+ {
1269
+ value: values[field.name] || "",
1270
+ onChange: (e) => handleChange(field.name, e.target.value),
1271
+ disabled: loading,
1272
+ className: `w-full bg-[var(--bui-bg-surface,#18181b)] border rounded-lg px-3 py-2 text-sm text-[var(--bui-fg,#f4f4f5)] focus:outline-none focus:border-[var(--bui-border-strong,#3f3f46)] ${errors[field.name] ? "border-[var(--bui-error-border,rgba(153,27,27,0.5))]" : "border-[var(--bui-border-strong,#3f3f46)]"}`,
1273
+ children: [
1274
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "", children: field.placeholder || "Select..." }),
1275
+ field.options?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: opt, children: opt }, opt))
1276
+ ]
1277
+ }
1278
+ ) : field.type === "toggle" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1279
+ "button",
1280
+ {
1281
+ type: "button",
1282
+ onClick: () => handleChange(field.name, values[field.name] === "true" ? "false" : "true"),
1283
+ disabled: loading,
1284
+ className: "flex items-center gap-2",
1285
+ children: [
1286
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: `w-8 h-4.5 rounded-full transition-colors ${values[field.name] === "true" ? "bg-[var(--bui-primary-hover,#3b82f6)]" : "bg-[var(--bui-bg-hover,#3f3f46)]"}`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: `w-3.5 h-3.5 rounded-full bg-white mt-0.5 transition-transform ${values[field.name] === "true" ? "translate-x-4" : "translate-x-0.5"}` }) }),
1287
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-xs", children: values[field.name] === "true" ? "On" : "Off" })
1288
+ ]
1289
+ }
1290
+ ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1291
+ "input",
1292
+ {
1293
+ type: field.type || "text",
1294
+ value: values[field.name] || "",
1295
+ onChange: (e) => handleChange(field.name, e.target.value),
1296
+ placeholder: field.placeholder,
1297
+ disabled: loading,
1298
+ className: `w-full bg-[var(--bui-bg-surface,#18181b)] border rounded-lg px-3 py-2 text-sm text-[var(--bui-fg,#f4f4f5)] placeholder-[var(--bui-fg-faint,#52525b)] focus:outline-none focus:border-[var(--bui-border-strong,#3f3f46)] ${errors[field.name] ? "border-[var(--bui-error-border,rgba(153,27,27,0.5))]" : "border-[var(--bui-border-strong,#3f3f46)]"}`
1299
+ }
1300
+ ),
1301
+ errors[field.name] && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-[var(--bui-error-fg,#f87171)] text-xs mt-1", children: errors[field.name] }),
1302
+ field.hint && !errors[field.name] && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs mt-1", children: field.hint })
1303
+ ] }, field.name)) }),
1304
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1305
+ "button",
1306
+ {
1307
+ type: "submit",
1308
+ disabled: loading,
1309
+ className: "mt-4 px-4 py-2 bg-[var(--bui-primary,#2563eb)] text-white text-sm rounded-lg hover:bg-[var(--bui-primary-hover,#3b82f6)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
1310
+ children: loading ? "Submitting..." : submitLabel
1311
+ }
1312
+ )
1313
+ ]
1314
+ }
1315
+ );
1316
+ }
1317
+
1318
+ // src/components/DataTable.tsx
1319
+ var import_react13 = require("react");
1320
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1321
+ function DataTableView({
1322
+ columns,
1323
+ rows,
1324
+ title,
1325
+ caption,
1326
+ pageSize = 0,
1327
+ loading = false,
1328
+ className
1329
+ }) {
1330
+ const [sortKey, setSortKey] = (0, import_react13.useState)(null);
1331
+ const [sortDir, setSortDir] = (0, import_react13.useState)("asc");
1332
+ const [page, setPage] = (0, import_react13.useState)(0);
1333
+ const handleSort = (key) => {
1334
+ if (sortKey === key) {
1335
+ setSortDir((d) => d === "asc" ? "desc" : "asc");
1336
+ } else {
1337
+ setSortKey(key);
1338
+ setSortDir("asc");
1339
+ }
1340
+ };
1341
+ const sortedRows = (0, import_react13.useMemo)(() => {
1342
+ if (!sortKey) return rows;
1343
+ return [...rows].sort((a, b) => {
1344
+ const av = a[sortKey];
1345
+ const bv = b[sortKey];
1346
+ if (av == null && bv == null) return 0;
1347
+ if (av == null) return 1;
1348
+ if (bv == null) return -1;
1349
+ const cmp = typeof av === "number" && typeof bv === "number" ? av - bv : String(av).localeCompare(String(bv));
1350
+ return sortDir === "asc" ? cmp : -cmp;
1351
+ });
1352
+ }, [rows, sortKey, sortDir]);
1353
+ const totalPages = pageSize > 0 ? Math.ceil(sortedRows.length / pageSize) : 1;
1354
+ const displayRows = pageSize > 0 ? sortedRows.slice(page * pageSize, (page + 1) * pageSize) : sortedRows;
1355
+ const formatCell = (col, value) => {
1356
+ if (col.format) return col.format(value);
1357
+ if (value == null) return "-";
1358
+ if (typeof value === "number") return value.toLocaleString();
1359
+ return String(value);
1360
+ };
1361
+ const alignClass = (align) => {
1362
+ if (align === "right") return "text-right";
1363
+ if (align === "center") return "text-center";
1364
+ return "text-left";
1365
+ };
1366
+ if (loading && rows.length === 0) {
1367
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 ${className || ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: [
1368
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-1.5 h-1.5 bg-[var(--bui-fg-muted,#71717a)] rounded-full animate-pulse" }),
1369
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Loading data..." })
1370
+ ] }) });
1371
+ }
1372
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl overflow-hidden transition-opacity ${loading ? "opacity-60" : ""} ${className || ""}`, children: [
1373
+ title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "px-4 py-3 border-b border-[var(--bui-border-strong,#3f3f46)]", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between", children: [
1374
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium", children: title }),
1375
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs font-mono", children: [
1376
+ rows.length,
1377
+ " rows"
1378
+ ] })
1379
+ ] }) }),
1380
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("table", { className: "w-full text-sm", children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tr", { className: "border-b border-[var(--bui-border-strong,#3f3f46)]", children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1382
+ "th",
1383
+ {
1384
+ className: `px-4 py-2.5 text-[var(--bui-fg-muted,#71717a)] text-xs font-medium uppercase tracking-wider ${alignClass(col.align)} ${col.sortable ? "cursor-pointer hover:text-[var(--bui-fg-secondary,#a1a1aa)] select-none" : ""}`,
1385
+ onClick: col.sortable ? () => handleSort(col.key) : void 0,
1386
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
1387
+ col.label,
1388
+ col.sortable && sortKey === col.key && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)]", children: sortDir === "asc" ? "\u2191" : "\u2193" })
1389
+ ] })
1390
+ },
1391
+ col.key
1392
+ )) }) }),
1393
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("tbody", { className: "divide-y divide-[var(--bui-border-strong,#3f3f46)]/50", children: [
1394
+ displayRows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tr", { className: "hover:bg-[var(--bui-bg-hover,#3f3f46)]/20 transition-colors", children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { className: `px-4 py-2.5 text-[var(--bui-fg-secondary,#a1a1aa)] ${alignClass(col.align)}`, children: formatCell(col, row[col.key]) }, col.key)) }, i)),
1395
+ displayRows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("td", { colSpan: columns.length, className: "px-4 py-6 text-center text-[var(--bui-fg-muted,#71717a)] text-sm", children: "No data" }) })
1396
+ ] })
1397
+ ] }) }),
1398
+ totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "px-4 py-2 border-t border-[var(--bui-border-strong,#3f3f46)] flex items-center justify-between", children: [
1399
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs", children: [
1400
+ "Page ",
1401
+ page + 1,
1402
+ " of ",
1403
+ totalPages
1404
+ ] }),
1405
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-1", children: [
1406
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1407
+ "button",
1408
+ {
1409
+ onClick: () => setPage((p) => Math.max(0, p - 1)),
1410
+ disabled: page === 0,
1411
+ className: "px-2 py-1 text-xs text-[var(--bui-fg-secondary,#a1a1aa)] hover:text-[var(--bui-fg,#f4f4f5)] disabled:opacity-30 disabled:cursor-not-allowed",
1412
+ children: "Prev"
1413
+ }
1414
+ ),
1415
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1416
+ "button",
1417
+ {
1418
+ onClick: () => setPage((p) => Math.min(totalPages - 1, p + 1)),
1419
+ disabled: page >= totalPages - 1,
1420
+ className: "px-2 py-1 text-xs text-[var(--bui-fg-secondary,#a1a1aa)] hover:text-[var(--bui-fg,#f4f4f5)] disabled:opacity-30 disabled:cursor-not-allowed",
1421
+ children: "Next"
1422
+ }
1423
+ )
1424
+ ] })
1425
+ ] }),
1426
+ caption && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "px-4 py-2 border-t border-[var(--bui-border-strong,#3f3f46)]", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs", children: caption }) })
1427
+ ] });
1428
+ }
1429
+
1430
+ // src/components/Progress.tsx
1431
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1432
+ function ProgressView({
1433
+ title,
1434
+ steps,
1435
+ percent,
1436
+ label,
1437
+ loading = false,
1438
+ className
1439
+ }) {
1440
+ if (percent != null && !steps) {
1441
+ const clamped = Math.max(0, Math.min(100, percent));
1442
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 ${className || ""}`, children: [
1443
+ title && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium mb-3", children: title }),
1444
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
1445
+ label && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-xs", children: label }),
1446
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-xs font-mono ml-auto", children: [
1447
+ clamped,
1448
+ "%"
1449
+ ] })
1450
+ ] }),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "h-2 bg-[var(--bui-bg-hover,#3f3f46)] rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1452
+ "div",
1453
+ {
1454
+ className: `h-full rounded-full transition-all duration-500 ${clamped >= 100 ? "bg-[var(--bui-success,#059669)]" : "bg-[var(--bui-primary-hover,#3b82f6)]"}`,
1455
+ style: { width: `${clamped}%` }
1456
+ }
1457
+ ) })
1458
+ ] });
1459
+ }
1460
+ if (!steps || steps.length === 0) return null;
1461
+ const done = steps.filter((s) => s.status === "done").length;
1462
+ const pct = steps.length > 0 ? done / steps.length * 100 : 0;
1463
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl overflow-hidden transition-opacity ${loading ? "opacity-70" : ""} ${className || ""}`, children: [
1464
+ title && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "px-4 py-3 border-b border-[var(--bui-border-strong,#3f3f46)]", children: [
1465
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between", children: [
1466
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium", children: title }),
1467
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs font-mono", children: [
1468
+ done,
1469
+ "/",
1470
+ steps.length
1471
+ ] })
1472
+ ] }),
1473
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-2 h-1 bg-[var(--bui-bg-hover,#3f3f46)] rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1474
+ "div",
1475
+ {
1476
+ className: "h-full bg-[var(--bui-success,#059669)] rounded-full transition-all duration-500",
1477
+ style: { width: `${pct}%` }
1478
+ }
1479
+ ) })
1480
+ ] }),
1481
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "divide-y divide-[var(--bui-border-strong,#3f3f46)]/50", children: steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "px-4 py-2.5 flex items-start gap-3", children: [
1482
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mt-0.5 shrink-0", children: [
1483
+ step.status === "pending" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-4 h-4 rounded-full border border-[var(--bui-border-strong,#3f3f46)]" }),
1484
+ step.status === "active" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-4 h-4 rounded-full bg-[var(--bui-primary-muted,#1e3a5f)] flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-2 h-2 bg-[var(--bui-primary-hover,#3b82f6)] rounded-full animate-pulse" }) }),
1485
+ step.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-[var(--bui-success-fg,#6ee7b7)]", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }),
1486
+ step.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("svg", { className: "w-4 h-4 text-[var(--bui-error-fg,#f87171)]", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) })
1487
+ ] }),
1488
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "min-w-0 flex-1", children: [
1489
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: `text-sm ${step.status === "done" ? "text-[var(--bui-fg-secondary,#a1a1aa)] line-through" : step.status === "error" ? "text-[var(--bui-error-fg,#f87171)]" : step.status === "active" ? "text-[var(--bui-fg,#f4f4f5)]" : "text-[var(--bui-fg-secondary,#a1a1aa)]"}`, children: step.label }),
1490
+ step.detail && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: `text-xs mt-0.5 ${step.status === "error" ? "text-[var(--bui-error-fg,#f87171)]" : "text-[var(--bui-fg-muted,#71717a)]"}`, children: step.detail })
1491
+ ] })
1492
+ ] }, i)) })
1493
+ ] });
1494
+ }
1495
+
1496
+ // src/components/MediaDisplay.tsx
1497
+ var import_react14 = require("react");
1498
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1499
+ function MediaDisplayView({
1500
+ items,
1501
+ layout = "grid",
1502
+ title,
1503
+ loading = false,
1504
+ className
1505
+ }) {
1506
+ const [lightboxIndex, setLightboxIndex] = (0, import_react14.useState)(null);
1507
+ if (loading && items.length === 0) {
1508
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 ${className || ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-2 text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: [
1509
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-1.5 h-1.5 bg-[var(--bui-fg-muted,#71717a)] rounded-full animate-pulse" }),
1510
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Loading media..." })
1511
+ ] }) });
1512
+ }
1513
+ if (items.length === 0) return null;
1514
+ const gridCols = items.length === 1 ? "grid-cols-1" : items.length === 2 ? "grid-cols-2" : items.length <= 4 ? "grid-cols-2" : "grid-cols-3";
1515
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl overflow-hidden transition-opacity ${loading ? "opacity-60" : ""} ${className || ""}`, children: [
1516
+ title && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "px-4 py-3 border-b border-[var(--bui-border-strong,#3f3f46)]", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between", children: [
1517
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium", children: title }),
1518
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs", children: [
1519
+ items.length,
1520
+ " item",
1521
+ items.length !== 1 ? "s" : ""
1522
+ ] })
1523
+ ] }) }),
1524
+ layout === "grid" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `grid ${gridCols} gap-1 p-1`, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1525
+ MediaItemRenderer,
1526
+ {
1527
+ item,
1528
+ onImageClick: () => setLightboxIndex(i),
1529
+ compact: items.length > 1
1530
+ },
1531
+ i
1532
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "space-y-3 p-4", children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1533
+ MediaItemRenderer,
1534
+ {
1535
+ item,
1536
+ onImageClick: () => setLightboxIndex(i),
1537
+ compact: false
1538
+ },
1539
+ i
1540
+ )) }),
1541
+ lightboxIndex != null && items[lightboxIndex]?.type === "image" && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1542
+ "div",
1543
+ {
1544
+ className: "fixed inset-0 z-50 bg-black/90 flex items-center justify-center p-8",
1545
+ onClick: () => setLightboxIndex(null),
1546
+ children: [
1547
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1548
+ "button",
1549
+ {
1550
+ onClick: () => setLightboxIndex(null),
1551
+ className: "absolute top-4 right-4 text-white/60 hover:text-white text-2xl",
1552
+ children: "\xD7"
1553
+ }
1554
+ ),
1555
+ items.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1556
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1557
+ "button",
1558
+ {
1559
+ onClick: (e) => {
1560
+ e.stopPropagation();
1561
+ setLightboxIndex(Math.max(0, lightboxIndex - 1));
1562
+ },
1563
+ disabled: lightboxIndex === 0,
1564
+ className: "absolute left-4 text-white/60 hover:text-white text-3xl disabled:opacity-20",
1565
+ children: "\u2039"
1566
+ }
1567
+ ),
1568
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1569
+ "button",
1570
+ {
1571
+ onClick: (e) => {
1572
+ e.stopPropagation();
1573
+ setLightboxIndex(Math.min(items.length - 1, lightboxIndex + 1));
1574
+ },
1575
+ disabled: lightboxIndex >= items.length - 1,
1576
+ className: "absolute right-4 text-white/60 hover:text-white text-3xl disabled:opacity-20",
1577
+ children: "\u203A"
1578
+ }
1579
+ )
1580
+ ] }),
1581
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1582
+ "img",
1583
+ {
1584
+ src: items[lightboxIndex].url,
1585
+ alt: items[lightboxIndex].alt || "",
1586
+ className: "max-w-full max-h-full object-contain rounded",
1587
+ onClick: (e) => e.stopPropagation()
1588
+ }
1589
+ ),
1590
+ items[lightboxIndex].caption && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "absolute bottom-6 text-white/70 text-sm text-center", children: items[lightboxIndex].caption })
1591
+ ]
1592
+ }
1593
+ )
1594
+ ] });
1595
+ }
1596
+ function MediaItemRenderer({
1597
+ item,
1598
+ onImageClick,
1599
+ compact
1600
+ }) {
1601
+ if (item.type === "image") {
1602
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative group", children: [
1603
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1604
+ "img",
1605
+ {
1606
+ src: item.url,
1607
+ alt: item.alt || "",
1608
+ className: `w-full object-cover rounded cursor-pointer hover:opacity-90 transition-opacity ${compact ? "h-32" : "h-auto max-h-80"}`,
1609
+ onClick: onImageClick
1610
+ }
1611
+ ),
1612
+ item.caption && !compact && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs mt-1", children: item.caption })
1613
+ ] });
1614
+ }
1615
+ if (item.type === "video") {
1616
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
1617
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1618
+ "video",
1619
+ {
1620
+ src: item.url,
1621
+ controls: true,
1622
+ className: `w-full rounded bg-black ${compact ? "max-h-32" : "max-h-80"}`,
1623
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("track", { kind: "captions" })
1624
+ }
1625
+ ),
1626
+ item.caption && !compact && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs mt-1", children: item.caption })
1627
+ ] });
1628
+ }
1629
+ if (item.type === "audio") {
1630
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "bg-[var(--bui-bg-surface,#18181b)] rounded-lg p-3", children: [
1631
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("audio", { src: item.url, controls: true, className: "w-full" }),
1632
+ item.caption && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[var(--bui-fg-muted,#71717a)] text-xs mt-2", children: item.caption })
1633
+ ] });
1634
+ }
1635
+ return null;
1636
+ }
1637
+
1638
+ // src/components/CodeBlock.tsx
1639
+ var import_react15 = require("react");
1640
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1641
+ function CodeBlockView({
1642
+ code,
1643
+ language,
1644
+ title,
1645
+ showLineNumbers = false,
1646
+ diff,
1647
+ loading = false,
1648
+ className
1649
+ }) {
1650
+ const [copied, setCopied] = (0, import_react15.useState)(false);
1651
+ const [diffView, setDiffView] = (0, import_react15.useState)("diff");
1652
+ const handleCopy = async (text) => {
1653
+ try {
1654
+ await navigator.clipboard.writeText(text);
1655
+ setCopied(true);
1656
+ setTimeout(() => setCopied(false), 2e3);
1657
+ } catch {
1658
+ }
1659
+ };
1660
+ if (loading) {
1661
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 ${className || ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: [
1662
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-1.5 h-1.5 bg-[var(--bui-fg-muted,#71717a)] rounded-full animate-pulse" }),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Generating code..." })
1664
+ ] }) });
1665
+ }
1666
+ if (diff) {
1667
+ const beforeLines = diff.before.split("\n");
1668
+ const afterLines = diff.after.split("\n");
1669
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl overflow-hidden ${className || ""}`, children: [
1670
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "px-4 py-2.5 border-b border-[var(--bui-border-strong,#3f3f46)] flex items-center justify-between", children: [
1671
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", children: [
1672
+ title && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium", children: title }),
1673
+ language && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs font-mono", children: language })
1674
+ ] }),
1675
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center gap-1", children: ["before", "diff", "after"].map((mode) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1676
+ "button",
1677
+ {
1678
+ onClick: () => setDiffView(mode),
1679
+ className: `px-2 py-0.5 text-xs rounded ${diffView === mode ? "bg-[var(--bui-bg-hover,#3f3f46)] text-[var(--bui-fg,#f4f4f5)]" : "text-[var(--bui-fg-muted,#71717a)] hover:text-[var(--bui-fg-secondary,#a1a1aa)]"}`,
1680
+ children: mode
1681
+ },
1682
+ mode
1683
+ )) })
1684
+ ] }),
1685
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("pre", { className: "p-4 overflow-x-auto text-sm leading-relaxed", children: [
1686
+ diffView === "before" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: "text-[var(--bui-fg-secondary,#a1a1aa)]", children: diff.before }),
1687
+ diffView === "after" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: "text-[var(--bui-fg-secondary,#a1a1aa)]", children: diff.after }),
1688
+ diffView === "diff" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { children: renderSimpleDiff(beforeLines, afterLines) })
1689
+ ] })
1690
+ ] });
1691
+ }
1692
+ const lines = code.split("\n");
1693
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl overflow-hidden ${className || ""}`, children: [
1694
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "px-4 py-2.5 border-b border-[var(--bui-border-strong,#3f3f46)] flex items-center justify-between", children: [
1695
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", children: [
1696
+ title && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium", children: title }),
1697
+ language && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs font-mono", children: language }),
1698
+ !title && !language && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs", children: "Code" })
1699
+ ] }),
1700
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1701
+ "button",
1702
+ {
1703
+ onClick: () => handleCopy(code),
1704
+ className: "flex items-center gap-1.5 px-2 py-1 text-xs text-[var(--bui-fg-muted,#71717a)] hover:text-[var(--bui-fg-secondary,#a1a1aa)] transition-colors rounded hover:bg-[var(--bui-bg-hover,#3f3f46)]/50",
1705
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1706
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { className: "w-3.5 h-3.5 text-[var(--bui-success-fg,#6ee7b7)]", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) }),
1707
+ "Copied"
1708
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
1709
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("svg", { className: "w-3.5 h-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: [
1710
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
1711
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
1712
+ ] }),
1713
+ "Copy"
1714
+ ] })
1715
+ }
1716
+ )
1717
+ ] }),
1718
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("pre", { className: "p-4 overflow-x-auto text-sm leading-relaxed", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: "text-[var(--bui-fg-secondary,#a1a1aa)]", children: showLineNumbers ? lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex", children: [
1719
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-fg-faint,#52525b)] select-none w-8 shrink-0 text-right mr-4 font-mono text-xs leading-relaxed", children: i + 1 }),
1720
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: line })
1721
+ ] }, i)) : code }) })
1722
+ ] });
1723
+ }
1724
+ function renderSimpleDiff(beforeLines, afterLines) {
1725
+ const result = [];
1726
+ const beforeSet = new Set(beforeLines);
1727
+ const afterSet = new Set(afterLines);
1728
+ for (const line of beforeLines) {
1729
+ if (!afterSet.has(line)) {
1730
+ result.push(
1731
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] text-[var(--bui-error-fg,#f87171)]", children: [
1732
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-error-fg,#f87171)] select-none mr-2", children: "-" }),
1733
+ line
1734
+ ] }, `-${result.length}`)
1735
+ );
1736
+ }
1737
+ }
1738
+ for (const line of afterLines) {
1739
+ if (!beforeSet.has(line)) {
1740
+ result.push(
1741
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "bg-[var(--bui-success-muted,rgba(16,185,129,0.12))] text-[var(--bui-success-fg,#6ee7b7)]", children: [
1742
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[var(--bui-success,#059669)] select-none mr-2", children: "+" }),
1743
+ line
1744
+ ] }, `+${result.length}`)
1745
+ );
1746
+ }
1747
+ }
1748
+ for (const line of afterLines) {
1749
+ if (beforeSet.has(line)) {
1750
+ result.push(
1751
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "text-[var(--bui-fg-muted,#71717a)]", children: [
1752
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "select-none mr-2", children: "\xA0" }),
1753
+ line
1754
+ ] }, `=${result.length}`)
1755
+ );
1756
+ }
1757
+ }
1758
+ return result;
1759
+ }
1760
+
1761
+ // src/components/Toast.tsx
1762
+ var import_react16 = require("react");
1763
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1764
+ var ToastContext = (0, import_react16.createContext)(null);
1765
+ function useToast() {
1766
+ const ctx = (0, import_react16.useContext)(ToastContext);
1767
+ if (!ctx) throw new Error("useToast must be used within a <ToastProvider>");
1768
+ return ctx;
1769
+ }
1770
+ function ToastProvider({ children, className }) {
1771
+ const [toasts, setToasts] = (0, import_react16.useState)([]);
1772
+ const counterRef = (0, import_react16.useRef)(0);
1773
+ const toast = (0, import_react16.useCallback)((message, type = "info", duration = 4e3) => {
1774
+ const id = `toast-${++counterRef.current}`;
1775
+ setToasts((prev) => [...prev, { id, message, type, duration }]);
1776
+ }, []);
1777
+ const dismiss = (0, import_react16.useCallback)((id) => {
1778
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1779
+ }, []);
1780
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(ToastContext.Provider, { value: { toast, dismiss }, children: [
1781
+ children,
1782
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToastContainer, { toasts, onDismiss: dismiss, className })
1783
+ ] });
1784
+ }
1785
+ function ToastContainer({
1786
+ toasts,
1787
+ onDismiss,
1788
+ className
1789
+ }) {
1790
+ if (toasts.length === 0) return null;
1791
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: `fixed bottom-4 right-4 z-50 flex flex-col gap-2 max-w-sm ${className || ""}`, children: toasts.map((t) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToastItem, { toast: t, onDismiss }, t.id)) });
1792
+ }
1793
+ function ToastItem({ toast: t, onDismiss }) {
1794
+ (0, import_react16.useEffect)(() => {
1795
+ if (t.duration && t.duration > 0) {
1796
+ const timer = setTimeout(() => onDismiss(t.id), t.duration);
1797
+ return () => clearTimeout(timer);
1798
+ }
1799
+ }, [t.id, t.duration, onDismiss]);
1800
+ const colors = {
1801
+ success: "border-[var(--bui-success-border,rgba(4,120,87,0.3))] bg-[var(--bui-success-muted,rgba(16,185,129,0.12))]",
1802
+ error: "border-[var(--bui-error-border,rgba(153,27,27,0.5))] bg-[var(--bui-error-muted,rgba(220,38,38,0.08))]",
1803
+ warning: "border-[var(--bui-warning-border,rgba(180,83,9,0.5))] bg-[var(--bui-warning-muted,rgba(245,158,11,0.12))]",
1804
+ info: "border-[var(--bui-primary-border,#2563eb80)] bg-[var(--bui-primary-muted,#1e3a5f)]"
1805
+ };
1806
+ const dotColors = {
1807
+ success: "bg-[var(--bui-success-fg,#6ee7b7)]",
1808
+ error: "bg-[var(--bui-error-fg,#f87171)]",
1809
+ warning: "bg-[var(--bui-warning-fg,#f59e0b)]",
1810
+ info: "bg-[var(--bui-primary-hover,#3b82f6)]"
1811
+ };
1812
+ const textColors = {
1813
+ success: "text-[var(--bui-success-fg,#6ee7b7)]",
1814
+ error: "text-[var(--bui-error-fg,#f87171)]",
1815
+ warning: "text-[var(--bui-warning-fg,#f59e0b)]",
1816
+ info: "text-[var(--bui-primary-hover,#3b82f6)]"
1817
+ };
1818
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1819
+ "div",
1820
+ {
1821
+ className: `border rounded-lg px-4 py-3 bg-[var(--bui-bg-surface,#18181b)] shadow-lg animate-in slide-in-from-right ${colors[t.type]}`,
1822
+ style: { animation: "slideIn 0.2s ease-out" },
1823
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-2", children: [
1824
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: `w-1.5 h-1.5 rounded-full shrink-0 ${dotColors[t.type]}` }),
1825
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: `text-sm flex-1 ${textColors[t.type]}`, children: t.message }),
1826
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1827
+ "button",
1828
+ {
1829
+ onClick: () => onDismiss(t.id),
1830
+ className: "text-[var(--bui-fg-muted,#71717a)] hover:text-[var(--bui-fg-secondary,#a1a1aa)] text-xs shrink-0 ml-2",
1831
+ children: "\xD7"
1832
+ }
1833
+ )
1834
+ ] })
1835
+ }
1836
+ );
1837
+ }
1838
+
1839
+ // src/components/FileUpload.tsx
1840
+ var import_react17 = require("react");
1841
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1842
+ function FileUploadView({
1843
+ accept,
1844
+ maxSize,
1845
+ multiple = false,
1846
+ files = [],
1847
+ onUpload,
1848
+ title,
1849
+ loading = false,
1850
+ className
1851
+ }) {
1852
+ const [isDragging, setIsDragging] = (0, import_react17.useState)(false);
1853
+ const [error, setError] = (0, import_react17.useState)(null);
1854
+ const inputRef = (0, import_react17.useRef)(null);
1855
+ const validateFiles = (0, import_react17.useCallback)((fileList) => {
1856
+ setError(null);
1857
+ const valid = [];
1858
+ for (const file of fileList) {
1859
+ if (maxSize && file.size > maxSize) {
1860
+ setError(`${file.name} exceeds max size (${formatBytes(maxSize)})`);
1861
+ continue;
1862
+ }
1863
+ valid.push(file);
1864
+ }
1865
+ if (!multiple && valid.length > 1) {
1866
+ return [valid[0]];
1867
+ }
1868
+ return valid;
1869
+ }, [maxSize, multiple]);
1870
+ const handleFiles = (0, import_react17.useCallback)((fileList) => {
1871
+ if (!fileList || fileList.length === 0) return;
1872
+ const valid = validateFiles(Array.from(fileList));
1873
+ if (valid.length > 0) {
1874
+ onUpload?.(valid);
1875
+ }
1876
+ }, [validateFiles, onUpload]);
1877
+ const handleDrop = (0, import_react17.useCallback)((e) => {
1878
+ e.preventDefault();
1879
+ setIsDragging(false);
1880
+ handleFiles(e.dataTransfer.files);
1881
+ }, [handleFiles]);
1882
+ const handleDragOver = (0, import_react17.useCallback)((e) => {
1883
+ e.preventDefault();
1884
+ setIsDragging(true);
1885
+ }, []);
1886
+ const handleDragLeave = (0, import_react17.useCallback)(() => {
1887
+ setIsDragging(false);
1888
+ }, []);
1889
+ if (files.length > 0 && !loading) {
1890
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl overflow-hidden ${className || ""}`, children: [
1891
+ title && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "px-4 py-3 border-b border-[var(--bui-border-strong,#3f3f46)]", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium", children: title }) }),
1892
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "divide-y divide-[var(--bui-border-strong,#3f3f46)]/50", children: files.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "px-4 py-2.5 flex items-center gap-3", children: [
1893
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FileIcon, { type: f.type }),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "min-w-0 flex-1", children: [
1895
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm truncate", children: f.name }),
1896
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs", children: formatBytes(f.size) })
1897
+ ] }),
1898
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-[var(--bui-success-fg,#6ee7b7)] shrink-0" })
1899
+ ] }, i)) })
1900
+ ] });
1901
+ }
1902
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `bg-[var(--bui-bg-elevated,#27272a)] border border-[var(--bui-border-strong,#3f3f46)] rounded-xl p-4 transition-opacity ${loading ? "opacity-60" : ""} ${className || ""}`, children: [
1903
+ title && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm font-medium mb-3", children: title }),
1904
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1905
+ "div",
1906
+ {
1907
+ onDrop: handleDrop,
1908
+ onDragOver: handleDragOver,
1909
+ onDragLeave: handleDragLeave,
1910
+ onClick: () => inputRef.current?.click(),
1911
+ className: `border-2 border-dashed rounded-lg p-6 text-center cursor-pointer transition-colors ${isDragging ? "border-[var(--bui-primary-border,#2563eb80)] bg-[var(--bui-primary-muted,#1e3a5f)]" : "border-[var(--bui-border-strong,#3f3f46)] hover:border-[var(--bui-border-strong,#3f3f46)] hover:bg-[var(--bui-bg-surface,#18181b)]/50"}`,
1912
+ children: [
1913
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1914
+ "input",
1915
+ {
1916
+ ref: inputRef,
1917
+ type: "file",
1918
+ accept,
1919
+ multiple,
1920
+ onChange: (e) => handleFiles(e.target.files),
1921
+ className: "hidden"
1922
+ }
1923
+ ),
1924
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { className: "w-8 h-8 text-[var(--bui-fg-faint,#52525b)] mx-auto mb-2", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" }) }),
1925
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: isDragging ? "Drop files here" : "Click or drag files to upload" }),
1926
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-[var(--bui-fg-faint,#52525b)] text-xs mt-1", children: [
1927
+ accept ? accept : "Any file type",
1928
+ maxSize ? ` (max ${formatBytes(maxSize)})` : ""
1929
+ ] })
1930
+ ]
1931
+ }
1932
+ ),
1933
+ error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[var(--bui-error-fg,#f87171)] text-xs mt-2", children: error }),
1934
+ loading && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "mt-3 flex items-center gap-2 text-[var(--bui-fg-secondary,#a1a1aa)] text-sm", children: [
1935
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "w-1.5 h-1.5 bg-[var(--bui-fg-muted,#71717a)] rounded-full animate-pulse" }),
1936
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "Uploading..." })
1937
+ ] })
1938
+ ] });
1939
+ }
1940
+ function formatBytes(bytes) {
1941
+ if (bytes < 1024) return `${bytes} B`;
1942
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1943
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1944
+ }
1945
+ function FileIcon({ type }) {
1946
+ const isImage = type.startsWith("image/");
1947
+ const isPdf = type === "application/pdf";
1948
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: `w-8 h-8 rounded-lg flex items-center justify-center shrink-0 ${isImage ? "bg-purple-900/30 text-purple-400" : isPdf ? "bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] text-[var(--bui-error-fg,#f87171)]" : "bg-[var(--bui-bg-hover,#3f3f46)] text-[var(--bui-fg-secondary,#a1a1aa)]"}`, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" }) }) });
1949
+ }
1950
+ // Annotate the CommonJS export names for ESM import in node:
1951
+ 0 && (module.exports = {
1952
+ Chat,
1953
+ ChatPanel,
1954
+ ChatProvider,
1955
+ CodeBlockView,
1956
+ Composer,
1957
+ DataTableView,
1958
+ FileUploadView,
1959
+ FormView,
1960
+ Markdown,
1961
+ MediaDisplayView,
1962
+ Message,
1963
+ Panel,
1964
+ ProgressView,
1965
+ QuestionView,
1966
+ ThemeProvider,
1967
+ Thread,
1968
+ ToastProvider,
1969
+ ToolResult,
1970
+ createToolStateStore,
1971
+ useChatContext,
1972
+ useChatToolOutput,
1973
+ useToast,
1974
+ useToolEffect,
1975
+ useToolOutput,
1976
+ useToolState
1977
+ });