@ash-cloud/ash-ui 0.0.6 → 0.0.7

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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useState, useRef, useEffect, useMemo, useCallback, useContext } from 'react';
1
+ import { createContext, useState, useMemo, useCallback, useContext, useRef, useEffect } from 'react';
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import ReactMarkdown from 'react-markdown';
4
4
 
@@ -35,27 +35,41 @@ function mapToolToActionType(toolName, input) {
35
35
  command: inputObj.command || "",
36
36
  description: inputObj.description
37
37
  };
38
- case "Read":
38
+ case "Read": {
39
+ const limit = inputObj.limit;
39
40
  return {
40
41
  action: "file_read",
41
42
  path: inputObj.file_path || "",
42
43
  offset: inputObj.offset,
43
- limit: inputObj.limit
44
+ limit,
45
+ linesRead: limit
46
+ // Use limit as approximate lines read if specified
44
47
  };
45
- case "Edit":
48
+ }
49
+ case "Edit": {
50
+ const oldStr = inputObj.old_string;
51
+ const newStr = inputObj.new_string;
52
+ const oldLines = oldStr ? oldStr.split("\n").length : 0;
53
+ const newLines = newStr ? newStr.split("\n").length : 0;
46
54
  return {
47
55
  action: "file_edit",
48
56
  path: inputObj.file_path || "",
49
- oldString: inputObj.old_string,
50
- newString: inputObj.new_string,
51
- replaceAll: inputObj.replace_all
57
+ oldString: oldStr,
58
+ newString: newStr,
59
+ replaceAll: inputObj.replace_all,
60
+ linesAdded: newLines,
61
+ linesRemoved: oldLines
52
62
  };
53
- case "Write":
63
+ }
64
+ case "Write": {
65
+ const content = inputObj.content;
54
66
  return {
55
67
  action: "file_write",
56
68
  path: inputObj.file_path || "",
57
- content: inputObj.content
69
+ content,
70
+ linesWritten: content ? content.split("\n").length : void 0
58
71
  };
72
+ }
59
73
  case "Grep":
60
74
  return {
61
75
  action: "search",
@@ -1304,275 +1318,422 @@ function StreamingText({
1304
1318
  isStreaming && /* @__PURE__ */ jsx(LoadingIndicator, { variant: "cursor", size: "sm", className: "inline-block ml-0.5" })
1305
1319
  ] }) });
1306
1320
  }
