@ash-ai/ui 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1781 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BottomPanels: () => BottomPanels,
24
+ Chat: () => Chat,
25
+ ChatInput: () => ChatInput,
26
+ ChatMessage: () => ChatMessage,
27
+ ChatMessages: () => ChatMessages,
28
+ FileBrowser: () => FileBrowser,
29
+ FileTree: () => FileTree,
30
+ Playground: () => Playground,
31
+ PlaygroundHeader: () => PlaygroundHeader,
32
+ PlaygroundProvider: () => PlaygroundProvider,
33
+ SessionHistory: () => SessionHistory,
34
+ StatusIndicator: () => StatusIndicator,
35
+ Terminal: () => Terminal2,
36
+ ToolCallBlock: () => ToolCallBlock,
37
+ buildFileTree: () => buildFileTree,
38
+ cn: () => cn,
39
+ formatBytes: () => formatBytes,
40
+ formatTime: () => formatTime,
41
+ getFileLanguage: () => getFileLanguage,
42
+ isImageFile: () => isImageFile,
43
+ parseContentBlocks: () => parseContentBlocks,
44
+ useAgents: () => useAgents,
45
+ useFileBrowser: () => useFileBrowser,
46
+ useFileUpload: () => useFileUpload,
47
+ useHealthCheck: () => useHealthCheck,
48
+ usePlaygroundChat: () => usePlaygroundChat,
49
+ usePlaygroundContext: () => usePlaygroundContext,
50
+ useSessions: () => useSessions,
51
+ useTerminal: () => useTerminal
52
+ });
53
+ module.exports = __toCommonJS(index_exports);
54
+
55
+ // src/utils.ts
56
+ var import_clsx = require("clsx");
57
+ var import_tailwind_merge = require("tailwind-merge");
58
+ function cn(...inputs) {
59
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
60
+ }
61
+ function formatBytes(bytes) {
62
+ if (bytes === 0) return "0 B";
63
+ const k = 1024;
64
+ const sizes = ["B", "KB", "MB", "GB"];
65
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
66
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
67
+ }
68
+ function formatTime(ts) {
69
+ return new Date(ts).toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit" });
70
+ }
71
+ function formatTimestamp(ts) {
72
+ return new Date(ts).toLocaleTimeString("en-US", {
73
+ hour12: false,
74
+ hour: "2-digit",
75
+ minute: "2-digit",
76
+ second: "2-digit"
77
+ });
78
+ }
79
+ function buildFileTree(files) {
80
+ const root = [];
81
+ for (const file of files) {
82
+ const parts = file.path.split("/");
83
+ let current = root;
84
+ for (let i = 0; i < parts.length; i++) {
85
+ const name = parts[i];
86
+ const isLast = i === parts.length - 1;
87
+ const partialPath = parts.slice(0, i + 1).join("/");
88
+ let existing = current.find((n) => n.name === name && n.isDir === !isLast);
89
+ if (!existing) {
90
+ existing = {
91
+ name,
92
+ path: partialPath,
93
+ isDir: !isLast,
94
+ size: isLast ? file.size : void 0,
95
+ modifiedAt: isLast ? file.modifiedAt : void 0,
96
+ children: []
97
+ };
98
+ current.push(existing);
99
+ }
100
+ current = existing.children;
101
+ }
102
+ }
103
+ function sortNodes(nodes) {
104
+ nodes.sort((a, b) => {
105
+ if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
106
+ return a.name.localeCompare(b.name);
107
+ });
108
+ for (const n of nodes) {
109
+ if (n.children.length > 0) sortNodes(n.children);
110
+ }
111
+ }
112
+ sortNodes(root);
113
+ return root;
114
+ }
115
+ function parseContentBlocks(content) {
116
+ let blocks = null;
117
+ if (typeof content === "string") {
118
+ try {
119
+ const parsed = JSON.parse(content);
120
+ if (Array.isArray(parsed)) {
121
+ blocks = parsed;
122
+ }
123
+ } catch {
124
+ return { text: content, toolCalls: [] };
125
+ }
126
+ } else if (Array.isArray(content)) {
127
+ blocks = content;
128
+ }
129
+ if (!blocks) {
130
+ return { text: typeof content === "string" ? content : "", toolCalls: [] };
131
+ }
132
+ let text = "";
133
+ const toolCalls = [];
134
+ for (const block of blocks) {
135
+ const b = block;
136
+ if (b.type === "text" && typeof b.text === "string") {
137
+ text += (text ? "\n" : "") + b.text;
138
+ } else if (b.type === "tool_use") {
139
+ toolCalls.push({
140
+ id: b.id || `tool-${toolCalls.length}`,
141
+ name: b.name || "unknown",
142
+ input: b.input,
143
+ state: "completed"
144
+ });
145
+ } else if (b.type === "tool_result") {
146
+ const match = toolCalls.find((tc) => tc.id === b.tool_use_id);
147
+ if (match) {
148
+ match.output = b.content;
149
+ match.isError = b.is_error ?? false;
150
+ match.state = b.is_error ? "error" : "completed";
151
+ }
152
+ }
153
+ }
154
+ return { text, toolCalls };
155
+ }
156
+ var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico"]);
157
+ function isImageFile(name) {
158
+ const ext = name.split(".").pop()?.toLowerCase();
159
+ return IMAGE_EXTS.has(ext || "");
160
+ }
161
+ function getFileLanguage(name) {
162
+ const ext = name.split(".").pop()?.toLowerCase();
163
+ const map = {
164
+ ts: "typescript",
165
+ tsx: "typescript",
166
+ js: "javascript",
167
+ jsx: "javascript",
168
+ py: "python",
169
+ rs: "rust",
170
+ go: "go",
171
+ rb: "ruby",
172
+ json: "json",
173
+ yaml: "yaml",
174
+ yml: "yaml",
175
+ toml: "toml",
176
+ md: "markdown",
177
+ sh: "bash",
178
+ bash: "bash",
179
+ css: "css",
180
+ html: "html",
181
+ sql: "sql",
182
+ xml: "xml"
183
+ };
184
+ return map[ext || ""] || "text";
185
+ }
186
+
187
+ // src/context/PlaygroundContext.tsx
188
+ var import_react6 = require("react");
189
+
190
+ // src/hooks/usePlaygroundChat.ts
191
+ var import_react = require("react");
192
+ function usePlaygroundChat({
193
+ client,
194
+ agentSlug,
195
+ initialSessionId,
196
+ onSessionStart,
197
+ onError
198
+ }) {
199
+ const [messages, setMessages] = (0, import_react.useState)([]);
200
+ const [input, setInput] = (0, import_react.useState)("");
201
+ const [sending, setSending] = (0, import_react.useState)(false);
202
+ const [loading, setLoading] = (0, import_react.useState)(false);
203
+ const [error, setError] = (0, import_react.useState)(null);
204
+ const [sessionId, setSessionId] = (0, import_react.useState)(initialSessionId ?? null);
205
+ const [attachedFiles, setAttachedFiles] = (0, import_react.useState)([]);
206
+ const sendingRef = (0, import_react.useRef)(false);
207
+ const startNewChat = (0, import_react.useCallback)(() => {
208
+ setSessionId(null);
209
+ setMessages([]);
210
+ setAttachedFiles([]);
211
+ setError(null);
212
+ }, []);
213
+ const loadSession = (0, import_react.useCallback)(async (sid) => {
214
+ setSessionId(sid);
215
+ setLoading(true);
216
+ setError(null);
217
+ try {
218
+ const sdkMessages = await client.listMessages(sid);
219
+ const msgs = [];
220
+ for (let i = 0; i < sdkMessages.length; i++) {
221
+ const m = sdkMessages[i];
222
+ let text = "";
223
+ const tools = [];
224
+ let parsed;
225
+ try {
226
+ parsed = JSON.parse(m.content);
227
+ } catch {
228
+ parsed = m.content;
229
+ }
230
+ const data = parsed;
231
+ if (data.type === "assistant" && Array.isArray(data.message?.content)) {
232
+ for (const b of data.message.content) {
233
+ if (b.type === "text" && b.text) {
234
+ text += (text ? "\n" : "") + b.text;
235
+ } else if (b.type === "tool_use") {
236
+ tools.push({
237
+ id: b.id || `tool-${tools.length}`,
238
+ name: b.name || "unknown",
239
+ input: b.input,
240
+ state: "completed"
241
+ });
242
+ }
243
+ }
244
+ }
245
+ if (data.type === "user" && data.tool_use_result) {
246
+ const r = data.tool_use_result;
247
+ const match = tools.find((tc) => tc.id === r.tool_use_id) || (msgs.length > 0 ? msgs[msgs.length - 1].toolCalls?.find((tc) => tc.id === r.tool_use_id) : void 0);
248
+ if (match) {
249
+ match.output = r.stdout ?? r.content;
250
+ match.isError = r.is_error ?? false;
251
+ match.state = r.is_error ? "error" : "completed";
252
+ }
253
+ continue;
254
+ }
255
+ if (data.type === "user" && !data.tool_use_result) {
256
+ const content = Array.isArray(data.content) ? data.content.filter((b) => b.type === "text").map((b) => b.text).join("\n") : typeof data.content === "string" ? data.content : "";
257
+ if (content) {
258
+ msgs.push({
259
+ id: m.id || `msg-${i}`,
260
+ role: "user",
261
+ content,
262
+ timestamp: m.createdAt
263
+ });
264
+ }
265
+ continue;
266
+ }
267
+ if (data.type === "result" && typeof data.result === "string") {
268
+ text = data.result;
269
+ }
270
+ if (text || tools.length > 0) {
271
+ msgs.push({
272
+ id: m.id || `msg-${i}`,
273
+ role: m.role === "user" ? "user" : "assistant",
274
+ content: text,
275
+ toolCalls: tools.length > 0 ? tools : void 0,
276
+ timestamp: m.createdAt
277
+ });
278
+ }
279
+ }
280
+ setMessages(msgs);
281
+ } catch {
282
+ setMessages([{ id: "err", role: "system", content: "Failed to load session messages." }]);
283
+ } finally {
284
+ setLoading(false);
285
+ }
286
+ }, [client]);
287
+ const send = (0, import_react.useCallback)(async () => {
288
+ const textContent = input.trim();
289
+ const readyFiles = attachedFiles.filter((f) => !f.uploading);
290
+ if (!textContent && readyFiles.length === 0 || !agentSlug || sendingRef.current) return;
291
+ let content = textContent;
292
+ if (readyFiles.length > 0) {
293
+ const fileRefs = readyFiles.map((f) => `[Attached file: ${f.filename}](${f.url})`).join("\n");
294
+ content = content ? `${content}
295
+
296
+ ${fileRefs}` : fileRefs;
297
+ }
298
+ let activeSessionId = sessionId;
299
+ if (!activeSessionId) {
300
+ setSending(true);
301
+ sendingRef.current = true;
302
+ setError(null);
303
+ try {
304
+ const session = await client.createSession(agentSlug);
305
+ activeSessionId = session.id;
306
+ setSessionId(activeSessionId);
307
+ onSessionStart?.(activeSessionId);
308
+ } catch (err) {
309
+ const msg = err instanceof Error ? err.message : "Failed to create session";
310
+ setError(msg);
311
+ onError?.(msg);
312
+ setSending(false);
313
+ sendingRef.current = false;
314
+ return;
315
+ }
316
+ }
317
+ setInput("");
318
+ setAttachedFiles([]);
319
+ const userMsg = {
320
+ id: `user-${Date.now()}`,
321
+ role: "user",
322
+ content,
323
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
324
+ };
325
+ setMessages((prev) => [...prev, userMsg]);
326
+ setSending(true);
327
+ sendingRef.current = true;
328
+ const assistantId = `assistant-${Date.now()}`;
329
+ setMessages((prev) => [...prev, { id: assistantId, role: "assistant", content: "", isStreaming: true }]);
330
+ try {
331
+ let fullContent = "";
332
+ const toolCalls = [];
333
+ for await (const event of client.sendMessageStream(activeSessionId, content, { includePartialMessages: true })) {
334
+ if (event.type === "text_delta") {
335
+ const delta = event.data.delta || "";
336
+ fullContent += delta;
337
+ setMessages((prev) => prev.map(
338
+ (m) => m.id === assistantId ? { ...m, content: fullContent, toolCalls: [...toolCalls], isStreaming: true } : m
339
+ ));
340
+ } else if (event.type === "tool_use") {
341
+ const data = event.data;
342
+ const tc = {
343
+ id: data.id || `tool-${Date.now()}-${toolCalls.length}`,
344
+ name: data.name || "unknown",
345
+ input: data.input,
346
+ state: "running"
347
+ };
348
+ toolCalls.push(tc);
349
+ setMessages((prev) => prev.map(
350
+ (m) => m.id === assistantId ? { ...m, content: fullContent, toolCalls: [...toolCalls], isStreaming: true } : m
351
+ ));
352
+ } else if (event.type === "tool_result") {
353
+ const data = event.data;
354
+ const matchIdx = toolCalls.findIndex((tc) => tc.id === data.tool_use_id);
355
+ if (matchIdx !== -1) {
356
+ toolCalls[matchIdx] = {
357
+ ...toolCalls[matchIdx],
358
+ output: data.content,
359
+ isError: data.is_error ?? false,
360
+ state: data.is_error ? "error" : "completed"
361
+ };
362
+ } else {
363
+ toolCalls.push({
364
+ id: data.tool_use_id || `result-${Date.now()}`,
365
+ name: "tool_result",
366
+ input: void 0,
367
+ output: data.content,
368
+ isError: data.is_error ?? false,
369
+ state: data.is_error ? "error" : "completed"
370
+ });
371
+ }
372
+ setMessages((prev) => prev.map(
373
+ (m) => m.id === assistantId ? { ...m, content: fullContent, toolCalls: [...toolCalls], isStreaming: true } : m
374
+ ));
375
+ } else if (event.type === "turn_complete" || event.type === "done") {
376
+ const data = event.data;
377
+ if (!fullContent && data.result) {
378
+ fullContent = data.result;
379
+ }
380
+ for (const tc of toolCalls) {
381
+ if (tc.state === "running" || tc.state === "pending") tc.state = "completed";
382
+ }
383
+ setMessages((prev) => prev.map(
384
+ (m) => m.id === assistantId ? { ...m, content: fullContent || "(no response)", toolCalls: [...toolCalls], isStreaming: false, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : m
385
+ ));
386
+ } else if (event.type === "error") {
387
+ const data = event.data;
388
+ const errText = data.error || "Unknown error";
389
+ setMessages((prev) => prev.map(
390
+ (m) => m.id === assistantId ? { ...m, role: "system", content: `Error: ${errText}`, isStreaming: false } : m
391
+ ));
392
+ }
393
+ }
394
+ for (const tc of toolCalls) {
395
+ if (tc.state === "running" || tc.state === "pending") tc.state = "completed";
396
+ }
397
+ setMessages((prev) => prev.map(
398
+ (m) => m.id === assistantId && m.isStreaming ? { ...m, content: fullContent || "(no response)", toolCalls: toolCalls.length > 0 ? [...toolCalls] : void 0, isStreaming: false, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : m
399
+ ));
400
+ } catch (err) {
401
+ const errMsg = err instanceof Error ? err.message : "Failed to send";
402
+ setMessages((prev) => prev.map(
403
+ (m) => m.id === assistantId ? { ...m, role: "system", content: `Error: ${errMsg}`, isStreaming: false } : m
404
+ ));
405
+ } finally {
406
+ setSending(false);
407
+ sendingRef.current = false;
408
+ }
409
+ }, [input, attachedFiles, sessionId, agentSlug, client, onSessionStart, onError]);
410
+ return {
411
+ messages,
412
+ input,
413
+ setInput,
414
+ sending,
415
+ loading,
416
+ error,
417
+ sessionId,
418
+ attachedFiles,
419
+ setAttachedFiles,
420
+ send,
421
+ loadSession,
422
+ startNewChat,
423
+ setMessages
424
+ };
425
+ }
426
+
427
+ // src/hooks/useAgents.ts
428
+ var import_react2 = require("react");
429
+ function useAgents({ client }) {
430
+ const [agents, setAgents] = (0, import_react2.useState)([]);
431
+ const [loading, setLoading] = (0, import_react2.useState)(true);
432
+ const [error, setError] = (0, import_react2.useState)(null);
433
+ const fetch_ = async () => {
434
+ setLoading(true);
435
+ setError(null);
436
+ try {
437
+ const result = await client.listAgents();
438
+ setAgents(result);
439
+ } catch (err) {
440
+ setError(err instanceof Error ? err.message : "Failed to fetch agents");
441
+ } finally {
442
+ setLoading(false);
443
+ }
444
+ };
445
+ (0, import_react2.useEffect)(() => {
446
+ fetch_();
447
+ }, []);
448
+ return { agents, loading, error, refresh: fetch_ };
449
+ }
450
+
451
+ // src/hooks/useSessions.ts
452
+ var import_react3 = require("react");
453
+ function useSessions({
454
+ client,
455
+ agent,
456
+ limit = 20,
457
+ enabled = true
458
+ }) {
459
+ const [sessions, setSessions] = (0, import_react3.useState)([]);
460
+ const [loading, setLoading] = (0, import_react3.useState)(false);
461
+ const [error, setError] = (0, import_react3.useState)(null);
462
+ const fetch_ = (0, import_react3.useCallback)(async () => {
463
+ setLoading(true);
464
+ setError(null);
465
+ try {
466
+ const result = await client.listSessions({ agent, limit });
467
+ setSessions(result);
468
+ } catch (err) {
469
+ setError(err instanceof Error ? err.message : "Failed to fetch sessions");
470
+ } finally {
471
+ setLoading(false);
472
+ }
473
+ }, [client, agent, limit]);
474
+ (0, import_react3.useEffect)(() => {
475
+ if (enabled) fetch_();
476
+ }, [enabled, fetch_]);
477
+ return { sessions, loading, error, refresh: fetch_ };
478
+ }
479
+
480
+ // src/hooks/useHealthCheck.ts
481
+ var import_react4 = require("react");
482
+ function useHealthCheck({ client }) {
483
+ const [connected, setConnected] = (0, import_react4.useState)(null);
484
+ const check = async () => {
485
+ try {
486
+ await client.health();
487
+ setConnected(true);
488
+ } catch {
489
+ setConnected(false);
490
+ }
491
+ };
492
+ (0, import_react4.useEffect)(() => {
493
+ check();
494
+ }, []);
495
+ return { connected, refresh: check };
496
+ }
497
+
498
+ // src/hooks/useFileUpload.ts
499
+ var import_react5 = require("react");
500
+ function useFileUpload({
501
+ client,
502
+ onFilesChange,
503
+ onError
504
+ }) {
505
+ const handleFileSelect = (0, import_react5.useCallback)(async (e) => {
506
+ const files = e.target.files;
507
+ if (!files || files.length === 0) return;
508
+ for (const file of Array.from(files)) {
509
+ const tempId = `uploading-${Date.now()}-${Math.random().toString(36).slice(2)}`;
510
+ onFilesChange((prev) => [...prev, { id: tempId, filename: file.name, url: "", uploading: true }]);
511
+ try {
512
+ const base64 = await new Promise((resolve, reject) => {
513
+ const reader = new FileReader();
514
+ reader.onload = () => {
515
+ const result = reader.result;
516
+ resolve(result.split(",")[1]);
517
+ };
518
+ reader.onerror = () => reject(new Error("Failed to read file"));
519
+ reader.readAsDataURL(file);
520
+ });
521
+ const uploaded = await client.uploadFile({
522
+ filename: file.name,
523
+ content: base64,
524
+ mimeType: file.type || void 0,
525
+ ttl: "24h"
526
+ });
527
+ const url = await client.getFileUrl(uploaded.id);
528
+ onFilesChange(
529
+ (prev) => prev.map((f) => f.id === tempId ? { id: uploaded.id, filename: file.name, url, uploading: false } : f)
530
+ );
531
+ } catch {
532
+ onFilesChange((prev) => prev.filter((f) => f.id !== tempId));
533
+ onError?.(`Failed to upload ${file.name}`);
534
+ }
535
+ }
536
+ e.target.value = "";
537
+ }, [client, onFilesChange, onError]);
538
+ const removeFile = (0, import_react5.useCallback)((fileId) => {
539
+ onFilesChange((prev) => prev.filter((f) => f.id !== fileId));
540
+ }, [onFilesChange]);
541
+ return { handleFileSelect, removeFile };
542
+ }
543
+
544
+ // src/context/PlaygroundContext.tsx
545
+ var import_jsx_runtime = require("react/jsx-runtime");
546
+ var PlaygroundCtx = (0, import_react6.createContext)(null);
547
+ function usePlaygroundContext() {
548
+ const ctx = (0, import_react6.useContext)(PlaygroundCtx);
549
+ if (!ctx) throw new Error("usePlaygroundContext must be used within <PlaygroundProvider>");
550
+ return ctx;
551
+ }
552
+ function PlaygroundProvider({ client, defaultAgent, children }) {
553
+ const [selectedAgent, setSelectedAgentRaw] = (0, import_react6.useState)(defaultAgent || "");
554
+ const [showHistory, setShowHistory] = (0, import_react6.useState)(false);
555
+ const [terminalOpen, setTerminalOpen] = (0, import_react6.useState)(false);
556
+ const [filesOpen, setFilesOpen] = (0, import_react6.useState)(false);
557
+ const agentsHook = useAgents({ client });
558
+ const health = useHealthCheck({ client });
559
+ const resolvedAgent = selectedAgent || agentsHook.agents[0]?.slug || agentsHook.agents[0]?.name || "";
560
+ const chat = usePlaygroundChat({
561
+ client,
562
+ agentSlug: resolvedAgent
563
+ });
564
+ const sessions = useSessions({
565
+ client,
566
+ agent: resolvedAgent,
567
+ enabled: showHistory
568
+ });
569
+ const fileUpload = useFileUpload({
570
+ client,
571
+ onFilesChange: chat.setAttachedFiles,
572
+ onError: (err) => {
573
+ chat.setMessages((prev) => [
574
+ ...prev,
575
+ { id: `err-${Date.now()}`, role: "system", content: err }
576
+ ]);
577
+ }
578
+ });
579
+ const setSelectedAgent = (0, import_react6.useCallback)((slug) => {
580
+ setSelectedAgentRaw(slug);
581
+ chat.startNewChat();
582
+ }, [chat]);
583
+ const value = {
584
+ client,
585
+ chat,
586
+ agents: agentsHook,
587
+ sessions,
588
+ health,
589
+ fileUpload,
590
+ selectedAgent: resolvedAgent,
591
+ setSelectedAgent,
592
+ showHistory,
593
+ setShowHistory,
594
+ terminalOpen,
595
+ setTerminalOpen,
596
+ filesOpen,
597
+ setFilesOpen
598
+ };
599
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaygroundCtx.Provider, { value, children });
600
+ }
601
+
602
+ // src/icons.tsx
603
+ var import_jsx_runtime2 = require("react/jsx-runtime");
604
+ function icon(d, opts) {
605
+ const Component = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
606
+ "svg",
607
+ {
608
+ xmlns: "http://www.w3.org/2000/svg",
609
+ width: "24",
610
+ height: "24",
611
+ viewBox: opts?.viewBox ?? "0 0 24 24",
612
+ fill: opts?.fill ? "currentColor" : "none",
613
+ stroke: "currentColor",
614
+ strokeWidth: "2",
615
+ strokeLinecap: "round",
616
+ strokeLinejoin: "round",
617
+ ...props,
618
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d })
619
+ }
620
+ );
621
+ return Component;
622
+ }
623
+ var Bot = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
624
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 8V4H8" }),
625
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }),
626
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 14h2" }),
627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 14h2" }),
628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 13v2" }),
629
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 13v2" })
630
+ ] });
631
+ var User = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
632
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" }),
633
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "7", r: "4" })
634
+ ] });
635
+ var Send = icon("m22 2-7 20-4-9-9-4Z M22 2 11 13");
636
+ var MessageSquare = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
637
+ var Terminal = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
638
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "4 17 10 11 4 5" }),
639
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", x2: "20", y1: "19", y2: "19" })
640
+ ] });
641
+ var FolderOpen = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2" }) });
642
+ var Folder = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" }) });
643
+ var FileText = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
644
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
645
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
646
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 9H8" }),
647
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 13H8" }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 17H8" })
649
+ ] });
650
+ var FileCode = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
651
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
653
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m10 13-2 2 2 2" }),
654
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m14 17 2-2-2-2" })
655
+ ] });
656
+ var FileJson = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
657
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
658
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
659
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1" }),
660
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1" })
661
+ ] });
662
+ var ImageIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
663
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
664
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "9", cy: "9", r: "2" }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
666
+ ] });
667
+ var Loader2 = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
668
+ var ChevronDown = icon("m6 9 6 6 6-6");
669
+ var ChevronUp = icon("m18 15-6-6-6 6");
670
+ var ChevronRight = icon("m9 18 6-6-6-6");
671
+ var Plus = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 12h14" }),
673
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 5v14" })
674
+ ] });
675
+ var X = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
676
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 6 6 18" }),
677
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m6 6 12 12" })
678
+ ] });
679
+ var Paperclip = icon("m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48");
680
+ var Clock = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
682
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "12 6 12 12 16 14" })
683
+ ] });
684
+ var Wifi = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
685
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 20h.01" }),
686
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 8.82a15 15 0 0 1 20 0" }),
687
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 12.859a10 10 0 0 1 14 0" }),
688
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8.5 16.429a5 5 0 0 1 7 0" })
689
+ ] });
690
+ var WifiOff = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
691
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 20h.01" }),
692
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8.5 16.429a5 5 0 0 1 7 0" }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 12.859a10 10 0 0 1 5.17-2.69" }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 12.859a10 10 0 0 0-2.007-1.523" }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 8.82a15 15 0 0 1 4.177-2.643" }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M22 8.82a15 15 0 0 0-11.288-3.764" }),
697
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m2 2 20 20" })
698
+ ] });
699
+ var Wrench = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" }) });
700
+ var CheckCircle2 = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
701
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
702
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m9 12 2 2 4-4" })
703
+ ] });
704
+ var XCircle = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
705
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
706
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m15 9-6 6" }),
707
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m9 9 6 6" })
708
+ ] });
709
+ var Search = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
711
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m21 21-4.3-4.3" })
712
+ ] });
713
+ var Trash2 = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
714
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 6h18" }),
715
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
716
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }),
717
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", x2: "10", y1: "11", y2: "17" }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "14", x2: "14", y1: "11", y2: "17" })
719
+ ] });
720
+ var ArrowDown = icon("M12 5v14 m7-7-7 7-7-7");
721
+ var Download = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
722
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
723
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: "7 10 12 15 17 10" }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", x2: "12", y1: "15", y2: "3" })
725
+ ] });
726
+ var RefreshCw = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [
727
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
728
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 3v5h-5" }),
729
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 16H3v5" })
731
+ ] });
732
+ function getFileIcon(name) {
733
+ const ext = name.split(".").pop()?.toLowerCase();
734
+ if (["ts", "tsx", "js", "jsx", "py", "rs", "go", "rb", "sh", "bash"].includes(ext || ""))
735
+ return FileCode;
736
+ if (["json", "yaml", "yml", "toml"].includes(ext || ""))
737
+ return FileJson;
738
+ if (["png", "jpg", "jpeg", "gif", "svg", "webp", "ico"].includes(ext || ""))
739
+ return ImageIcon;
740
+ return FileText;
741
+ }
742
+
743
+ // src/components/StatusIndicator.tsx
744
+ var import_jsx_runtime3 = require("react/jsx-runtime");
745
+ function StatusIndicator({ connected, className }) {
746
+ if (connected === null) return null;
747
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: cn(
748
+ "flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[10px] font-medium border",
749
+ connected ? "bg-green-500/10 text-green-400 border-green-500/20" : "bg-red-500/10 text-red-400 border-red-500/20",
750
+ className
751
+ ), children: connected ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
752
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Wifi, { className: "h-3 w-3" }),
753
+ " Runtime connected"
754
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
755
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WifiOff, { className: "h-3 w-3" }),
756
+ " Runtime disconnected"
757
+ ] }) });
758
+ }
759
+
760
+ // src/components/PlaygroundHeader.tsx
761
+ var import_jsx_runtime4 = require("react/jsx-runtime");
762
+ function PlaygroundHeader({
763
+ agents,
764
+ selectedAgent,
765
+ onAgentChange,
766
+ runtimeConnected,
767
+ showHistory,
768
+ onToggleHistory,
769
+ sessionId,
770
+ onNewChat,
771
+ className
772
+ }) {
773
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: cn("shrink-0 flex items-center justify-between border-b border-white/10 pb-3 mb-4", className), children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-3", children: [
775
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 text-xs font-medium text-white/40 uppercase tracking-wider", children: [
776
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-accent animate-pulse" }),
777
+ "PLAYGROUND"
778
+ ] }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
780
+ "select",
781
+ {
782
+ value: selectedAgent,
783
+ onChange: (e) => onAgentChange(e.target.value),
784
+ className: "rounded-lg border border-white/10 bg-white/5 px-3 py-1.5 text-sm text-white focus:border-accent/50 focus:outline-none",
785
+ children: [
786
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", disabled: true, children: "Select agent..." }),
787
+ agents.map((a) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: a.slug || a.name, children: a.name }, a.id))
788
+ ]
789
+ }
790
+ ),
791
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusIndicator, { connected: runtimeConnected })
792
+ ] }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2", children: [
794
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
795
+ "button",
796
+ {
797
+ onClick: onToggleHistory,
798
+ className: cn(
799
+ "flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium border transition-all",
800
+ showHistory ? "bg-accent/10 text-accent border-accent/30" : "bg-white/5 text-white/50 border-white/10 hover:bg-white/10"
801
+ ),
802
+ children: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Clock, { className: "h-3 w-3" }),
804
+ "History"
805
+ ]
806
+ }
807
+ ),
808
+ selectedAgent && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
809
+ "button",
810
+ {
811
+ onClick: onNewChat,
812
+ className: cn(
813
+ "flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium border transition-all",
814
+ !sessionId ? "bg-accent/10 text-accent border-accent/30" : "bg-white/5 text-white/50 border-white/10 hover:bg-white/10"
815
+ ),
816
+ children: [
817
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Plus, { className: "h-3 w-3" }),
818
+ "New Chat"
819
+ ]
820
+ }
821
+ )
822
+ ] })
823
+ ] });
824
+ }
825
+
826
+ // src/components/ChatMessages.tsx
827
+ var import_react8 = require("react");
828
+
829
+ // src/components/ToolCallBlock.tsx
830
+ var import_react7 = require("react");
831
+ var import_jsx_runtime5 = require("react/jsx-runtime");
832
+ function ToolCallBlock({ tool }) {
833
+ const [open, setOpen] = (0, import_react7.useState)(false);
834
+ const isRunning = tool.state === "running" || tool.state === "pending";
835
+ const isError = tool.state === "error";
836
+ const isDone = tool.state === "completed";
837
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "my-2 rounded-lg border border-white/10 bg-white/[0.03] overflow-hidden", children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
839
+ "button",
840
+ {
841
+ onClick: () => setOpen(!open),
842
+ className: "flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-white/5 transition-colors",
843
+ children: [
844
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Wrench, { className: "h-3.5 w-3.5 shrink-0 text-white/40" }),
845
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-xs font-medium text-white/70 truncate", children: tool.name }),
846
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ml-auto flex items-center gap-1.5", children: [
847
+ isRunning && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex items-center gap-1 rounded-full bg-blue-500/10 border border-blue-500/20 px-2 py-0.5 text-[10px] font-medium text-blue-400", children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Loader2, { className: "h-3 w-3 animate-spin" }),
849
+ "Running"
850
+ ] }),
851
+ isDone && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex items-center gap-1 rounded-full bg-green-500/10 border border-green-500/20 px-2 py-0.5 text-[10px] font-medium text-green-400", children: [
852
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CheckCircle2, { className: "h-3 w-3" }),
853
+ "Done"
854
+ ] }),
855
+ isError && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex items-center gap-1 rounded-full bg-red-500/10 border border-red-500/20 px-2 py-0.5 text-[10px] font-medium text-red-400", children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(XCircle, { className: "h-3 w-3" }),
857
+ "Error"
858
+ ] }),
859
+ open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronDown, { className: "h-3 w-3 text-white/30" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronRight, { className: "h-3 w-3 text-white/30" })
860
+ ] })
861
+ ]
862
+ }
863
+ ),
864
+ open && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "border-t border-white/10 px-3 py-2 space-y-2", children: [
865
+ tool.input != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
866
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-[10px] font-medium text-white/30 uppercase tracking-wider mb-1", children: "Input" }),
867
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { className: "rounded-md bg-black/30 px-3 py-2 text-[11px] text-white/60 overflow-x-auto max-h-48 scrollbar-thin", children: typeof tool.input === "string" ? tool.input : JSON.stringify(tool.input, null, 2) })
868
+ ] }),
869
+ tool.output != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
870
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-[10px] font-medium text-white/30 uppercase tracking-wider mb-1", children: isError ? "Error" : "Output" }),
871
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
872
+ "pre",
873
+ {
874
+ className: cn(
875
+ "rounded-md px-3 py-2 text-[11px] overflow-x-auto max-h-64 scrollbar-thin",
876
+ isError ? "bg-red-500/10 text-red-400" : "bg-black/30 text-white/60"
877
+ ),
878
+ children: typeof tool.output === "string" ? tool.output : JSON.stringify(tool.output, null, 2)
879
+ }
880
+ )
881
+ ] })
882
+ ] })
883
+ ] });
884
+ }
885
+
886
+ // src/components/ChatMessage.tsx
887
+ var import_jsx_runtime6 = require("react/jsx-runtime");
888
+ function ChatMessage({ message: msg }) {
889
+ const isUser = msg.role === "user";
890
+ const isSystem = msg.role === "system";
891
+ if (isSystem) {
892
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "max-w-lg rounded-xl bg-red-500/10 border border-red-500/20 px-4 py-2 text-xs text-red-400", children: msg.content }) });
893
+ }
894
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn("flex gap-3", isUser ? "flex-row-reverse" : "flex-row"), children: [
895
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cn(
896
+ "flex h-8 w-8 shrink-0 items-center justify-center rounded-full",
897
+ isUser ? "bg-accent/20" : "bg-white/10"
898
+ ), children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(User, { className: "h-4 w-4 text-accent" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Bot, { className: "h-4 w-4 text-white/60" }) }),
899
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn(
900
+ "max-w-[75%] rounded-2xl px-4 py-2.5",
901
+ isUser ? "bg-accent/10 border border-accent/20 text-white" : "bg-white/5 border border-white/10 text-white/80"
902
+ ), children: [
903
+ msg.content && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "whitespace-pre-wrap text-sm leading-relaxed", children: [
904
+ msg.content,
905
+ msg.isStreaming && !msg.toolCalls?.some((tc) => tc.state === "running") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ml-1 inline-block h-4 w-0.5 animate-pulse bg-accent align-text-bottom" })
906
+ ] }),
907
+ msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn(msg.content && "mt-2"), children: [
908
+ msg.toolCalls.map((tc) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToolCallBlock, { tool: tc }, tc.id)),
909
+ msg.isStreaming && msg.toolCalls.some((tc) => tc.state === "running") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ml-1 inline-block h-4 w-0.5 animate-pulse bg-accent align-text-bottom" })
910
+ ] }),
911
+ msg.isStreaming && !msg.content && (!msg.toolCalls || msg.toolCalls.length === 0) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "inline-block h-4 w-0.5 animate-pulse bg-accent align-text-bottom" }),
912
+ msg.timestamp && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mt-1 text-right text-xs text-white/30", children: formatTime(msg.timestamp) })
913
+ ] })
914
+ ] });
915
+ }
916
+
917
+ // src/components/ChatMessages.tsx
918
+ var import_jsx_runtime7 = require("react/jsx-runtime");
919
+ function ChatMessages({ messages, className }) {
920
+ const chatRef = (0, import_react8.useRef)(null);
921
+ (0, import_react8.useEffect)(() => {
922
+ if (chatRef.current) {
923
+ chatRef.current.scrollTo({ top: chatRef.current.scrollHeight, behavior: "smooth" });
924
+ }
925
+ }, [messages]);
926
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: chatRef, className: className ?? "flex-1 overflow-auto p-4 space-y-3 scrollbar-thin", children: messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex h-full flex-col items-center justify-center text-center", children: [
927
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageSquare, { className: "mb-3 h-8 w-8 text-white/20" }),
928
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm text-white/40", children: "No messages yet" }),
929
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mt-1 text-xs text-white/30", children: "Send a message to start the conversation" })
930
+ ] }) : messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatMessage, { message: msg }, msg.id)) });
931
+ }
932
+
933
+ // src/components/ChatInput.tsx
934
+ var import_react9 = require("react");
935
+ var import_jsx_runtime8 = require("react/jsx-runtime");
936
+ function ChatInput({
937
+ input,
938
+ onInputChange,
939
+ onSend,
940
+ sending,
941
+ disabled,
942
+ placeholder = "Send a message...",
943
+ attachedFiles = [],
944
+ onFileSelect,
945
+ onRemoveFile,
946
+ className
947
+ }) {
948
+ const textareaRef = (0, import_react9.useRef)(null);
949
+ const fileInputRef = (0, import_react9.useRef)(null);
950
+ const handleKeyDown = (e) => {
951
+ if (e.key === "Enter" && !e.shiftKey) {
952
+ e.preventDefault();
953
+ onSend();
954
+ }
955
+ };
956
+ const readyFiles = attachedFiles.filter((f) => !f.uploading);
957
+ const canSend = (input.trim() || readyFiles.length > 0) && !sending && !disabled;
958
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cn("shrink-0 border-t border-white/10 p-4", className), children: [
959
+ attachedFiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mb-2 flex flex-wrap gap-2", children: attachedFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
960
+ "div",
961
+ {
962
+ className: cn(
963
+ "flex items-center gap-1.5 rounded-lg border px-2.5 py-1 text-xs",
964
+ file.uploading ? "border-white/10 bg-white/5 text-white/40" : "border-accent/20 bg-accent/5 text-accent"
965
+ ),
966
+ children: [
967
+ file.uploading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Paperclip, { className: "h-3 w-3" }),
968
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "max-w-[180px] truncate", children: file.filename }),
969
+ !file.uploading && onRemoveFile && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
970
+ "button",
971
+ {
972
+ onClick: () => onRemoveFile(file.id),
973
+ className: "ml-0.5 rounded-full p-0.5 hover:bg-white/10 transition-colors",
974
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(X, { className: "h-3 w-3" })
975
+ }
976
+ )
977
+ ]
978
+ },
979
+ file.id
980
+ )) }),
981
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-end gap-3", children: [
982
+ onFileSelect && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
984
+ "input",
985
+ {
986
+ ref: fileInputRef,
987
+ type: "file",
988
+ multiple: true,
989
+ onChange: onFileSelect,
990
+ className: "hidden"
991
+ }
992
+ ),
993
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
994
+ "button",
995
+ {
996
+ onClick: () => fileInputRef.current?.click(),
997
+ disabled: sending || disabled,
998
+ className: cn(
999
+ "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border transition-colors",
1000
+ "border-white/10 bg-white/5 text-white/40 hover:bg-white/10 hover:text-white/60",
1001
+ (sending || disabled) && "opacity-50 cursor-not-allowed"
1002
+ ),
1003
+ title: "Attach files",
1004
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Paperclip, { className: "h-4 w-4" })
1005
+ }
1006
+ )
1007
+ ] }),
1008
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1009
+ "textarea",
1010
+ {
1011
+ ref: textareaRef,
1012
+ value: input,
1013
+ onChange: (e) => {
1014
+ onInputChange(e.target.value);
1015
+ const el = e.target;
1016
+ el.style.height = "auto";
1017
+ el.style.height = Math.min(el.scrollHeight, 160) + "px";
1018
+ },
1019
+ onKeyDown: handleKeyDown,
1020
+ placeholder,
1021
+ rows: 1,
1022
+ className: cn(
1023
+ "w-full resize-none rounded-xl border bg-white/5 px-4 py-3 text-sm text-white",
1024
+ "border-white/10 placeholder:text-white/40",
1025
+ "focus:border-accent/50 focus:outline-none focus:ring-0"
1026
+ )
1027
+ }
1028
+ ),
1029
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1030
+ "button",
1031
+ {
1032
+ onClick: onSend,
1033
+ disabled: !canSend,
1034
+ className: cn(
1035
+ "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl transition-colors",
1036
+ canSend ? "bg-accent text-white hover:bg-accent/90" : "bg-white/5 text-white/20 cursor-not-allowed"
1037
+ ),
1038
+ children: sending ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Send, { className: "h-4 w-4" })
1039
+ }
1040
+ )
1041
+ ] })
1042
+ ] });
1043
+ }
1044
+
1045
+ // src/components/Chat.tsx
1046
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1047
+ function Chat({
1048
+ messages,
1049
+ input,
1050
+ onInputChange,
1051
+ onSend,
1052
+ sending,
1053
+ loading,
1054
+ error,
1055
+ isActive,
1056
+ agentName,
1057
+ attachedFiles,
1058
+ onFileSelect,
1059
+ onRemoveFile,
1060
+ className
1061
+ }) {
1062
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cn("flex flex-col overflow-hidden", className), children: [
1063
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-1 items-center justify-center", children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Loader2, { className: "mr-2 h-5 w-5 animate-spin text-white/40" }),
1065
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm text-white/50", children: "Loading session..." })
1066
+ ] }) : !isActive ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-1 flex-col items-center justify-center text-center px-8", children: [
1067
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-accent/10 border border-accent/20", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageSquare, { className: "h-8 w-8 text-accent" }) }),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { className: "text-xl font-semibold text-white", children: "Agent Playground" }),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "mt-2 max-w-md text-sm text-white/50", children: agentName ? `Start a new conversation with ${agentName}, or resume a past session from History.` : "Select an agent above to get started." }),
1070
+ agentName && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mt-4 inline-flex items-center gap-2 rounded-full bg-white/10 px-4 py-2 text-sm text-white/70", children: [
1071
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Bot, { className: "h-4 w-4 text-accent" }),
1072
+ agentName
1073
+ ] })
1074
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatMessages, { messages }),
1075
+ error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "shrink-0 mx-4 mb-2 rounded-xl border border-red-500/20 bg-red-500/10 px-4 py-2.5 text-sm text-red-400", children: error }),
1076
+ agentName && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1077
+ ChatInput,
1078
+ {
1079
+ input,
1080
+ onInputChange,
1081
+ onSend,
1082
+ sending,
1083
+ placeholder: isActive ? "Send a message..." : "Send a message to start a new session...",
1084
+ attachedFiles,
1085
+ onFileSelect,
1086
+ onRemoveFile
1087
+ }
1088
+ )
1089
+ ] });
1090
+ }
1091
+
1092
+ // src/components/SessionHistory.tsx
1093
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1094
+ function SessionHistory({ sessions, activeSessionId, onSelectSession, className }) {
1095
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("w-64 shrink-0 rounded-lg border border-white/10 bg-white/[0.02] flex flex-col overflow-hidden", className), children: [
1096
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "px-3 py-2 border-b border-white/10 text-xs font-medium text-white/40 uppercase tracking-wider", children: "Recent Sessions" }),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 overflow-auto scrollbar-thin", children: sessions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "px-3 py-6 text-center text-xs text-white/30", children: "No sessions yet" }) : sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1098
+ "button",
1099
+ {
1100
+ onClick: () => onSelectSession(s),
1101
+ className: cn(
1102
+ "w-full text-left px-3 py-2.5 border-b border-white/5 hover:bg-white/5 transition-colors",
1103
+ activeSessionId === s.id && "border-l-2 border-l-accent bg-accent/5"
1104
+ ),
1105
+ children: [
1106
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-xs font-medium text-white truncate", children: s.agentName }),
1107
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "text-[10px] text-white/30 mt-0.5", children: [
1108
+ s.createdAt ? new Date(s.createdAt).toLocaleDateString() : "No events",
1109
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "mx-1 text-white/15", children: "|" }),
1110
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: cn(
1111
+ s.status === "active" ? "text-green-400" : "text-white/30"
1112
+ ), children: s.status })
1113
+ ] })
1114
+ ]
1115
+ },
1116
+ s.id
1117
+ )) })
1118
+ ] });
1119
+ }
1120
+
1121
+ // src/components/Terminal.tsx
1122
+ var import_react10 = require("react");
1123
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1124
+ var levelColors = {
1125
+ stdout: "text-green-300",
1126
+ stderr: "text-red-400",
1127
+ system: "text-white/40"
1128
+ };
1129
+ function Terminal2({ logs, connected, onClear, className }) {
1130
+ const [filter, setFilter] = (0, import_react10.useState)("");
1131
+ const [showTimestamps, setShowTimestamps] = (0, import_react10.useState)(false);
1132
+ const [autoScroll, setAutoScroll] = (0, import_react10.useState)(true);
1133
+ const containerRef = (0, import_react10.useRef)(null);
1134
+ (0, import_react10.useEffect)(() => {
1135
+ if (autoScroll && containerRef.current) {
1136
+ containerRef.current.scrollTop = containerRef.current.scrollHeight;
1137
+ }
1138
+ }, [logs, autoScroll]);
1139
+ const handleScroll = (0, import_react10.useCallback)(() => {
1140
+ if (!containerRef.current) return;
1141
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
1142
+ const atBottom = scrollHeight - scrollTop - clientHeight < 40;
1143
+ setAutoScroll(atBottom);
1144
+ }, []);
1145
+ const scrollToBottom = () => {
1146
+ if (containerRef.current) {
1147
+ containerRef.current.scrollTop = containerRef.current.scrollHeight;
1148
+ setAutoScroll(true);
1149
+ }
1150
+ };
1151
+ const filteredLogs = filter ? logs.filter((l) => l.text.toLowerCase().includes(filter.toLowerCase())) : logs;
1152
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: cn("flex flex-col bg-[#0d1117] rounded-lg border border-white/10 overflow-hidden", className), children: [
1153
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 border-b border-white/10 px-3 py-2 bg-[#161b22]", children: [
1154
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Terminal, { className: "h-3.5 w-3.5 text-white/50" }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-xs font-medium text-white/60", children: "Terminal" }),
1156
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn(
1157
+ "ml-1 flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium",
1158
+ connected === true ? "text-green-400" : connected === false ? "text-red-400" : "text-white/30"
1159
+ ), children: connected === true ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1160
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Wifi, { className: "h-2.5 w-2.5" }),
1161
+ " Live"
1162
+ ] }) : connected === false ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1163
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(WifiOff, { className: "h-2.5 w-2.5" }),
1164
+ " Disconnected"
1165
+ ] }) : "Connecting..." }),
1166
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex-1" }),
1167
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-[10px] text-white/30", children: [
1168
+ filteredLogs.length.toLocaleString(),
1169
+ " line",
1170
+ filteredLogs.length !== 1 ? "s" : ""
1171
+ ] }),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1173
+ "button",
1174
+ {
1175
+ onClick: () => setShowTimestamps(!showTimestamps),
1176
+ className: cn(
1177
+ "rounded p-1 text-white/40 hover:bg-white/10 hover:text-white/70 transition-colors",
1178
+ showTimestamps && "bg-white/10 text-white/70"
1179
+ ),
1180
+ title: "Toggle timestamps",
1181
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Clock, { className: "h-3 w-3" })
1182
+ }
1183
+ ),
1184
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative", children: [
1185
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Search, { className: "absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-white/30" }),
1186
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1187
+ "input",
1188
+ {
1189
+ type: "text",
1190
+ value: filter,
1191
+ onChange: (e) => setFilter(e.target.value),
1192
+ placeholder: "Filter...",
1193
+ className: "h-6 w-32 rounded border border-white/10 bg-white/5 pl-7 pr-2 text-[11px] text-white placeholder:text-white/30 focus:border-white/20 focus:outline-none"
1194
+ }
1195
+ )
1196
+ ] }),
1197
+ !autoScroll && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1198
+ "button",
1199
+ {
1200
+ onClick: scrollToBottom,
1201
+ className: "rounded p-1 text-white/40 hover:bg-white/10 hover:text-white/70 transition-colors",
1202
+ title: "Scroll to bottom",
1203
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ArrowDown, { className: "h-3 w-3" })
1204
+ }
1205
+ ),
1206
+ onClear && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1207
+ "button",
1208
+ {
1209
+ onClick: onClear,
1210
+ className: "rounded p-1 text-white/40 hover:bg-white/10 hover:text-white/70 transition-colors",
1211
+ title: "Clear",
1212
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Trash2, { className: "h-3 w-3" })
1213
+ }
1214
+ )
1215
+ ] }),
1216
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1217
+ "div",
1218
+ {
1219
+ ref: containerRef,
1220
+ onScroll: handleScroll,
1221
+ className: "flex-1 overflow-auto p-3 font-mono text-xs leading-5 scrollbar-thin min-h-[120px]",
1222
+ children: filteredLogs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex h-full items-center justify-center text-white/20 text-xs", children: logs.length === 0 ? "Waiting for sandbox output..." : "No matching lines" }) : filteredLogs.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex hover:bg-white/5 rounded px-1 -mx-1", children: [
1223
+ showTimestamps && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "mr-3 shrink-0 select-none text-white/20", children: formatTimestamp(entry.ts) }),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: cn(
1225
+ "whitespace-pre-wrap break-all",
1226
+ levelColors[entry.level] || "text-white/60"
1227
+ ), children: entry.text })
1228
+ ] }, entry.index))
1229
+ }
1230
+ )
1231
+ ] });
1232
+ }
1233
+
1234
+ // src/components/FileBrowser.tsx
1235
+ var import_react11 = require("react");
1236
+
1237
+ // src/components/FileTree.tsx
1238
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1239
+ function TreeItem({
1240
+ node,
1241
+ depth,
1242
+ selectedPath,
1243
+ expandedDirs,
1244
+ onSelectFile,
1245
+ onToggleDir
1246
+ }) {
1247
+ const isExpanded = expandedDirs.has(node.path);
1248
+ const isSelected = selectedPath === node.path;
1249
+ const Icon = node.isDir ? isExpanded ? FolderOpen : Folder : getFileIcon(node.name);
1250
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1251
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1252
+ "button",
1253
+ {
1254
+ onClick: () => node.isDir ? onToggleDir(node.path) : onSelectFile(node.path),
1255
+ className: cn(
1256
+ "flex items-center gap-1.5 w-full text-left py-1 pr-2 text-xs transition-colors hover:bg-white/5",
1257
+ isSelected && !node.isDir && "bg-accent/10 text-accent",
1258
+ !isSelected && "text-white/60"
1259
+ ),
1260
+ style: { paddingLeft: `${depth * 16 + 8}px` },
1261
+ children: [
1262
+ node.isDir ? isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChevronDown, { className: "h-3 w-3 shrink-0 text-white/30" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ChevronRight, { className: "h-3 w-3 shrink-0 text-white/30" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-3" }),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { className: cn(
1264
+ "h-3.5 w-3.5 shrink-0",
1265
+ node.isDir ? "text-accent" : "text-white/40"
1266
+ ) }),
1267
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "truncate", children: node.name }),
1268
+ node.isDir && node.children.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "ml-auto text-[10px] text-white/20 shrink-0", children: node.children.length })
1269
+ ]
1270
+ }
1271
+ ),
1272
+ node.isDir && isExpanded && node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1273
+ TreeItem,
1274
+ {
1275
+ node: child,
1276
+ depth: depth + 1,
1277
+ selectedPath,
1278
+ expandedDirs,
1279
+ onSelectFile,
1280
+ onToggleDir
1281
+ },
1282
+ child.path
1283
+ ))
1284
+ ] });
1285
+ }
1286
+ function FileTree({ nodes, selectedPath, expandedDirs, onSelectFile, onToggleDir }) {
1287
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "overflow-y-auto scrollbar-thin", children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1288
+ TreeItem,
1289
+ {
1290
+ node,
1291
+ depth: 0,
1292
+ selectedPath,
1293
+ expandedDirs,
1294
+ onSelectFile,
1295
+ onToggleDir
1296
+ },
1297
+ node.path
1298
+ )) });
1299
+ }
1300
+
1301
+ // src/components/FileBrowser.tsx
1302
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1303
+ function FileBrowser({
1304
+ files,
1305
+ source,
1306
+ loading,
1307
+ selectedPath,
1308
+ fileContent,
1309
+ fileLoading,
1310
+ fileError,
1311
+ expandedDirs,
1312
+ filter,
1313
+ onFilterChange,
1314
+ onSelectFile,
1315
+ onToggleDir,
1316
+ onRefresh,
1317
+ fileBaseUrl,
1318
+ className
1319
+ }) {
1320
+ const contentRef = (0, import_react11.useRef)(null);
1321
+ if (loading) {
1322
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("flex items-center justify-center py-12", className), children: [
1323
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Loader2, { className: "mr-2 h-4 w-4 animate-spin text-white/40" }),
1324
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm text-white/50", children: "Loading files..." })
1325
+ ] });
1326
+ }
1327
+ if (files.length === 0) {
1328
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("flex flex-col items-center justify-center py-12 text-center", className), children: [
1329
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FolderOpen, { className: "mb-3 h-10 w-10 text-white/20" }),
1330
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm font-medium text-white/50", children: "No files in workspace" }),
1331
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "mt-1 text-xs text-white/40", children: "Files created by the agent will appear here" })
1332
+ ] });
1333
+ }
1334
+ const filteredFiles = filter ? files.filter((f) => f.path.toLowerCase().includes(filter.toLowerCase())) : files;
1335
+ const tree = buildFileTree(filteredFiles);
1336
+ const selectedFile = files.find((f) => f.path === selectedPath);
1337
+ const downloadUrl = fileBaseUrl && selectedPath ? `${fileBaseUrl}/${selectedPath}` : void 0;
1338
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: cn("flex flex-col h-full", className), children: [
1339
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 border-b border-white/10 px-3 py-2 shrink-0", children: [
1340
+ source && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: cn(
1341
+ "rounded-full px-2 py-0.5 text-[10px] font-medium border",
1342
+ source === "sandbox" ? "bg-green-500/10 text-green-400 border-green-500/20" : "bg-white/5 text-white/40 border-white/10"
1343
+ ), children: source }),
1344
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-xs text-white/40", children: [
1345
+ files.length,
1346
+ " files"
1347
+ ] }),
1348
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1" }),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative", children: [
1350
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Search, { className: "absolute left-2 top-1/2 h-3 w-3 -translate-y-1/2 text-white/30" }),
1351
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1352
+ "input",
1353
+ {
1354
+ type: "text",
1355
+ value: filter,
1356
+ onChange: (e) => onFilterChange(e.target.value),
1357
+ placeholder: "Filter...",
1358
+ className: "w-40 rounded-md border border-white/10 bg-white/5 pl-7 pr-2 py-1 text-xs text-white placeholder:text-white/30 focus:border-accent/50 focus:outline-none"
1359
+ }
1360
+ ),
1361
+ filter && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1362
+ "button",
1363
+ {
1364
+ onClick: () => onFilterChange(""),
1365
+ className: "absolute right-1.5 top-1/2 -translate-y-1/2 text-white/30 hover:text-white",
1366
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(X, { className: "h-3 w-3" })
1367
+ }
1368
+ )
1369
+ ] }),
1370
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1371
+ "button",
1372
+ {
1373
+ onClick: onRefresh,
1374
+ className: "rounded-md p-1 text-white/40 hover:bg-white/5 hover:text-white transition-colors",
1375
+ title: "Refresh",
1376
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(RefreshCw, { className: "h-3.5 w-3.5" })
1377
+ }
1378
+ )
1379
+ ] }),
1380
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 min-h-0", children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-64 shrink-0 border-r border-white/10 overflow-y-auto scrollbar-thin", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1382
+ FileTree,
1383
+ {
1384
+ nodes: tree,
1385
+ selectedPath,
1386
+ expandedDirs,
1387
+ onSelectFile,
1388
+ onToggleDir
1389
+ }
1390
+ ) }),
1391
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1 min-w-0 flex flex-col overflow-hidden", children: !selectedPath ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-1 items-center justify-center text-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1392
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FileText, { className: "mx-auto mb-2 h-8 w-8 text-white/15" }),
1393
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-white/30", children: "Select a file to view its contents" })
1394
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1395
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between border-b border-white/10 px-4 py-2 shrink-0", children: [
1396
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 min-w-0", children: [
1397
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-mono text-xs text-white/60 truncate", children: selectedPath }),
1398
+ selectedFile && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-white/30 shrink-0", children: formatBytes(selectedFile.size) })
1399
+ ] }),
1400
+ downloadUrl && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1401
+ "a",
1402
+ {
1403
+ href: downloadUrl,
1404
+ download: true,
1405
+ className: "rounded-md p-1.5 text-white/40 hover:bg-white/5 hover:text-white transition-colors shrink-0",
1406
+ title: "Download",
1407
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Download, { className: "h-3.5 w-3.5" })
1408
+ }
1409
+ )
1410
+ ] }),
1411
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1 overflow-auto bg-[#0d1117]", children: fileLoading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-center py-12", children: [
1412
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Loader2, { className: "mr-2 h-4 w-4 animate-spin text-white/40" }),
1413
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-xs text-white/50", children: "Loading..." })
1414
+ ] }) : fileError ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center justify-center py-12 text-center px-4", children: [
1415
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-xs text-white/40", children: fileError }),
1416
+ downloadUrl && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1417
+ "a",
1418
+ {
1419
+ href: downloadUrl,
1420
+ download: true,
1421
+ className: "mt-2 text-xs text-accent hover:underline",
1422
+ children: "Download file"
1423
+ }
1424
+ )
1425
+ ] }) : isImageFile(selectedPath) ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center justify-center p-4", children: downloadUrl && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1426
+ "img",
1427
+ {
1428
+ src: downloadUrl,
1429
+ alt: selectedPath,
1430
+ className: "max-w-full max-h-[60vh] object-contain rounded"
1431
+ }
1432
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1433
+ "pre",
1434
+ {
1435
+ ref: contentRef,
1436
+ className: "p-4 text-xs font-mono text-white/70 leading-relaxed whitespace-pre-wrap break-words",
1437
+ children: fileContent
1438
+ }
1439
+ ) })
1440
+ ] }) })
1441
+ ] })
1442
+ ] });
1443
+ }
1444
+
1445
+ // src/components/BottomPanels.tsx
1446
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1447
+ function BottomPanels({
1448
+ terminalOpen,
1449
+ onToggleTerminal,
1450
+ filesOpen,
1451
+ onToggleFiles,
1452
+ className
1453
+ }) {
1454
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: cn("flex border-t border-white/10 bg-[#161b22] rounded-b-lg", className), children: [
1455
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1456
+ "button",
1457
+ {
1458
+ onClick: onToggleTerminal,
1459
+ className: cn(
1460
+ "flex flex-1 items-center justify-center gap-1.5 py-1 text-[10px] font-medium transition-colors",
1461
+ terminalOpen ? "text-accent hover:text-accent/80" : "text-white/40 hover:text-white/60 hover:bg-[#1c2128]"
1462
+ ),
1463
+ children: [
1464
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Terminal, { className: "h-3 w-3" }),
1465
+ "Terminal",
1466
+ terminalOpen ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronDown, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronUp, { className: "h-3 w-3" })
1467
+ ]
1468
+ }
1469
+ ),
1470
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-px bg-white/10" }),
1471
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1472
+ "button",
1473
+ {
1474
+ onClick: onToggleFiles,
1475
+ className: cn(
1476
+ "flex flex-1 items-center justify-center gap-1.5 py-1 text-[10px] font-medium transition-colors",
1477
+ filesOpen ? "text-accent hover:text-accent/80" : "text-white/40 hover:text-white/60 hover:bg-[#1c2128]"
1478
+ ),
1479
+ children: [
1480
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(FolderOpen, { className: "h-3 w-3" }),
1481
+ "Files",
1482
+ filesOpen ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronDown, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronUp, { className: "h-3 w-3" })
1483
+ ]
1484
+ }
1485
+ )
1486
+ ] });
1487
+ }
1488
+
1489
+ // src/hooks/useTerminal.ts
1490
+ var import_react12 = require("react");
1491
+ function useTerminal({
1492
+ client,
1493
+ sessionId,
1494
+ historical,
1495
+ pollInterval = 500
1496
+ }) {
1497
+ const [logs, setLogs] = (0, import_react12.useState)([]);
1498
+ const [connected, setConnected] = (0, import_react12.useState)(null);
1499
+ const lastIndexRef = (0, import_react12.useRef)(-1);
1500
+ const pollingRef = (0, import_react12.useRef)(null);
1501
+ const fetchLogs = (0, import_react12.useCallback)(async () => {
1502
+ try {
1503
+ const afterOpt = lastIndexRef.current >= 0 ? { after: lastIndexRef.current } : void 0;
1504
+ const data = await client.getSessionLogs(sessionId, afterOpt);
1505
+ setConnected(true);
1506
+ if (data.logs && data.logs.length > 0) {
1507
+ setLogs((prev) => [...prev, ...data.logs]);
1508
+ lastIndexRef.current = data.logs[data.logs.length - 1].index;
1509
+ }
1510
+ } catch {
1511
+ setConnected(false);
1512
+ }
1513
+ }, [client, sessionId]);
1514
+ (0, import_react12.useEffect)(() => {
1515
+ setLogs([]);
1516
+ lastIndexRef.current = -1;
1517
+ setConnected(null);
1518
+ fetchLogs();
1519
+ if (!historical) {
1520
+ pollingRef.current = setInterval(fetchLogs, pollInterval);
1521
+ return () => {
1522
+ if (pollingRef.current) clearInterval(pollingRef.current);
1523
+ };
1524
+ }
1525
+ }, [sessionId, historical, pollInterval, fetchLogs]);
1526
+ const clearLogs = (0, import_react12.useCallback)(() => {
1527
+ setLogs([]);
1528
+ }, []);
1529
+ return { logs, connected, clearLogs };
1530
+ }
1531
+
1532
+ // src/hooks/useFileBrowser.ts
1533
+ var import_react13 = require("react");
1534
+ function useFileBrowser({
1535
+ client,
1536
+ sessionId
1537
+ }) {
1538
+ const [files, setFiles] = (0, import_react13.useState)([]);
1539
+ const [source, setSource] = (0, import_react13.useState)(null);
1540
+ const [loading, setLoading] = (0, import_react13.useState)(true);
1541
+ const [selectedPath, setSelectedPath] = (0, import_react13.useState)(null);
1542
+ const [fileContent, setFileContent] = (0, import_react13.useState)(null);
1543
+ const [fileLoading, setFileLoading] = (0, import_react13.useState)(false);
1544
+ const [fileError, setFileError] = (0, import_react13.useState)(null);
1545
+ const [expandedDirs, setExpandedDirs] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
1546
+ const [filter, setFilter] = (0, import_react13.useState)("");
1547
+ const fetchFiles = (0, import_react13.useCallback)(async () => {
1548
+ setLoading(true);
1549
+ try {
1550
+ const data = await client.getSessionFiles(sessionId);
1551
+ setFiles(data.files || []);
1552
+ setSource(data.source || null);
1553
+ const topDirs = /* @__PURE__ */ new Set();
1554
+ for (const f of data.files || []) {
1555
+ const first = f.path.split("/")[0];
1556
+ if (f.path.includes("/")) topDirs.add(first);
1557
+ }
1558
+ setExpandedDirs(topDirs);
1559
+ } catch {
1560
+ } finally {
1561
+ setLoading(false);
1562
+ }
1563
+ }, [client, sessionId]);
1564
+ (0, import_react13.useEffect)(() => {
1565
+ fetchFiles();
1566
+ }, [fetchFiles]);
1567
+ const selectFile = (0, import_react13.useCallback)(async (path) => {
1568
+ setSelectedPath(path);
1569
+ setFileContent(null);
1570
+ setFileError(null);
1571
+ const ext = path.split(".").pop()?.toLowerCase();
1572
+ const imageExts = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico"]);
1573
+ if (imageExts.has(ext || "")) return;
1574
+ setFileLoading(true);
1575
+ try {
1576
+ const data = await client.getSessionFile(sessionId, path);
1577
+ setFileContent(data.content || "");
1578
+ } catch (err) {
1579
+ setFileError("Failed to load file content");
1580
+ } finally {
1581
+ setFileLoading(false);
1582
+ }
1583
+ }, [client, sessionId]);
1584
+ const toggleDir = (0, import_react13.useCallback)((path) => {
1585
+ setExpandedDirs((prev) => {
1586
+ const next = new Set(prev);
1587
+ if (next.has(path)) next.delete(path);
1588
+ else next.add(path);
1589
+ return next;
1590
+ });
1591
+ }, []);
1592
+ return {
1593
+ files,
1594
+ source,
1595
+ loading,
1596
+ selectedPath,
1597
+ fileContent,
1598
+ fileLoading,
1599
+ fileError,
1600
+ expandedDirs,
1601
+ filter,
1602
+ setFilter,
1603
+ selectFile,
1604
+ toggleDir,
1605
+ refresh: fetchFiles
1606
+ };
1607
+ }
1608
+
1609
+ // src/components/Playground.tsx
1610
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1611
+ function PlaygroundInner({ className }) {
1612
+ const ctx = usePlaygroundContext();
1613
+ const {
1614
+ client,
1615
+ chat,
1616
+ agents,
1617
+ sessions,
1618
+ health,
1619
+ fileUpload,
1620
+ selectedAgent,
1621
+ setSelectedAgent,
1622
+ showHistory,
1623
+ setShowHistory,
1624
+ terminalOpen,
1625
+ setTerminalOpen,
1626
+ filesOpen,
1627
+ setFilesOpen
1628
+ } = ctx;
1629
+ const selectedAgentObj = agents.agents.find((a) => (a.slug || a.name) === selectedAgent);
1630
+ const isActive = chat.sessionId !== null;
1631
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: cn("flex flex-col", className), children: [
1632
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1633
+ PlaygroundHeader,
1634
+ {
1635
+ agents: agents.agents,
1636
+ selectedAgent,
1637
+ onAgentChange: setSelectedAgent,
1638
+ runtimeConnected: health.connected,
1639
+ showHistory,
1640
+ onToggleHistory: () => setShowHistory(!showHistory),
1641
+ sessionId: chat.sessionId,
1642
+ onNewChat: chat.startNewChat
1643
+ }
1644
+ ),
1645
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-1 gap-4 min-h-0", children: [
1646
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1647
+ SessionHistory,
1648
+ {
1649
+ sessions: sessions.sessions,
1650
+ activeSessionId: chat.sessionId,
1651
+ onSelectSession: (s) => {
1652
+ chat.loadSession(s.id);
1653
+ setShowHistory(false);
1654
+ }
1655
+ }
1656
+ ),
1657
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex-1 flex flex-col min-w-0 gap-0", children: [
1658
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1659
+ Chat,
1660
+ {
1661
+ messages: chat.messages,
1662
+ input: chat.input,
1663
+ onInputChange: chat.setInput,
1664
+ onSend: chat.send,
1665
+ sending: chat.sending,
1666
+ loading: chat.loading,
1667
+ error: chat.error,
1668
+ isActive,
1669
+ agentName: selectedAgentObj?.name,
1670
+ attachedFiles: chat.attachedFiles,
1671
+ onFileSelect: fileUpload.handleFileSelect,
1672
+ onRemoveFile: fileUpload.removeFile,
1673
+ className: cn(
1674
+ "rounded-lg border border-white/10 bg-white/[0.02]",
1675
+ terminalOpen && chat.sessionId ? "flex-1 min-h-0" : "flex-1"
1676
+ )
1677
+ }
1678
+ ),
1679
+ chat.sessionId && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: cn(
1680
+ "shrink-0 transition-all",
1681
+ terminalOpen ? "h-64" : "h-0"
1682
+ ), children: terminalOpen && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1683
+ TerminalPanel,
1684
+ {
1685
+ client,
1686
+ sessionId: chat.sessionId
1687
+ }
1688
+ ) }),
1689
+ chat.sessionId && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: cn(
1690
+ "shrink-0 transition-all",
1691
+ filesOpen ? "h-72" : "h-0"
1692
+ ), children: filesOpen && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1693
+ FileBrowserPanel,
1694
+ {
1695
+ client,
1696
+ sessionId: chat.sessionId
1697
+ }
1698
+ ) }),
1699
+ chat.sessionId && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1700
+ BottomPanels,
1701
+ {
1702
+ terminalOpen,
1703
+ onToggleTerminal: () => setTerminalOpen(!terminalOpen),
1704
+ filesOpen,
1705
+ onToggleFiles: () => setFilesOpen(!filesOpen)
1706
+ }
1707
+ )
1708
+ ] })
1709
+ ] })
1710
+ ] });
1711
+ }
1712
+ function TerminalPanel({ client, sessionId }) {
1713
+ const { logs, connected, clearLogs } = useTerminal({ client, sessionId });
1714
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1715
+ Terminal2,
1716
+ {
1717
+ logs,
1718
+ connected,
1719
+ onClear: clearLogs,
1720
+ className: "h-full rounded-t-none border-t-0"
1721
+ }
1722
+ );
1723
+ }
1724
+ function FileBrowserPanel({ client, sessionId }) {
1725
+ const fb = useFileBrowser({ client, sessionId });
1726
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1727
+ FileBrowser,
1728
+ {
1729
+ files: fb.files,
1730
+ source: fb.source,
1731
+ loading: fb.loading,
1732
+ selectedPath: fb.selectedPath,
1733
+ fileContent: fb.fileContent,
1734
+ fileLoading: fb.fileLoading,
1735
+ fileError: fb.fileError,
1736
+ expandedDirs: fb.expandedDirs,
1737
+ filter: fb.filter,
1738
+ onFilterChange: fb.setFilter,
1739
+ onSelectFile: fb.selectFile,
1740
+ onToggleDir: fb.toggleDir,
1741
+ onRefresh: fb.refresh,
1742
+ className: "h-full border-t border-white/10"
1743
+ }
1744
+ );
1745
+ }
1746
+ function Playground({ client, defaultAgent, className }) {
1747
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PlaygroundProvider, { client, defaultAgent, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(PlaygroundInner, { className }) });
1748
+ }
1749
+ // Annotate the CommonJS export names for ESM import in node:
1750
+ 0 && (module.exports = {
1751
+ BottomPanels,
1752
+ Chat,
1753
+ ChatInput,
1754
+ ChatMessage,
1755
+ ChatMessages,
1756
+ FileBrowser,
1757
+ FileTree,
1758
+ Playground,
1759
+ PlaygroundHeader,
1760
+ PlaygroundProvider,
1761
+ SessionHistory,
1762
+ StatusIndicator,
1763
+ Terminal,
1764
+ ToolCallBlock,
1765
+ buildFileTree,
1766
+ cn,
1767
+ formatBytes,
1768
+ formatTime,
1769
+ getFileLanguage,
1770
+ isImageFile,
1771
+ parseContentBlocks,
1772
+ useAgents,
1773
+ useFileBrowser,
1774
+ useFileUpload,
1775
+ useHealthCheck,
1776
+ usePlaygroundChat,
1777
+ usePlaygroundContext,
1778
+ useSessions,
1779
+ useTerminal
1780
+ });
1781
+ //# sourceMappingURL=index.cjs.map