1307
- function CompactToolStatusLine({
1308
- toolCall,
1309
- previousToolCall,
1310
- animationDuration = 300,
1311
- className
1312
- }) {
1313
- const [isAnimating, setIsAnimating] = useState(false);
1314
- const [displayedToolCall, setDisplayedToolCall] = useState(toolCall);
1315
- const [exitingToolCall, setExitingToolCall] = useState(null);
1316
- const prevToolCallRef = useRef(null);
1317
- useEffect(() => {
1318
- if (toolCall.id !== prevToolCallRef.current) {
1319
- if (prevToolCallRef.current !== null && previousToolCall) {
1320
- setExitingToolCall(previousToolCall);
1321
- setIsAnimating(true);
1322
- const timer = setTimeout(() => {
1323
- setDisplayedToolCall(toolCall);
1324
- setExitingToolCall(null);
1325
- setIsAnimating(false);
1326
- }, animationDuration);
1327
- prevToolCallRef.current = toolCall.id;
1328
- return () => clearTimeout(timer);
1329
- } else {
1330
- setDisplayedToolCall(toolCall);
1331
- prevToolCallRef.current = toolCall.id;
1321
+ function getFilePath(actionType) {
1322
+ switch (actionType.action) {
1323
+ case "file_read":
1324
+ case "file_edit":
1325
+ case "file_write":
1326
+ return actionType.path;
1327
+ default:
1328
+ return null;
1329
+ }
1330
+ }
1331
+ function getFileName(path) {
1332
+ const parts = path.split("/");
1333
+ return parts[parts.length - 1] || path;
1334
+ }
1335
+ function getFileExtension(path) {
1336
+ const fileName = getFileName(path);
1337
+ const dotIndex = fileName.lastIndexOf(".");
1338
+ if (dotIndex === -1) return null;
1339
+ return fileName.slice(dotIndex + 1).toLowerCase();
1340
+ }
1341
+ function getDiffStats(actionType) {
1342
+ switch (actionType.action) {
1343
+ case "file_edit": {
1344
+ const edit = actionType;
1345
+ if (edit.linesAdded !== void 0 || edit.linesRemoved !== void 0) {
1346
+ return { added: edit.linesAdded, removed: edit.linesRemoved };
1332
1347
  }
1333
- } else {
1334
- setDisplayedToolCall(toolCall);
1348
+ return null;
1335
1349
  }
1336
- return void 0;
1337
- }, [toolCall, previousToolCall, animationDuration]);
1338
- const statusClasses = {
1339
- pending: "border-yellow-500/30",
1340
- success: "border-[var(--ash-accent)]/30",
1341
- failed: "border-red-500/30"
1342
- };
1343
- const renderToolCallContent = (tc, isExiting) => /* @__PURE__ */ jsxs(
1350
+ case "file_read": {
1351
+ const read = actionType;
1352
+ if (read.linesRead !== void 0) {
1353
+ return { read: read.linesRead };
1354
+ }
1355
+ return null;
1356
+ }
1357
+ case "file_write": {
1358
+ const write = actionType;
1359
+ if (write.linesWritten !== void 0) {
1360
+ return { written: write.linesWritten };
1361
+ }
1362
+ return null;
1363
+ }
1364
+ default:
1365
+ return null;
1366
+ }
1367
+ }
1368
+ function getFileTypeColor(ext) {
1369
+ switch (ext) {
1370
+ case "ts":
1371
+ case "tsx":
1372
+ return "text-blue-400";
1373
+ case "js":
1374
+ case "jsx":
1375
+ return "text-yellow-400";
1376
+ case "md":
1377
+ return "text-white/60";
1378
+ case "json":
1379
+ return "text-orange-400";
1380
+ case "sh":
1381
+ return "text-green-400";
1382
+ case "css":
1383
+ case "scss":
1384
+ return "text-pink-400";
1385
+ case "py":
1386
+ return "text-blue-300";
1387
+ default:
1388
+ return "text-white/70";
1389
+ }
1390
+ }
1391
+ function CompactToolRow({ toolCall, showFullPath = false, className }) {
1392
+ const { actionType, status, summary } = toolCall;
1393
+ const label = getActionLabel(actionType);
1394
+ const filePath = getFilePath(actionType);
1395
+ const diffStats = getDiffStats(actionType);
1396
+ const displayPath = filePath ? showFullPath ? filePath : getFileName(filePath) : null;
1397
+ const ext = filePath ? getFileExtension(filePath) : null;
1398
+ const fileColor = getFileTypeColor(ext);
1399
+ const showSummary = !filePath && summary;
1400
+ return /* @__PURE__ */ jsxs(
1344
1401
  "div",
1345
1402
  {
1346
1403
  className: cn(
1347
- "flex items-center gap-3 px-4 py-2.5",
1348
- isExiting ? "ash-status-line-exit" : isAnimating ? "ash-status-line-enter" : ""
1404
+ "flex items-center gap-2 py-1.5 text-sm min-w-0",
1405
+ className
1349
1406
  ),
1350
- style: {
1351
- animationDuration: `${animationDuration}ms`
1352
- },
1353
1407
  children: [
1354
- /* @__PURE__ */ jsx(
1355
- "div",
1356
- {
1357
- className: cn(
1358
- "w-6 h-6 rounded-lg flex items-center justify-center shrink-0",
1359
- tc.status === "pending" ? "bg-yellow-500/20" : tc.status === "failed" ? "bg-red-500/20" : "bg-[var(--ash-accent)]/20"
1360
- ),
1361
- children: /* @__PURE__ */ jsx(
1362
- ActionIcon,
1363
- {
1364
- actionType: tc.actionType,
1365
- className: cn(
1366
- "w-3.5 h-3.5",
1367
- tc.status === "pending" ? "text-yellow-400" : tc.status === "failed" ? "text-red-400" : "text-[var(--ash-accent)]"
1368
- )
1369
- }
1370
- )
1371
- }
1372
- ),
1373
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-white shrink-0", children: getActionLabel(tc.actionType) }),
1374
- /* @__PURE__ */ jsx("span", { className: "font-mono text-sm truncate text-white/60 flex-1 min-w-0", children: tc.summary }),
1375
- /* @__PURE__ */ jsx(StatusIndicator, { status: tc.status, size: "sm" })
1408
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
1409
+ /* @__PURE__ */ jsx(
1410
+ ActionIcon,
1411
+ {
1412
+ actionType,
1413
+ className: cn(
1414
+ "w-3.5 h-3.5",
1415
+ status === "pending" ? "text-yellow-400" : status === "failed" ? "text-red-400" : "text-white/50"
1416
+ )
1417
+ }
1418
+ ),
1419
+ /* @__PURE__ */ jsx("span", { className: cn(
1420
+ "font-medium",
1421
+ status === "pending" ? "text-white/90" : status === "failed" ? "text-red-400" : "text-white/60"
1422
+ ), children: label })
1423
+ ] }),
1424
+ displayPath && /* @__PURE__ */ jsx("code", { className: cn(
1425
+ "px-1.5 py-0.5 rounded bg-white/5 font-mono text-xs truncate max-w-[200px]",
1426
+ fileColor
1427
+ ), children: displayPath }),
1428
+ diffStats && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0 font-mono", children: [
1429
+ diffStats.added !== void 0 && diffStats.added > 0 && /* @__PURE__ */ jsxs("span", { className: "text-emerald-400", children: [
1430
+ "+",
1431
+ diffStats.added
1432
+ ] }),
1433
+ diffStats.removed !== void 0 && diffStats.removed > 0 && /* @__PURE__ */ jsxs("span", { className: "text-red-400", children: [
1434
+ "-",
1435
+ diffStats.removed
1436
+ ] }),
1437
+ diffStats.read !== void 0 && /* @__PURE__ */ jsxs("span", { className: "text-white/40", children: [
1438
+ diffStats.read,
1439
+ " lines"
1440
+ ] }),
1441
+ diffStats.written !== void 0 && /* @__PURE__ */ jsxs("span", { className: "text-emerald-400", children: [
1442
+ "+",
1443
+ diffStats.written
1444
+ ] })
1445
+ ] }),
1446
+ showSummary && /* @__PURE__ */ jsx("span", { className: "text-white/40 truncate min-w-0 text-xs", children: summary })
1376
1447
  ]
1377
1448
  }
1378
1449
  );
1450
+ }
1451
+ function getFileExtension2(path) {
1452
+ const fileName = path.split("/").pop() || path;
1453
+ const dotIndex = fileName.lastIndexOf(".");
1454
+ if (dotIndex === -1) return null;
1455
+ return fileName.slice(dotIndex + 1).toLowerCase();
1456
+ }
1457
+ function getFileIcon(ext) {
1458
+ switch (ext) {
1459
+ case "ts":
1460
+ case "tsx":
1461
+ return "TS";
1462
+ case "js":
1463
+ case "jsx":
1464
+ return "JS";
1465
+ case "md":
1466
+ return "MD";
1467
+ case "json":
1468
+ return "{}";
1469
+ case "sh":
1470
+ return "$";
1471
+ case "css":
1472
+ case "scss":
1473
+ return "#";
1474
+ case "py":
1475
+ return "PY";
1476
+ default:
1477
+ return "";
1478
+ }
1479
+ }
1480
+ function getFileBgColor(ext) {
1481
+ switch (ext) {
1482
+ case "ts":
1483
+ case "tsx":
1484
+ return "bg-blue-500/20";
1485
+ case "js":
1486
+ case "jsx":
1487
+ return "bg-yellow-500/20";
1488
+ case "md":
1489
+ return "bg-white/10";
1490
+ case "json":
1491
+ return "bg-orange-500/20";
1492
+ case "sh":
1493
+ return "bg-green-500/20";
1494
+ case "css":
1495
+ case "scss":
1496
+ return "bg-pink-500/20";
1497
+ case "py":
1498
+ return "bg-blue-400/20";
1499
+ default:
1500
+ return "bg-white/10";
1501
+ }
1502
+ }
1503
+ function FileBadge({
1504
+ path,
1505
+ linesAdded,
1506
+ linesRemoved,
1507
+ showOnlyFilename = true,
1508
+ className
1509
+ }) {
1510
+ const fileName = showOnlyFilename ? path.split("/").pop() || path : path;
1511
+ const ext = getFileExtension2(path);
1512
+ const icon = getFileIcon(ext);
1513
+ const bgColor = getFileBgColor(ext);
1514
+ const hasDiff = linesAdded !== void 0 && linesAdded > 0 || linesRemoved !== void 0 && linesRemoved > 0;
1379
1515
  return /* @__PURE__ */ jsxs(
1380
- "div",
1516
+ "span",
1381
1517
  {
1382
1518
  className: cn(
1383
- "relative rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
1384
- statusClasses[displayedToolCall.status],
1385
- displayedToolCall.status === "pending" && "ash-tool-status-pending",
1519
+ "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-xs font-mono",
1520
+ bgColor,
1386
1521
  className
1387
1522
  ),
1388
1523
  children: [
1389
- exitingToolCall && /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children: renderToolCallContent(exitingToolCall, true) }),
1390
- renderToolCallContent(displayedToolCall, false)
1524
+ icon && /* @__PURE__ */ jsx("span", { className: "text-[10px] opacity-60 font-semibold", children: icon }),
1525
+ /* @__PURE__ */ jsx("span", { className: "text-white/80 truncate max-w-[120px]", children: fileName }),
1526
+ hasDiff && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5", children: [
1527
+ linesAdded !== void 0 && linesAdded > 0 && /* @__PURE__ */ jsxs("span", { className: "text-emerald-400", children: [
1528
+ "+",
1529
+ linesAdded
1530
+ ] }),
1531
+ linesRemoved !== void 0 && linesRemoved > 0 && /* @__PURE__ */ jsxs("span", { className: "text-red-400", children: [
1532
+ "-",
1533
+ linesRemoved
1534
+ ] })
1535
+ ] })
1391
1536
  ]
1392
1537
  }
1393
1538
  );
1394
1539
  }
1395
- function calculateGroupStatus(toolCalls) {
1396
- const hasPending = toolCalls.some((tc) => tc.status === "pending");
1397
- const hasFailed = toolCalls.some((tc) => tc.status === "failed");
1398
- const allSuccess = toolCalls.every((tc) => tc.status === "success");
1399
- if (hasPending) return "pending";
1400
- if (allSuccess) return "success";
1401
- if (hasFailed && !hasPending) return toolCalls.some((tc) => tc.status === "success") ? "partial_failure" : "failed";
1402
- return "pending";
1540
+ function extractFileChanges(toolCalls) {
1541
+ const fileMap = /* @__PURE__ */ new Map();
1542
+ for (const tc of toolCalls) {
1543
+ const { actionType } = tc;
1544
+ if (actionType.action === "file_edit") {
1545
+ const edit = actionType;
1546
+ const existing = fileMap.get(edit.path);
1547
+ if (existing) {
1548
+ existing.linesAdded = (existing.linesAdded || 0) + (edit.linesAdded || 0);
1549
+ existing.linesRemoved = (existing.linesRemoved || 0) + (edit.linesRemoved || 0);
1550
+ } else {
1551
+ fileMap.set(edit.path, {
1552
+ path: edit.path,
1553
+ linesAdded: edit.linesAdded,
1554
+ linesRemoved: edit.linesRemoved
1555
+ });
1556
+ }
1557
+ } else if (actionType.action === "file_write") {
1558
+ const write = actionType;
1559
+ if (!fileMap.has(write.path)) {
1560
+ fileMap.set(write.path, {
1561
+ path: write.path,
1562
+ linesAdded: write.linesWritten
1563
+ });
1564
+ }
1565
+ }
1566
+ }
1567
+ return Array.from(fileMap.values());
1568
+ }
1569
+ function countActionTypes(toolCalls) {
1570
+ const counts = {};
1571
+ for (const tc of toolCalls) {
1572
+ const action = tc.actionType.action;
1573
+ counts[action] = (counts[action] || 0) + 1;
1574
+ }
1575
+ return counts;
1576
+ }
1577
+ function getActionIconComponent(action) {
1578
+ switch (action) {
1579
+ case "file_read":
1580
+ return FileIcon;
1581
+ case "file_edit":
1582
+ case "file_write":
1583
+ return EditIcon;
1584
+ case "command_run":
1585
+ return TerminalIcon;
1586
+ case "search":
1587
+ case "glob":
1588
+ return SearchIcon;
1589
+ default:
1590
+ return null;
1591
+ }
1403
1592
  }
1404
1593
  function ToolExecutionGroup({
1405
1594
  toolCalls,
1406
1595
  defaultExpanded = false,
1407
- animationDuration = 300,
1408
1596
  className
1409
1597
  }) {
1410
1598
  const [expanded, setExpanded] = useState(defaultExpanded);
1411
- const activeToolCall = toolCalls[toolCalls.length - 1];
1412
- const previousToolCall = toolCalls.length > 1 ? toolCalls[toolCalls.length - 2] : null;
1413
- const groupStatus = useMemo(() => calculateGroupStatus(toolCalls), [toolCalls]);
1414
- const completedCount = toolCalls.filter((tc) => tc.status === "success").length;
1415
- const failedCount = toolCalls.filter((tc) => tc.status === "failed").length;
1599
+ const [expandedCardId, setExpandedCardId] = useState(null);
1600
+ const fileChanges = useMemo(() => extractFileChanges(toolCalls), [toolCalls]);
1601
+ const actionCounts = useMemo(() => countActionTypes(toolCalls), [toolCalls]);
1602
+ const displayActions = useMemo(() => {
1603
+ return Object.entries(actionCounts).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([action]) => action);
1604
+ }, [actionCounts]);
1416
1605
  const totalCount = toolCalls.length;
1417
- if (!activeToolCall) {
1606
+ if (toolCalls.length === 0) {
1418
1607
  return null;
1419
1608
  }
1420
- const borderClasses = {
1421
- pending: "border-yellow-500/30",
1422
- success: "border-[var(--ash-accent)]/30",
1423
- partial_failure: "border-orange-500/30",
1424
- failed: "border-red-500/30"
1425
- };
1426
- return /* @__PURE__ */ jsxs(
1427
- "div",
1428
- {
1429
- className: cn(
1430
- "rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden ash-animate-fade-in",
1431
- borderClasses[groupStatus],
1432
- groupStatus === "pending" && "ash-tool-status-pending",
1433
- className
1434
- ),
1435
- children: [
1436
- /* @__PURE__ */ jsx(
1437
- "button",
1438
- {
1439
- onClick: () => setExpanded(!expanded),
1440
- className: cn(
1441
- "w-full transition-colors hover:bg-white/5 cursor-pointer"
1442
- ),
1443
- children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1444
- /* @__PURE__ */ jsx(
1445
- CompactToolStatusLine,
1446
- {
1447
- toolCall: activeToolCall,
1448
- previousToolCall,
1449
- animationDuration,
1450
- className: "border-0 rounded-none"
1451
- }
1452
- ),
1453
- /* @__PURE__ */ jsxs("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-2", children: [
1454
- totalCount > 1 && /* @__PURE__ */ jsx(
1455
- "div",
1456
- {
1457
- className: cn(
1458
- "flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium",
1459
- groupStatus === "pending" ? "bg-yellow-500/20 text-yellow-400" : groupStatus === "success" ? "bg-[var(--ash-accent)]/20 text-[var(--ash-accent)]" : groupStatus === "failed" ? "bg-red-500/20 text-red-400" : "bg-orange-500/20 text-orange-400"
1460
- ),
1461
- children: groupStatus === "pending" ? /* @__PURE__ */ jsxs("span", { children: [
1462
- completedCount,
1463
- "/",
1464
- totalCount
1465
- ] }) : failedCount > 0 ? /* @__PURE__ */ jsxs("span", { children: [
1466
- completedCount,
1467
- " ok, ",
1468
- failedCount,
1469
- " failed"
1470
- ] }) : /* @__PURE__ */ jsxs("span", { children: [
1471
- totalCount,
1472
- " tools"
1473
- ] })
1474
- }
1475
- ),
1476
- /* @__PURE__ */ jsx(
1477
- ChevronDownIcon,
1478
- {
1479
- className: cn(
1480
- "w-4 h-4 text-white/40 transition-transform duration-200",
1481
- expanded && "rotate-180"
1482
- )
1483
- }
1484
- )
1485
- ] })
1486
- ] })
1487
- }
1488
- ),
1489
- expanded && /* @__PURE__ */ jsxs("div", { className: "border-t border-white/5 bg-black/20", children: [
1490
- /* @__PURE__ */ jsx("div", { className: "p-3 space-y-2", children: toolCalls.map((toolCall, index) => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1491
- index < toolCalls.length - 1 && /* @__PURE__ */ jsx("div", { className: "absolute left-[19px] top-10 bottom-0 w-px bg-white/10" }),
1492
- /* @__PURE__ */ jsx(
1493
- ToolCallCard,
1494
- {
1495
- toolCall,
1496
- defaultExpanded: false,
1497
- className: "relative z-10"
1498
- }
1499
- )
1500
- ] }, toolCall.id)) }),
1501
- groupStatus !== "pending" && /* @__PURE__ */ jsx(
1502
- "div",
1609
+ return /* @__PURE__ */ jsxs("div", { className: cn("ash-animate-fade-in", className), children: [
1610
+ /* @__PURE__ */ jsxs(
1611
+ "button",
1612
+ {
1613
+ onClick: () => setExpanded(!expanded),
1614
+ className: "w-full flex items-center gap-2 py-1 text-left group",
1615
+ children: [
1616
+ /* @__PURE__ */ jsx(
1617
+ ChevronRightIcon,
1503
1618
  {
1504
1619
  className: cn(
1505
- "px-4 py-3 border-t border-white/5 flex items-center gap-2",
1506
- groupStatus === "success" ? "bg-[var(--ash-accent)]/5" : groupStatus === "failed" ? "bg-red-500/5" : "bg-orange-500/5"
1507
- ),
1508
- children: groupStatus === "success" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1509
- /* @__PURE__ */ jsx(CheckIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }),
1510
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-[var(--ash-accent)] font-medium", children: [
1511
- "All ",
1512
- totalCount,
1513
- " tool",
1514
- totalCount !== 1 ? "s" : "",
1515
- " completed successfully"
1516
- ] })
1517
- ] }) : groupStatus === "failed" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1518
- /* @__PURE__ */ jsx(StatusIndicator, { status: "failed", size: "sm" }),
1519
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-red-400 font-medium", children: [
1520
- failedCount,
1521
- " tool",
1522
- failedCount !== 1 ? "s" : "",
1523
- " failed"
1524
- ] })
1525
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1526
- /* @__PURE__ */ jsx(StatusIndicator, { status: "failed", size: "sm" }),
1527
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-orange-400 font-medium", children: [
1528
- completedCount,
1529
- " succeeded, ",
1530
- failedCount,
1531
- " failed"
1532
- ] })
1533
- ] })
1620
+ "w-3.5 h-3.5 text-white/40 transition-transform duration-200 shrink-0",
1621
+ expanded && "rotate-90"
1622
+ )
1534
1623
  }
1535
- )
1536
- ] })
1537
- ]
1538
- }
1539
- );
1540
- }
1541
- function formatDuration(startMs, endMs) {
1542
- const duration = endMs - startMs;
1543
- if (duration < 1e3) return `${duration}ms`;
1544
- return `${(duration / 1e3).toFixed(1)}s`;
1624
+ ),
1625
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-white/60", children: [
1626
+ totalCount,
1627
+ " tool call",
1628
+ totalCount !== 1 ? "s" : ""
1629
+ ] }),
1630
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: displayActions.map((action) => {
1631
+ const IconComponent = getActionIconComponent(action);
1632
+ if (!IconComponent) return null;
1633
+ return /* @__PURE__ */ jsx(
1634
+ IconComponent,
1635
+ {
1636
+ className: "w-3.5 h-3.5 text-white/30"
1637
+ },
1638
+ action
1639
+ );
1640
+ }) }),
1641
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1642
+ !expanded && fileChanges.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 flex-wrap justify-end", children: [
1643
+ fileChanges.slice(0, 4).map((fc) => /* @__PURE__ */ jsx(
1644
+ FileBadge,
1645
+ {
1646
+ path: fc.path,
1647
+ linesAdded: fc.linesAdded,
1648
+ linesRemoved: fc.linesRemoved
1649
+ },
1650
+ fc.path
1651
+ )),
1652
+ fileChanges.length > 4 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-white/40", children: [
1653
+ "+",
1654
+ fileChanges.length - 4,
1655
+ " more"
1656
+ ] })
1657
+ ] })
1658
+ ]
1659
+ }
1660
+ ),
1661
+ expanded && /* @__PURE__ */ jsx("div", { className: "pl-5 border-l border-white/10 ml-1.5 mt-1 space-y-0.5", children: toolCalls.map((toolCall) => /* @__PURE__ */ jsx("div", { children: expandedCardId === toolCall.id ? /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
1662
+ /* @__PURE__ */ jsx(
1663
+ ToolCallCard,
1664
+ {
1665
+ toolCall,
1666
+ defaultExpanded: true
1667
+ }
1668
+ ),
1669
+ /* @__PURE__ */ jsx(
1670
+ "button",
1671
+ {
1672
+ onClick: () => setExpandedCardId(null),
1673
+ className: "text-xs text-white/40 hover:text-white/60 mt-1 pl-1",
1674
+ children: "Collapse"
1675
+ }
1676
+ )
1677
+ ] }) : /* @__PURE__ */ jsx(
1678
+ "button",
1679
+ {
1680
+ onClick: () => setExpandedCardId(toolCall.id),
1681
+ className: "w-full text-left hover:bg-white/5 rounded px-1 -mx-1 transition-colors",
1682
+ children: /* @__PURE__ */ jsx(CompactToolRow, { toolCall })
1683
+ }
1684
+ ) }, toolCall.id)) })
1685
+ ] });
1545
1686
  }
1546
- function getToolLabel(toolName, summary) {
1547
- if (summary && summary.length > 0 && summary.length < 50) {
1548
- return summary;
1687
+ function extractFileChanges2(toolCalls) {
1688
+ const fileMap = /* @__PURE__ */ new Map();
1689
+ for (const tc of toolCalls) {
1690
+ const { actionType } = tc;
1691
+ if (actionType.action === "file_edit") {
1692
+ const edit = actionType;
1693
+ const existing = fileMap.get(edit.path);
1694
+ if (existing) {
1695
+ existing.linesAdded = (existing.linesAdded || 0) + (edit.linesAdded || 0);
1696
+ existing.linesRemoved = (existing.linesRemoved || 0) + (edit.linesRemoved || 0);
1697
+ } else {
1698
+ fileMap.set(edit.path, {
1699
+ path: edit.path,
1700
+ linesAdded: edit.linesAdded,
1701
+ linesRemoved: edit.linesRemoved
1702
+ });
1703
+ }
1704
+ } else if (actionType.action === "file_write") {
1705
+ if (!fileMap.has(actionType.path)) {
1706
+ fileMap.set(actionType.path, {
1707
+ path: actionType.path,
1708
+ linesAdded: actionType.linesWritten
1709
+ });
1710
+ }
1711
+ }
1549
1712
  }
1550
- const cleaned = toolName.replace(/^mcp__[^_]+__/, "").replace(/Tool$/, "").replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2");
1551
- return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
1713
+ return Array.from(fileMap.values());
1552
1714
  }
1553
- function toStepStatus(status) {
1554
- switch (status) {
1555
- case "pending":
1556
- return "running";
1557
- case "success":
1558
- return "success";
1559
- case "failed":
1560
- return "error";
1561
- default:
1562
- return "pending";
1715
+ function countActionTypes2(toolCalls) {
1716
+ const counts = {};
1717
+ for (const tc of toolCalls) {
1718
+ const action = tc.actionType.action;
1719
+ counts[action] = (counts[action] || 0) + 1;
1563
1720
  }
1721
+ return counts;
1564
1722
  }
1565
- function StepIcon({ status }) {
1566
- const iconClass = "w-3.5 h-3.5";
1567
- switch (status) {
1568
- case "running":
1569
- return /* @__PURE__ */ jsx(SpinnerIcon, { className: cn(iconClass, "animate-spin text-[var(--ash-accent)]") });
1570
- case "success":
1571
- return /* @__PURE__ */ jsx(CheckIcon, { className: cn(iconClass, "text-[var(--ash-accent)]") });
1572
- case "error":
1573
- return /* @__PURE__ */ jsx(ErrorIcon, { className: cn(iconClass, "text-red-500") });
1723
+ function getActionIconComponent2(action) {
1724
+ switch (action) {
1725
+ case "file_read":
1726
+ return FileIcon;
1727
+ case "file_edit":
1728
+ case "file_write":
1729
+ return EditIcon;
1730
+ case "command_run":
1731
+ return TerminalIcon;
1732
+ case "search":
1733
+ case "glob":
1734
+ return SearchIcon;
1574
1735
  default:
1575
- return /* @__PURE__ */ jsx(ToolIcon, { className: cn(iconClass, "text-white/40") });
1736
+ return null;
1576
1737
  }
1577
1738
  }
1578
1739
  function StepAccordion({
@@ -1591,109 +1752,70 @@ function StepAccordion({
1591
1752
  setInternalExpanded((prev) => !prev);
1592
1753
  }
1593
1754
  }, [onToggle]);
1755
+ const fileChanges = useMemo(() => extractFileChanges2(toolCalls), [toolCalls]);
1756
+ const actionCounts = useMemo(() => countActionTypes2(toolCalls), [toolCalls]);
1757
+ const displayActions = useMemo(() => {
1758
+ return Object.entries(actionCounts).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([action]) => action);
1759
+ }, [actionCounts]);
1594
1760
  if (toolCalls.length === 0) {
1595
1761
  return null;
1596
1762
  }
1597
- const completedSteps = toolCalls.filter((tc) => tc.status === "success" || tc.status === "failed").length;
1598
- const runningStep = toolCalls.find((tc) => tc.status === "pending");
1599
- const hasError = toolCalls.some((tc) => tc.status === "failed");
1600
- const allComplete = completedSteps === toolCalls.length;
1601
- return /* @__PURE__ */ jsxs(
1602
- "div",
1603
- {
1604
- className: cn(
1605
- "rounded-xl border overflow-hidden",
1606
- hasError ? "border-red-500/30" : allComplete ? "border-[var(--ash-accent)]/30" : "border-yellow-500/30",
1607
- className
1608
- ),
1609
- children: [
1610
- /* @__PURE__ */ jsxs(
1611
- "button",
1612
- {
1613
- type: "button",
1614
- onClick: handleToggle,
1615
- className: "w-full flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 transition-colors text-left cursor-pointer",
1616
- children: [
1617
- /* @__PURE__ */ jsx(
1618
- ChevronDownIcon,
1619
- {
1620
- className: cn(
1621
- "w-4 h-4 text-white/40 transition-transform duration-200 flex-shrink-0",
1622
- !isExpanded && "-rotate-90"
1623
- )
1624
- }
1625
- ),
1626
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 flex items-center gap-2", children: runningStep ? /* @__PURE__ */ jsxs(Fragment, { children: [
1627
- /* @__PURE__ */ jsx(SpinnerIcon, { className: "w-3.5 h-3.5 animate-spin text-[var(--ash-accent)] flex-shrink-0" }),
1628
- /* @__PURE__ */ jsx("span", { className: "text-sm text-white/80 truncate", children: getToolLabel(runningStep.toolName, runningStep.summary) })
1629
- ] }) : hasError ? /* @__PURE__ */ jsxs(Fragment, { children: [
1630
- /* @__PURE__ */ jsx(ErrorIcon, { className: "w-3.5 h-3.5 text-red-500 flex-shrink-0" }),
1631
- /* @__PURE__ */ jsx("span", { className: "text-sm text-red-400 truncate", children: "Completed with errors" })
1632
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1633
- /* @__PURE__ */ jsx(CheckIcon, { className: "w-3.5 h-3.5 text-[var(--ash-accent)] flex-shrink-0" }),
1634
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-white/70 truncate", children: [
1635
- completedSteps,
1636
- " step",
1637
- completedSteps !== 1 ? "s" : "",
1638
- " completed"
1639
- ] })
1640
- ] }) }),
1641
- /* @__PURE__ */ jsxs("span", { className: "text-xs px-1.5 py-0.5 rounded-full bg-white/10 text-white/50 flex-shrink-0", children: [
1642
- completedSteps,
1643
- "/",
1644
- toolCalls.length
1645
- ] })
1646
- ]
1647
- }
1648
- ),
1649
- isExpanded && /* @__PURE__ */ jsx("div", { className: "border-t border-white/10 ash-accordion-content", children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-white/5", children: toolCalls.map((toolCall, index) => {
1650
- const stepStatus = toStepStatus(toolCall.status);
1651
- const startTime = toolCall.startedAt ? new Date(toolCall.startedAt).getTime() : 0;
1652
- const endTime = toolCall.completedAt ? new Date(toolCall.completedAt).getTime() : 0;
1653
- const hasDuration = startTime > 0 && endTime > 0;
1654
- return /* @__PURE__ */ jsxs(
1655
- "div",
1763
+ const totalCount = toolCalls.length;
1764
+ return /* @__PURE__ */ jsxs("div", { className: cn("ash-animate-fade-in", className), children: [
1765
+ /* @__PURE__ */ jsxs(
1766
+ "button",
1767
+ {
1768
+ type: "button",
1769
+ onClick: handleToggle,
1770
+ className: "w-full flex items-center gap-2 py-1 text-left group",
1771
+ children: [
1772
+ /* @__PURE__ */ jsx(
1773
+ ChevronRightIcon,
1656
1774
  {
1657
1775
  className: cn(
1658
- "px-3 py-2 flex items-start gap-2",
1659
- stepStatus === "running" && "bg-[var(--ash-accent)]/5",
1660
- stepStatus === "error" && "bg-red-500/5"
1661
- ),
1662
- children: [
1663
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 flex-shrink-0 pt-0.5", children: [
1664
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-white/40 w-4 text-right", children: [
1665
- index + 1,
1666
- "."
1667
- ] }),
1668
- /* @__PURE__ */ jsx(StepIcon, { status: stepStatus })
1669
- ] }),
1670
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1671
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1672
- /* @__PURE__ */ jsx(
1673
- "span",
1674
- {
1675
- className: cn(
1676
- "text-sm",
1677
- stepStatus === "running" && "text-[var(--ash-accent)]",
1678
- stepStatus === "success" && "text-white/70",
1679
- stepStatus === "error" && "text-red-400",
1680
- stepStatus === "pending" && "text-white/40"
1681
- ),
1682
- children: getToolLabel(toolCall.toolName, toolCall.summary)
1683
- }
1684
- ),
1685
- hasDuration && (stepStatus === "success" || stepStatus === "error") && /* @__PURE__ */ jsx("span", { className: "text-xs text-white/40", children: formatDuration(startTime, endTime) })
1686
- ] }),
1687
- toolCall.isError && toolCall.actionType && "result" in toolCall.actionType && /* @__PURE__ */ jsx("p", { className: "text-xs mt-1 text-red-400/80 truncate", children: String(toolCall.actionType.result?.value || "Error") })
1688
- ] })
1689
- ]
1690
- },
1691
- toolCall.id
1692
- );
1693
- }) }) })
1694
- ]
1695
- }
1696
- );
1776
+ "w-3.5 h-3.5 text-white/40 transition-transform duration-200 shrink-0",
1777
+ isExpanded && "rotate-90"
1778
+ )
1779
+ }
1780
+ ),
1781
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-white/60", children: [
1782
+ totalCount,
1783
+ " tool call",
1784
+ totalCount !== 1 ? "s" : ""
1785
+ ] }),
1786
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: displayActions.map((action) => {
1787
+ const IconComponent = getActionIconComponent2(action);
1788
+ if (!IconComponent) return null;
1789
+ return /* @__PURE__ */ jsx(
1790
+ IconComponent,
1791
+ {
1792
+ className: "w-3.5 h-3.5 text-white/30"
1793
+ },
1794
+ action
1795
+ );
1796
+ }) }),
1797
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1798
+ !isExpanded && fileChanges.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 flex-wrap justify-end", children: [
1799
+ fileChanges.slice(0, 4).map((fc) => /* @__PURE__ */ jsx(
1800
+ FileBadge,
1801
+ {
1802
+ path: fc.path,
1803
+ linesAdded: fc.linesAdded,
1804
+ linesRemoved: fc.linesRemoved
1805
+ },
1806
+ fc.path
1807
+ )),
1808
+ fileChanges.length > 4 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-white/40", children: [
1809
+ "+",
1810
+ fileChanges.length - 4,
1811
+ " more"
1812
+ ] })
1813
+ ] })
1814
+ ]
1815
+ }
1816
+ ),
1817
+ isExpanded && /* @__PURE__ */ jsx("div", { className: "pl-5 border-l border-white/10 ml-1.5 mt-1 space-y-0.5", children: toolCalls.map((toolCall) => /* @__PURE__ */ jsx(CompactToolRow, { toolCall }, toolCall.id)) })
1818
+ ] });
1697
1819
  }
1698
1820
 
1699
1821
  // src/types.ts
@@ -1796,10 +1918,18 @@ function MessageList({
1796
1918
  onOptionSelect,
1797
1919
  renderWidget,
1798
1920
  onWidgetAction,
1921
+ autoScroll = true,
1799
1922
  className
1800
1923
  }) {
1801
1924
  const contextConfig = useDisplayConfig();
1802
1925
  const config = displayConfigProp || contextConfig;
1926
+ const containerRef = useRef(null);
1927
+ const messagesEndRef = useRef(null);
1928
+ useEffect(() => {
1929
+ if (autoScroll && messagesEndRef.current && containerRef.current) {
1930
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth", block: "end" });
1931
+ }
1932
+ }, [entries, streamingContent, loading, autoScroll]);
1803
1933
  const createWidgetActionHandler = useCallback(
1804
1934
  (entryId, widgetType) => {
1805
1935
  if (!onWidgetAction) return void 0;
@@ -1823,7 +1953,7 @@ function MessageList({
1823
1953
  }
1824
1954
  return groupEntriesForCompactMode(entries, config);
1825
1955
  }, [entries, config]);
1826
- return /* @__PURE__ */ jsxs("div", { className: cn("flex-1 overflow-y-auto p-4 space-y-4 ash-scrollbar", className), children: [
1956
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("flex-1 overflow-y-auto p-4 space-y-4 ash-scrollbar", className), children: [
1827
1957
  groupedEntries.map((groupedEntry) => {
1828
1958
  if (groupedEntry.type === "single") {
1829
1959
  const entry = groupedEntry.entry;
@@ -1867,7 +1997,8 @@ function MessageList({
1867
1997
  loading && !streamingContent && /* @__PURE__ */ jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
1868
1998
  /* @__PURE__ */ jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
1869
1999
  /* @__PURE__ */ jsx("div", { className: "rounded-xl p-3 bg-white/5", children: /* @__PURE__ */ jsx(LoadingIndicator, { variant: "dots" }) })
1870
- ] })
2000
+ ] }),
2001
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1871
2002
  ] });
1872
2003
  }
1873
2004
  function getLevelIcon(level) {
@@ -2014,6 +2145,94 @@ function LogsPanel({
2014
2145
  }
2015
2146
  );
2016
2147
  }
2148
+ function CompactToolStatusLine({
2149
+ toolCall,
2150
+ previousToolCall,
2151
+ animationDuration = 300,
2152
+ className
2153
+ }) {
2154
+ const [isAnimating, setIsAnimating] = useState(false);
2155
+ const [displayedToolCall, setDisplayedToolCall] = useState(toolCall);
2156
+ const [exitingToolCall, setExitingToolCall] = useState(null);
2157
+ const prevToolCallRef = useRef(null);
2158
+ useEffect(() => {
2159
+ if (toolCall.id !== prevToolCallRef.current) {
2160
+ if (prevToolCallRef.current !== null && previousToolCall) {
2161
+ setExitingToolCall(previousToolCall);
2162
+ setIsAnimating(true);
2163
+ const timer = setTimeout(() => {
2164
+ setDisplayedToolCall(toolCall);
2165
+ setExitingToolCall(null);
2166
+ setIsAnimating(false);
2167
+ }, animationDuration);
2168
+ prevToolCallRef.current = toolCall.id;
2169
+ return () => clearTimeout(timer);
2170
+ } else {
2171
+ setDisplayedToolCall(toolCall);
2172
+ prevToolCallRef.current = toolCall.id;
2173
+ }
2174
+ } else {
2175
+ setDisplayedToolCall(toolCall);
2176
+ }
2177
+ return void 0;
2178
+ }, [toolCall, previousToolCall, animationDuration]);
2179
+ const statusClasses = {
2180
+ pending: "border-yellow-500/30",
2181
+ success: "border-[var(--ash-accent)]/30",
2182
+ failed: "border-red-500/30"
2183
+ };
2184
+ const renderToolCallContent = (tc, isExiting) => /* @__PURE__ */ jsxs(
2185
+ "div",
2186
+ {
2187
+ className: cn(
2188
+ "flex items-center gap-3 px-4 py-2.5",
2189
+ isExiting ? "ash-status-line-exit" : isAnimating ? "ash-status-line-enter" : ""
2190
+ ),
2191
+ style: {
2192
+ animationDuration: `${animationDuration}ms`
2193
+ },
2194
+ children: [
2195
+ /* @__PURE__ */ jsx(
2196
+ "div",
2197
+ {
2198
+ className: cn(
2199
+ "w-6 h-6 rounded-lg flex items-center justify-center shrink-0",
2200
+ tc.status === "pending" ? "bg-yellow-500/20" : tc.status === "failed" ? "bg-red-500/20" : "bg-[var(--ash-accent)]/20"
2201
+ ),
2202
+ children: /* @__PURE__ */ jsx(
2203
+ ActionIcon,
2204
+ {
2205
+ actionType: tc.actionType,
2206
+ className: cn(
2207
+ "w-3.5 h-3.5",
2208
+ tc.status === "pending" ? "text-yellow-400" : tc.status === "failed" ? "text-red-400" : "text-[var(--ash-accent)]"
2209
+ )
2210
+ }
2211
+ )
2212
+ }
2213
+ ),
2214
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-white shrink-0", children: getActionLabel(tc.actionType) }),
2215
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-sm truncate text-white/60 flex-1 min-w-0", children: tc.summary }),
2216
+ /* @__PURE__ */ jsx(StatusIndicator, { status: tc.status, size: "sm" })
2217
+ ]
2218
+ }
2219
+ );
2220
+ return /* @__PURE__ */ jsxs(
2221
+ "div",
2222
+ {
2223
+ className: cn(
2224
+ "relative rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
2225
+ statusClasses[displayedToolCall.status],
2226
+ displayedToolCall.status === "pending" && "ash-tool-status-pending",
2227
+ className
2228
+ ),
2229
+ children: [
2230
+ exitingToolCall && /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children: renderToolCallContent(exitingToolCall, true) }),
2231
+ renderToolCallContent(displayedToolCall, false)
2232
+ ]
2233
+ }
2234
+ );
2235
+ }
2017
2236
  function TodoStatusIcon({ status, className = "w-4 h-4" }) {
2018
2237
  switch (status) {
2019
2238
  case "completed":
@@ -3173,7 +3392,79 @@ function useAgentChat(options) {
3173
3392
  setEntries
3174
3393
  };
3175
3394
  }
3395
+ function textToBase64(text) {
3396
+ const encoder = new TextEncoder();
3397
+ const bytes = encoder.encode(text);
3398
+ let binary = "";
3399
+ for (let i = 0; i < bytes.length; i++) {
3400
+ const byte = bytes[i];
3401
+ if (byte !== void 0) {
3402
+ binary += String.fromCharCode(byte);
3403
+ }
3404
+ }
3405
+ return btoa(binary);
3406
+ }
3407
+ function generateFilename(template) {
3408
+ const timestamp = Date.now();
3409
+ const date = new Date(timestamp);
3410
+ const dateStr = date.toISOString().split("T")[0] ?? "unknown-date";
3411
+ const timeStr = (date.toTimeString().split(" ")[0] ?? "00-00-00").replace(/:/g, "-");
3412
+ return template.replace("{timestamp}", String(timestamp)).replace("{date}", dateStr).replace("{time}", timeStr);
3413
+ }
3414
+ function useLongTextConversion({
3415
+ threshold = 5e3,
3416
+ filenameTemplate = "pasted-text-{timestamp}.txt",
3417
+ onConversion
3418
+ } = {}) {
3419
+ const [lastConversion, setLastConversion] = useState(null);
3420
+ const processText = useCallback((text) => {
3421
+ if (text.length <= threshold) {
3422
+ return { text };
3423
+ }
3424
+ const filename = generateFilename(filenameTemplate);
3425
+ const content = textToBase64(text);
3426
+ const encoder = new TextEncoder();
3427
+ const size = encoder.encode(text).length;
3428
+ const lineCount = text.split("\n").length;
3429
+ const attachment = {
3430
+ filename,
3431
+ content,
3432
+ mimeType: "text/plain",
3433
+ size
3434
+ };
3435
+ const preview = text.slice(0, 100) + (text.length > 100 ? "..." : "");
3436
+ const conversionInfo = {
3437
+ originalLength: text.length,
3438
+ filename,
3439
+ preview,
3440
+ lineCount
3441
+ };
3442
+ setLastConversion(conversionInfo);
3443
+ onConversion?.(conversionInfo);
3444
+ return { text: "", attachment };
3445
+ }, [threshold, filenameTemplate, onConversion]);
3446
+ const handlePaste = useCallback((event, _currentValue, _setValue, addAttachment) => {
3447
+ const pastedText = event.clipboardData.getData("text/plain");
3448
+ if (pastedText.length > threshold) {
3449
+ event.preventDefault();
3450
+ const result = processText(pastedText);
3451
+ if (result.attachment) {
3452
+ addAttachment(result.attachment);
3453
+ }
3454
+ }
3455
+ }, [threshold, processText]);
3456
+ const clearLastConversion = useCallback(() => {
3457
+ setLastConversion(null);
3458
+ }, []);
3459
+ return {
3460
+ processText,
3461
+ handlePaste,
3462
+ lastConversion,
3463
+ clearLastConversion,
3464
+ threshold
3465
+ };
3466
+ }
3176
3467
 
3177
- export { ActionIcon, AlertCircleIcon, AlertTriangleIcon, AssistantMessage, BotIcon, BrainIcon, BugIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CircleIcon, ClipboardListIcon, CodeBlock, CodeIcon, CompactToolStatusLine, CopyIcon, DEFAULT_DISPLAY_CONFIG, DisplayModeProvider, DisplayModeToggle, EditIcon, EnvVarsPanel, ErrorIcon, ErrorMessage, FileIcon, FilePlusIcon, FolderSearchIcon, GlobeIcon, InfoIcon, JsonDisplay, ListChecksIcon, LoaderIcon, LoadingIndicator, LogsPanel, MessageEntry, MessageList, MessageSquareIcon, MoonIcon, OptionCards, PaperclipIcon, PlugIcon, SearchIcon, SendIcon, SparklesIcon, SpinnerIcon, StatusIndicator, StepAccordion, StopCircleIcon, StreamingText, SunIcon, TerminalIcon, ThemeProvider, ThinkingMessage, TodoPanel, ToolCallCard, ToolCallMessage, ToolExecutionGroup, ToolIcon, TypewriterText, UserIcon, UserMessage, XCircleIcon, XIcon, allKeyframesCss, borderRadius, cn, colors, createToolCall, extractTextContent, extractToolCallsFromGroup, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, inlineStyles, isCommandRunAction, isErrorEntry, isFileEditAction, isFileReadAction, isFileWriteAction, isGenericToolAction, isGlobAction, isMcpToolAction, isSearchAction, isTodoWriteAction, isToolCallEntry, isWebFetchAction, isWebSearchAction, isWidgetEntry, keyframes, keyframesCss, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, shadows, spacing, tokensToCssVariables, transitions, truncate, typography, updateToolCallWithResult, useAgentChat, useDisplayConfig, useDisplayMode, useFileUpload, useMessageQueue, useStopExecution, useTheme, widget, zIndex };
3468
+ export { ActionIcon, AlertCircleIcon, AlertTriangleIcon, AssistantMessage, BotIcon, BrainIcon, BugIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CircleIcon, ClipboardListIcon, CodeBlock, CodeIcon, CompactToolRow, CompactToolStatusLine, CopyIcon, DEFAULT_DISPLAY_CONFIG, DisplayModeProvider, DisplayModeToggle, EditIcon, EnvVarsPanel, ErrorIcon, ErrorMessage, FileBadge, FileIcon, FilePlusIcon, FolderSearchIcon, GlobeIcon, InfoIcon, JsonDisplay, ListChecksIcon, LoaderIcon, LoadingIndicator, LogsPanel, MessageEntry, MessageList, MessageSquareIcon, MoonIcon, OptionCards, PaperclipIcon, PlugIcon, SearchIcon, SendIcon, SparklesIcon, SpinnerIcon, StatusIndicator, StepAccordion, StopCircleIcon, StreamingText, SunIcon, TerminalIcon, ThemeProvider, ThinkingMessage, TodoPanel, ToolCallCard, ToolCallMessage, ToolExecutionGroup, ToolIcon, TypewriterText, UserIcon, UserMessage, XCircleIcon, XIcon, allKeyframesCss, borderRadius, cn, colors, createToolCall, extractTextContent, extractToolCallsFromGroup, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, inlineStyles, isCommandRunAction, isErrorEntry, isFileEditAction, isFileReadAction, isFileWriteAction, isGenericToolAction, isGlobAction, isMcpToolAction, isSearchAction, isTodoWriteAction, isToolCallEntry, isWebFetchAction, isWebSearchAction, isWidgetEntry, keyframes, keyframesCss, mapToolToActionType, normalizeToolResult, parseCommandResult, parseMcpToolName, parseOptionsFromContent, shadows, spacing, tokensToCssVariables, transitions, truncate, typography, updateToolCallWithResult, useAgentChat, useDisplayConfig, useDisplayMode, useFileUpload, useLongTextConversion, useMessageQueue, useStopExecution, useTheme, widget, zIndex };
3178
3469
  //# sourceMappingURL=index.js.map
3179
3470
  //# sourceMappingURL=index.js.map