@ash-cloud/ash-ui 0.1.0 → 0.1.1

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 CHANGED
@@ -694,6 +694,26 @@ function ErrorIcon({ className }) {
694
694
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
695
695
  ] });
696
696
  }
697
+ function MicrophoneIcon({ className }) {
698
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
699
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
700
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
701
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
702
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
703
+ ] });
704
+ }
705
+ function HomeIcon({ className }) {
706
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
707
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
708
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 22 9 12 15 12 15 22" })
709
+ ] });
710
+ }
711
+ function ArrowUpIcon({ className }) {
712
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
713
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
714
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "5 12 12 5 19 12" })
715
+ ] });
716
+ }
697
717
  function StatusIndicator({ status, size = "sm", className }) {
698
718
  const sizeClasses = {
699
719
  sm: "w-2 h-2",
@@ -754,14 +774,41 @@ function CodeBlock({
754
774
  className
755
775
  }) {
756
776
  const [expanded, setExpanded] = react.useState(false);
777
+ const [copied, setCopied] = react.useState(false);
757
778
  const lines = children.split("\n");
758
779
  const isLong = lines.length > 10 || children.length > 500;
759
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", className), children: [
780
+ const handleCopy = react.useCallback(async () => {
781
+ try {
782
+ await navigator.clipboard.writeText(children);
783
+ setCopied(true);
784
+ setTimeout(() => setCopied(false), 2e3);
785
+ } catch (err) {
786
+ console.error("Failed to copy:", err);
787
+ }
788
+ }, [children]);
789
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative rounded-lg overflow-hidden border border-white/10", className), children: [
790
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2 bg-[var(--ash-code-header-bg)] border-b border-white/10", children: [
791
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-white/60 font-medium", children: language || "code" }),
792
+ /* @__PURE__ */ jsxRuntime.jsx(
793
+ "button",
794
+ {
795
+ onClick: handleCopy,
796
+ className: "flex items-center gap-1.5 text-xs text-white/60 hover:text-white transition-colors",
797
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
798
+ /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "w-3.5 h-3.5" }),
799
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Copied!" })
800
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
801
+ /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, { className: "w-3.5 h-3.5" }),
802
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Copy" })
803
+ ] })
804
+ }
805
+ )
806
+ ] }),
760
807
  /* @__PURE__ */ jsxRuntime.jsx(
761
808
  "pre",
762
809
  {
763
810
  className: cn(
764
- "ash-tool-code-block text-xs font-mono text-white/90 p-3 rounded-xl overflow-x-auto whitespace-pre-wrap break-words",
811
+ "text-xs font-mono text-white/90 p-4 bg-black/30 overflow-x-auto whitespace-pre-wrap break-words",
765
812
  !expanded && isLong && "overflow-y-hidden"
766
813
  ),
767
814
  style: !expanded && isLong ? { maxHeight } : void 0,
@@ -945,7 +992,7 @@ function SectionHeader({ children }) {
945
992
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-tool-section-header", children });
946
993
  }
947
994
  function SectionContent({ children }) {
948
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2", children });
995
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1.5", children });
949
996
  }
950
997
  function CommandRunDetails({ action }) {
951
998
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -1066,7 +1113,7 @@ function WebSearchDetails({ action }) {
1066
1113
  /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/90", children: action.query }) })
1067
1114
  ] });
1068
1115
  }
1069
- function McpToolDetails({ action }) {
1116
+ function McpToolDetails({ action, isError }) {
1070
1117
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1071
1118
  /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "TOOL" }),
1072
1119
  /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsxs("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: [
@@ -1079,20 +1126,20 @@ function McpToolDetails({ action }) {
1079
1126
  /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.arguments }) })
1080
1127
  ] }),
1081
1128
  action.result && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1082
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "RESULT" }),
1083
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: action.result.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.result.value }) })
1129
+ /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: isError ? "ERROR" : "RESULT" }),
1130
+ /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: isError ? "text-red-400" : "", children: action.result.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.result.value }) }) })
1084
1131
  ] })
1085
1132
  ] });
1086
1133
  }
1087
- function GenericToolDetails({ action }) {
1134
+ function GenericToolDetails({ action, isError }) {
1088
1135
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1089
1136
  action.arguments && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1090
1137
  /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "ARGS" }),
1091
1138
  /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.arguments }) })
1092
1139
  ] }),
1093
1140
  action.result && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1094
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "RESULT" }),
1095
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: action.result.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.result.value }) })
1141
+ /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: isError ? "ERROR" : "RESULT" }),
1142
+ /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: isError ? "text-red-400" : "", children: action.result.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.result.value }) }) })
1096
1143
  ] })
1097
1144
  ] });
1098
1145
  }
@@ -1164,7 +1211,7 @@ function TodoWriteDetails({ action }) {
1164
1211
  }) }) })
1165
1212
  ] });
1166
1213
  }
1167
- function ToolDetails({ actionType }) {
1214
+ function ToolDetails({ actionType, isError }) {
1168
1215
  switch (actionType.action) {
1169
1216
  case "command_run":
1170
1217
  return /* @__PURE__ */ jsxRuntime.jsx(CommandRunDetails, { action: actionType });
@@ -1183,9 +1230,9 @@ function ToolDetails({ actionType }) {
1183
1230
  case "web_search":
1184
1231
  return /* @__PURE__ */ jsxRuntime.jsx(WebSearchDetails, { action: actionType });
1185
1232
  case "mcp_tool":
1186
- return /* @__PURE__ */ jsxRuntime.jsx(McpToolDetails, { action: actionType });
1233
+ return /* @__PURE__ */ jsxRuntime.jsx(McpToolDetails, { action: actionType, isError });
1187
1234
  case "generic_tool":
1188
- return /* @__PURE__ */ jsxRuntime.jsx(GenericToolDetails, { action: actionType });
1235
+ return /* @__PURE__ */ jsxRuntime.jsx(GenericToolDetails, { action: actionType, isError });
1189
1236
  case "todo_write":
1190
1237
  return /* @__PURE__ */ jsxRuntime.jsx(TodoWriteDetails, { action: actionType });
1191
1238
  default:
@@ -1246,7 +1293,7 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1246
1293
  "div",
1247
1294
  {
1248
1295
  className: cn(
1249
- "rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
1296
+ "rounded-lg border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
1250
1297
  statusClasses[status],
1251
1298
  className
1252
1299
  ),
@@ -1256,35 +1303,35 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1256
1303
  {
1257
1304
  onClick: () => canExpand && setExpanded(!expanded),
1258
1305
  className: cn(
1259
- "w-full px-4 py-3 flex items-center justify-between transition-colors",
1306
+ "w-full px-3 py-2 flex items-center justify-between transition-colors",
1260
1307
  canExpand ? "hover:bg-white/5 cursor-pointer" : "cursor-default"
1261
1308
  ),
1262
1309
  disabled: !canExpand,
1263
1310
  children: [
1264
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0 flex-1", children: [
1311
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [
1265
1312
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
1266
- "w-6 h-6 rounded-lg flex items-center justify-center shrink-0",
1313
+ "w-5 h-5 rounded flex items-center justify-center shrink-0",
1267
1314
  status === "pending" ? "bg-yellow-500/20" : status === "failed" ? "bg-red-500/20" : "bg-[var(--ash-accent)]/20"
1268
1315
  ), children: /* @__PURE__ */ jsxRuntime.jsx(
1269
1316
  ActionIcon,
1270
1317
  {
1271
1318
  actionType,
1272
1319
  className: cn(
1273
- "w-3.5 h-3.5",
1320
+ "w-3 h-3",
1274
1321
  status === "pending" ? "text-yellow-400" : status === "failed" ? "text-red-400" : "text-[var(--ash-accent)]"
1275
1322
  )
1276
1323
  }
1277
1324
  ) }),
1278
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-white shrink-0", children: getActionLabel(actionType) }),
1279
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm truncate text-white/60 min-w-0", children: summary })
1325
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px] font-medium text-white shrink-0", children: getActionLabel(actionType) }),
1326
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[12px] truncate text-white/50 min-w-0", children: summary })
1280
1327
  ] }),
1281
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
1328
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
1282
1329
  /* @__PURE__ */ jsxRuntime.jsx(StatusIndicator, { status, size: "sm" }),
1283
1330
  canExpand && /* @__PURE__ */ jsxRuntime.jsx(
1284
1331
  ChevronDownIcon,
1285
1332
  {
1286
1333
  className: cn(
1287
- "w-4 h-4 text-white/40 transition-transform duration-200",
1334
+ "w-3.5 h-3.5 text-white/30 transition-transform duration-200",
1288
1335
  expanded && "rotate-180"
1289
1336
  )
1290
1337
  }
@@ -1293,11 +1340,15 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1293
1340
  ]
1294
1341
  }
1295
1342
  ),
1296
- expanded && canExpand && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-white/5 max-h-[400px] overflow-y-auto ash-scrollbar bg-black/20", children: [
1297
- /* @__PURE__ */ jsxRuntime.jsx(ToolDetails, { actionType }),
1298
- status === "success" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-t border-white/5 bg-[var(--ash-accent)]/5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1299
- /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }),
1300
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-[var(--ash-accent)] font-medium", children: "Completed successfully" })
1343
+ expanded && canExpand && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-white/5 max-h-[350px] overflow-y-auto ash-scrollbar bg-black/20", children: [
1344
+ /* @__PURE__ */ jsxRuntime.jsx(ToolDetails, { actionType, isError: toolCall.isError || status === "failed" }),
1345
+ status === "success" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 border-t border-white/5 bg-[var(--ash-accent)]/5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
1346
+ /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "w-3.5 h-3.5 text-[var(--ash-accent)]" }),
1347
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] text-[var(--ash-accent)] font-medium", children: "Completed" })
1348
+ ] }) }),
1349
+ status === "failed" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 border-t border-red-500/20 bg-red-500/10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
1350
+ /* @__PURE__ */ jsxRuntime.jsx(XCircleIcon, { className: "w-3.5 h-3.5 text-red-400" }),
1351
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] text-red-400 font-medium", children: "Failed" })
1301
1352
  ] }) })
1302
1353
  ] })
1303
1354
  ]
@@ -1305,15 +1356,19 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1305
1356
  );
1306
1357
  }
1307
1358
  var ReactMarkdown = react.lazy(() => import('react-markdown'));
1308
- function LazyMarkdown({ children, fallback, className }) {
1359
+ function LazyMarkdown({ children, fallback, components, className }) {
1309
1360
  const [mounted, setMounted] = react.useState(false);
1310
1361
  react.useEffect(() => {
1311
1362
  setMounted(true);
1312
1363
  }, []);
1364
+ const markdownComponents = react.useMemo(() => {
1365
+ if (!components) return void 0;
1366
+ return components;
1367
+ }, [components]);
1313
1368
  if (!mounted) {
1314
1369
  return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback ?? children });
1315
1370
  }
1316
- return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback ?? children }), children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown, { children }) });
1371
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback ?? children }), children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown, { components: markdownComponents, children }) });
1317
1372
  }
1318
1373
  function DefaultMentionBadge({ segment }) {
1319
1374
  const bgColor = segment.color ? `${segment.color}20` : "rgba(34, 197, 94, 0.2)";
@@ -1413,6 +1468,36 @@ function OptionCards({ options, onSelect, className }) {
1413
1468
  option.id
1414
1469
  )) });
1415
1470
  }
1471
+ var scaleClasses = {
1472
+ dense: {
1473
+ text: "text-xs",
1474
+ padding: "px-2 py-1",
1475
+ gap: "gap-1.5",
1476
+ avatar: "w-4 h-4",
1477
+ messageGap: "space-y-0.5"
1478
+ },
1479
+ compact: {
1480
+ text: "text-[13px]",
1481
+ padding: "px-3 py-2",
1482
+ gap: "gap-2",
1483
+ avatar: "w-5 h-5",
1484
+ messageGap: "space-y-1"
1485
+ },
1486
+ default: {
1487
+ text: "text-sm",
1488
+ padding: "px-4 py-3",
1489
+ gap: "gap-3",
1490
+ avatar: "w-6 h-6",
1491
+ messageGap: "space-y-2"
1492
+ },
1493
+ comfortable: {
1494
+ text: "text-[15px]",
1495
+ padding: "px-5 py-4",
1496
+ gap: "gap-4",
1497
+ avatar: "w-7 h-7",
1498
+ messageGap: "space-y-3"
1499
+ }
1500
+ };
1416
1501
  function parseFilesFromContent(content) {
1417
1502
  const fileMarker = "[Uploaded files available at /uploads/]";
1418
1503
  const markerIndex = content.indexOf(fileMarker);
@@ -1424,43 +1509,82 @@ function parseFilesFromContent(content) {
1424
1509
  const files = fileSection.split("\n").filter((line) => line.startsWith("- ")).map((line) => line.substring(2).trim());
1425
1510
  return { text, files };
1426
1511
  }
1427
- function UserMessage({ entry, className }) {
1512
+ function UserMessage({
1513
+ entry,
1514
+ variant = "bubble",
1515
+ scale = "compact",
1516
+ showAvatar = false,
1517
+ // Hidden by default for ChatGPT style
1518
+ showTimestamp = true,
1519
+ renderMetadata,
1520
+ metadata,
1521
+ className
1522
+ }) {
1428
1523
  const { text, files } = parseFilesFromContent(entry.content);
1429
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 justify-end ash-animate-fade-in", className), children: [
1430
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-[85%]", children: [
1431
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-2xl p-4 bg-[var(--ash-accent)] text-[var(--ash-accent-foreground)]", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm leading-relaxed whitespace-pre-wrap", children: text || "(files attached)" }) }),
1432
- files.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 pt-2 border-t border-[var(--ash-accent-foreground)]/20", children: [
1433
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-[var(--ash-accent-foreground)]/60 mb-1 flex items-center gap-1", children: [
1434
- /* @__PURE__ */ jsxRuntime.jsx(PaperclipIcon, { className: "w-3 h-3" }),
1435
- "Attached Files"
1524
+ const s = scaleClasses[scale];
1525
+ const variantClasses = {
1526
+ bubble: "rounded-[20px] bg-[var(--ash-user-bg)] text-[var(--ash-user-text)] border border-[var(--ash-user-border)]",
1527
+ plain: "bg-transparent text-[var(--ash-text-primary)]",
1528
+ minimal: "bg-transparent text-[var(--ash-text-secondary)]"
1529
+ };
1530
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1531
+ "div",
1532
+ {
1533
+ "data-message-role": "user",
1534
+ className: cn("flex justify-end ash-animate-fade-in pr-1", s.gap, className),
1535
+ children: [
1536
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-[85%] flex flex-col items-end", children: [
1537
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(variantClasses[variant], variant === "bubble" && "px-4 py-3"), children: [
1538
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn(s.text, "leading-snug whitespace-pre-wrap"), children: text || "(files attached)" }),
1539
+ files.length > 0 && variant === "bubble" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 pt-1.5 border-t border-[var(--ash-user-text)]/15 flex flex-wrap gap-1", children: files.map((file, index) => /* @__PURE__ */ jsxRuntime.jsxs(
1540
+ "span",
1541
+ {
1542
+ className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-[var(--ash-user-text)]/10 text-[11px]",
1543
+ title: file,
1544
+ children: [
1545
+ /* @__PURE__ */ jsxRuntime.jsx(PaperclipIcon, { className: "w-2.5 h-2.5 opacity-60" }),
1546
+ file.split(" (")[0]
1547
+ ]
1548
+ },
1549
+ index
1550
+ )) })
1551
+ ] }),
1552
+ renderMetadata ? renderMetadata({ entry, metadata }) : showTimestamp && entry.timestamp && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[10px] text-[var(--ash-text-faint)] mt-0.5 mr-1 tabular-nums flex items-center gap-1.5", children: [
1553
+ metadata?.model && /* @__PURE__ */ jsxRuntime.jsx("span", { children: metadata.model }),
1554
+ metadata?.model && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50", children: "\xB7" }),
1555
+ formatTimestamp(entry.timestamp)
1556
+ ] })
1436
1557
  ] }),
1437
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: files.map((file, index) => /* @__PURE__ */ jsxRuntime.jsx(
1438
- "span",
1439
- {
1440
- className: "inline-flex items-center px-2 py-0.5 rounded-lg bg-[var(--ash-accent-foreground)]/10 text-xs",
1441
- title: file,
1442
- children: file.split(" (")[0]
1443
- },
1444
- index
1445
- )) })
1446
- ] }),
1447
- entry.timestamp && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-white/40 mt-2", children: formatTimestamp(entry.timestamp) })
1448
- ] }),
1449
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-white/10 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(UserIcon, { className: "w-4 h-4 text-white/50" }) })
1450
- ] });
1558
+ showAvatar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(s.avatar, "rounded-full bg-[var(--ash-avatar-user-bg)] flex items-center justify-center shrink-0 mt-0.5"), children: /* @__PURE__ */ jsxRuntime.jsx(UserIcon, { className: "w-3 h-3 text-[var(--ash-text-muted)]" }) })
1559
+ ]
1560
+ }
1561
+ );
1451
1562
  }
1452
1563
  function AssistantMessage({
1453
1564
  entry,
1565
+ variant = "bubble",
1566
+ scale = "compact",
1567
+ showAvatar = true,
1568
+ showTimestamp = true,
1454
1569
  onOptionSelect,
1455
1570
  richContentRenderers,
1571
+ markdownComponents,
1572
+ renderMetadata,
1573
+ metadata,
1456
1574
  className
1457
1575
  }) {
1458
1576
  const parsedOptions = !entry.richContent ? parseOptionsFromContent(entry.content) : null;
1577
+ const s = scaleClasses[scale];
1459
1578
  const handleOptionSelect = (option) => {
1460
1579
  if (onOptionSelect) {
1461
1580
  onOptionSelect(`Option ${option.id}: ${option.label}`);
1462
1581
  }
1463
1582
  };
1583
+ const variantClasses = {
1584
+ bubble: "rounded-[16px] bg-[var(--ash-assistant-bg)] border border-[var(--ash-assistant-border)]",
1585
+ plain: "bg-transparent",
1586
+ minimal: "bg-transparent"
1587
+ };
1464
1588
  const renderContent = (textContent) => {
1465
1589
  if (entry.richContent && entry.richContent.length > 0) {
1466
1590
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1471,81 +1595,143 @@ function AssistantMessage({
1471
1595
  }
1472
1596
  );
1473
1597
  }
1474
- return /* @__PURE__ */ jsxRuntime.jsx(LazyMarkdown, { children: textContent || entry.content });
1598
+ return /* @__PURE__ */ jsxRuntime.jsx(LazyMarkdown, { components: markdownComponents, children: textContent || entry.content });
1475
1599
  };
1476
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1477
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
1478
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 max-w-[85%]", children: [
1479
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-card-glass rounded-2xl p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-message-content prose prose-sm prose-invert max-w-none text-sm leading-relaxed", children: parsedOptions ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1480
- parsedOptions.preamble && renderContent(parsedOptions.preamble),
1481
- /* @__PURE__ */ jsxRuntime.jsx(
1482
- OptionCards,
1483
- {
1484
- options: parsedOptions.options,
1485
- onSelect: handleOptionSelect
1486
- }
1487
- )
1488
- ] }) : renderContent() }) }),
1489
- entry.timestamp && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-white/40 mt-2", children: formatTimestamp(entry.timestamp) })
1490
- ] })
1491
- ] });
1600
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1601
+ "div",
1602
+ {
1603
+ "data-message-role": "assistant",
1604
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1605
+ children: [
1606
+ showAvatar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(s.avatar, "rounded-full bg-[var(--ash-avatar-assistant-bg)] flex items-center justify-center shrink-0 mt-0.5"), children: /* @__PURE__ */ jsxRuntime.jsx(SparklesIcon, { className: "w-3 h-3 text-white" }) }),
1607
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
1608
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(variantClasses[variant], variant === "bubble" && "px-4 py-3"), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("ash-message-content prose prose-sm prose-invert max-w-none leading-relaxed text-[var(--ash-assistant-text)]", s.text), children: parsedOptions ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1609
+ parsedOptions.preamble && renderContent(parsedOptions.preamble),
1610
+ /* @__PURE__ */ jsxRuntime.jsx(
1611
+ OptionCards,
1612
+ {
1613
+ options: parsedOptions.options,
1614
+ onSelect: handleOptionSelect
1615
+ }
1616
+ )
1617
+ ] }) : renderContent() }) }),
1618
+ renderMetadata ? renderMetadata({ entry, metadata }) : showTimestamp && entry.timestamp && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[10px] text-[var(--ash-text-faint)] mt-0.5 ml-1 tabular-nums flex items-center gap-1.5", children: [
1619
+ metadata?.model && /* @__PURE__ */ jsxRuntime.jsx("span", { children: metadata.model }),
1620
+ metadata?.model && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50", children: "\xB7" }),
1621
+ formatTimestamp(entry.timestamp)
1622
+ ] })
1623
+ ] })
1624
+ ]
1625
+ }
1626
+ );
1492
1627
  }
1493
- function ThinkingMessage({ entry, className }) {
1494
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1495
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-purple-500/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(SparklesIcon, { className: "w-4 h-4 text-purple-400" }) }),
1496
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl p-4 bg-purple-500/10 border border-purple-500/30", children: [
1497
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-purple-400 mb-2 font-medium", children: "Thinking" }),
1498
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-purple-300 italic opacity-80 whitespace-pre-wrap leading-relaxed", children: entry.content })
1499
- ] }) })
1500
- ] });
1628
+ function ThinkingMessage({ entry, scale = "compact", showAvatar = true, className }) {
1629
+ const s = scaleClasses[scale];
1630
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1631
+ "div",
1632
+ {
1633
+ "data-message-role": "thinking",
1634
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1635
+ children: [
1636
+ showAvatar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(s.avatar, "rounded-md bg-[var(--ash-avatar-thinking-bg)] flex items-center justify-center shrink-0 mt-0.5"), children: /* @__PURE__ */ jsxRuntime.jsx(SparklesIcon, { className: "w-3 h-3 text-purple-400" }) }),
1637
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("rounded-lg bg-[var(--ash-thinking-bg)] border border-[var(--ash-thinking-border)]", s.padding), children: [
1638
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] text-[var(--ash-thinking-text)] opacity-80 mb-1 font-medium uppercase tracking-wide", children: "Thinking" }),
1639
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(s.text, "text-[var(--ash-thinking-text)] opacity-70 italic whitespace-pre-wrap leading-relaxed"), children: entry.content })
1640
+ ] }) })
1641
+ ]
1642
+ }
1643
+ );
1501
1644
  }
1502
- function ToolCallMessage({ entry, defaultExpanded = false, className }) {
1645
+ function ToolCallMessage({ entry, scale = "compact", showAvatar = true, defaultExpanded = false, className }) {
1503
1646
  if (entry.entryType.type !== "tool_call") return null;
1504
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1505
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
1506
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(ToolCallCard, { toolCall: entry.entryType.toolCall, defaultExpanded }) })
1507
- ] });
1647
+ const s = scaleClasses[scale];
1648
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1649
+ "div",
1650
+ {
1651
+ "data-message-role": "tool",
1652
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1653
+ children: [
1654
+ showAvatar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(s.avatar, "rounded-md bg-[var(--ash-avatar-assistant-bg)] flex items-center justify-center shrink-0 mt-0.5"), children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
1655
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(ToolCallCard, { toolCall: entry.entryType.toolCall, defaultExpanded }) })
1656
+ ]
1657
+ }
1658
+ );
1508
1659
  }
1509
- function ErrorMessage({ entry, className }) {
1660
+ function ErrorMessage({ entry, scale = "compact", showAvatar = true, className }) {
1510
1661
  if (entry.entryType.type !== "error") return null;
1511
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1512
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-red-500/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(AlertTriangleIcon, { className: "w-4 h-4 text-red-400" }) }),
1513
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl p-4 bg-red-500/10 border border-red-500/30", children: [
1514
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-red-400 mb-2 font-medium", children: "Error" }),
1515
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-300", children: entry.entryType.message }),
1516
- entry.entryType.code && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-red-400/70 mt-2 font-mono", children: [
1517
- "Code: ",
1518
- entry.entryType.code
1519
- ] })
1520
- ] }) })
1521
- ] });
1662
+ const s = scaleClasses[scale];
1663
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1664
+ "div",
1665
+ {
1666
+ "data-message-role": "error",
1667
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1668
+ children: [
1669
+ showAvatar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(s.avatar, "rounded-md bg-[var(--ash-avatar-error-bg)] flex items-center justify-center shrink-0 mt-0.5"), children: /* @__PURE__ */ jsxRuntime.jsx(AlertTriangleIcon, { className: "w-3 h-3 text-red-400" }) }),
1670
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("rounded-lg bg-[var(--ash-error-bg)] border border-[var(--ash-error-border)]", s.padding), children: [
1671
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] text-[var(--ash-error-text)] opacity-80 mb-1 font-medium uppercase tracking-wide", children: "Error" }),
1672
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn(s.text, "text-[var(--ash-error-text)] opacity-90"), children: entry.entryType.message }),
1673
+ entry.entryType.code && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[11px] text-[var(--ash-error-text)] opacity-60 mt-1 font-mono", children: [
1674
+ "Code: ",
1675
+ entry.entryType.code
1676
+ ] })
1677
+ ] }) })
1678
+ ]
1679
+ }
1680
+ );
1522
1681
  }
1523
1682
  function MessageEntry({
1524
1683
  entry,
1525
1684
  onOptionSelect,
1526
1685
  defaultExpanded,
1527
1686
  richContentRenderers,
1687
+ markdownComponents,
1688
+ userVariant,
1689
+ assistantVariant,
1690
+ scale,
1691
+ showAvatars = true,
1692
+ showTimestamp = true,
1693
+ renderMetadata,
1694
+ metadata,
1528
1695
  className
1529
1696
  }) {
1530
1697
  switch (entry.entryType.type) {
1531
1698
  case "user_message":
1532
- return /* @__PURE__ */ jsxRuntime.jsx(UserMessage, { entry, className });
1699
+ return /* @__PURE__ */ jsxRuntime.jsx(
1700
+ UserMessage,
1701
+ {
1702
+ entry,
1703
+ variant: userVariant,
1704
+ scale,
1705
+ showAvatar: showAvatars,
1706
+ showTimestamp,
1707
+ renderMetadata,
1708
+ metadata,
1709
+ className
1710
+ }
1711
+ );
1533
1712
  case "assistant_message":
1534
1713
  return /* @__PURE__ */ jsxRuntime.jsx(
1535
1714
  AssistantMessage,
1536
1715
  {
1537
1716
  entry,
1717
+ variant: assistantVariant,
1718
+ scale,
1719
+ showAvatar: showAvatars,
1720
+ showTimestamp,
1538
1721
  onOptionSelect,
1539
1722
  richContentRenderers,
1723
+ markdownComponents,
1724
+ renderMetadata,
1725
+ metadata,
1540
1726
  className
1541
1727
  }
1542
1728
  );
1543
1729
  case "thinking":
1544
- return /* @__PURE__ */ jsxRuntime.jsx(ThinkingMessage, { entry, className });
1730
+ return /* @__PURE__ */ jsxRuntime.jsx(ThinkingMessage, { entry, scale, showAvatar: showAvatars, className });
1545
1731
  case "tool_call":
1546
- return /* @__PURE__ */ jsxRuntime.jsx(ToolCallMessage, { entry, defaultExpanded, className });
1732
+ return /* @__PURE__ */ jsxRuntime.jsx(ToolCallMessage, { entry, scale, showAvatar: showAvatars, defaultExpanded, className });
1547
1733
  case "error":
1548
- return /* @__PURE__ */ jsxRuntime.jsx(ErrorMessage, { entry, className });
1734
+ return /* @__PURE__ */ jsxRuntime.jsx(ErrorMessage, { entry, scale, showAvatar: showAvatars, className });
1549
1735
  default:
1550
1736
  return null;
1551
1737
  }
@@ -2164,6 +2350,13 @@ var DEFAULT_DISPLAY_CONFIG = {
2164
2350
  defaultExpanded: false,
2165
2351
  animationDuration: 300
2166
2352
  };
2353
+ var DEFAULT_STYLE_CONFIG = {
2354
+ userVariant: "bubble",
2355
+ assistantVariant: "bubble",
2356
+ scale: "compact",
2357
+ showTimestamp: true,
2358
+ showAvatars: true
2359
+ };
2167
2360
  var DisplayModeContext = react.createContext(null);
2168
2361
  function DisplayModeProvider({ children, initialConfig }) {
2169
2362
  const [config, setConfigState] = react.useState({
@@ -2217,6 +2410,14 @@ function MessageList({
2217
2410
  onWidgetAction,
2218
2411
  autoScroll = true,
2219
2412
  richContentRenderers,
2413
+ userVariant,
2414
+ assistantVariant,
2415
+ scale,
2416
+ showAvatars = true,
2417
+ showTimestamp = true,
2418
+ markdownComponents,
2419
+ renderMetadata,
2420
+ metadata,
2220
2421
  className
2221
2422
  }) {
2222
2423
  const contextConfig = useDisplayConfig();
@@ -2251,7 +2452,8 @@ function MessageList({
2251
2452
  }
2252
2453
  return groupEntriesForCompactMode(entries, config);
2253
2454
  }, [entries, config]);
2254
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("flex-1 overflow-y-auto p-4 space-y-4 ash-scrollbar", className), children: [
2455
+ const densityClass = scale ? `ash-density-${scale}` : void 0;
2456
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("flex-1 overflow-y-auto px-3 py-2 space-y-1 ash-scrollbar", densityClass, className), children: [
2255
2457
  groupedEntries.map((groupedEntry) => {
2256
2458
  if (groupedEntry.type === "single") {
2257
2459
  const entry = groupedEntry.entry;
@@ -2273,15 +2475,23 @@ function MessageList({
2273
2475
  entry,
2274
2476
  onOptionSelect,
2275
2477
  defaultExpanded: config.defaultExpanded,
2276
- richContentRenderers
2478
+ richContentRenderers,
2479
+ markdownComponents,
2480
+ userVariant,
2481
+ assistantVariant,
2482
+ scale,
2483
+ showAvatars,
2484
+ showTimestamp,
2485
+ renderMetadata,
2486
+ metadata
2277
2487
  },
2278
2488
  entry.id
2279
2489
  );
2280
2490
  }
2281
2491
  const toolCalls = extractToolCallsFromGroup(groupedEntry.entries);
2282
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2283
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
2284
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: config.mode === "accordion" ? /* @__PURE__ */ jsxRuntime.jsx(
2492
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ash-message-row flex ash-animate-fade-in pl-1", style: { gap: "var(--ash-message-gap, 0.5rem)" }, children: [
2493
+ showAvatars && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-avatar rounded-md bg-[var(--ash-avatar-assistant-bg)] flex items-center justify-center shrink-0 mt-0.5", style: { width: "var(--ash-avatar-size, 1.25rem)", height: "var(--ash-avatar-size, 1.25rem)" }, children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
2494
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: config.mode === "accordion" ? /* @__PURE__ */ jsxRuntime.jsx(
2285
2495
  StepAccordion,
2286
2496
  {
2287
2497
  toolCalls,
@@ -2297,13 +2507,13 @@ function MessageList({
2297
2507
  ) })
2298
2508
  ] }, groupedEntry.id);
2299
2509
  }),
2300
- streamingContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2301
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
2302
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl p-3 bg-white/5 text-white/80", children: /* @__PURE__ */ jsxRuntime.jsx(StreamingText, { content: streamingContent, isStreaming: true }) }) })
2510
+ streamingContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ash-message-row flex ash-animate-fade-in pl-1", style: { gap: "var(--ash-message-gap, 0.5rem)" }, children: [
2511
+ showAvatars && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-avatar rounded-md bg-[var(--ash-avatar-assistant-bg)] flex items-center justify-center shrink-0 mt-0.5", style: { width: "var(--ash-avatar-size, 1.25rem)", height: "var(--ash-avatar-size, 1.25rem)" }, children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
2512
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-message-bubble rounded-lg bg-white/[0.03] text-white/80 leading-relaxed", style: { padding: "var(--ash-message-padding, 0.5rem 0.75rem)", fontSize: "var(--ash-font-size-base, 13px)" }, children: /* @__PURE__ */ jsxRuntime.jsx(StreamingText, { content: streamingContent, isStreaming: true }) }) })
2303
2513
  ] }),
2304
- loading && !streamingContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2305
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
2306
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl p-3 bg-white/5", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingIndicator, { variant: "dots" }) })
2514
+ loading && !streamingContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ash-message-row flex ash-animate-fade-in pl-1", style: { gap: "var(--ash-message-gap, 0.5rem)" }, children: [
2515
+ showAvatars && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-avatar rounded-md bg-[var(--ash-avatar-assistant-bg)] flex items-center justify-center shrink-0 mt-0.5", style: { width: "var(--ash-avatar-size, 1.25rem)", height: "var(--ash-avatar-size, 1.25rem)" }, children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
2516
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg px-3 py-1.5 bg-white/[0.03]", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingIndicator, { variant: "dots" }) })
2307
2517
  ] }),
2308
2518
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
2309
2519
  ] });
@@ -3120,6 +3330,21 @@ var typography = {
3120
3330
  relaxed: "1.75"
3121
3331
  }
3122
3332
  };
3333
+ var cssVars = {
3334
+ fontSize: {
3335
+ base: "--ash-font-size-base",
3336
+ sm: "--ash-font-size-sm",
3337
+ xs: "--ash-font-size-xs",
3338
+ code: "--ash-font-size-code"
3339
+ },
3340
+ accent: "--ash-accent",
3341
+ accentForeground: "--ash-accent-foreground",
3342
+ surfaceDark: "--ash-surface-dark",
3343
+ surfaceDarker: "--ash-surface-darker",
3344
+ surfaceCard: "--ash-surface-card",
3345
+ surfaceElevated: "--ash-surface-elevated",
3346
+ surfaceBorder: "--ash-surface-border"
3347
+ };
3123
3348
  var keyframes = {
3124
3349
  slideUp: {
3125
3350
  from: { opacity: "0", transform: "translateY(20px)" },
@@ -3238,6 +3463,10 @@ function tokensToCssVariables(prefix = "ash") {
3238
3463
  if (key !== "DEFAULT") vars[`--${prefix}-border-${key}`] = value;
3239
3464
  else vars[`--${prefix}-border`] = value;
3240
3465
  });
3466
+ vars[`--${prefix}-font-size-base`] = "14px";
3467
+ vars[`--${prefix}-font-size-sm`] = "12px";
3468
+ vars[`--${prefix}-font-size-xs`] = "11px";
3469
+ vars[`--${prefix}-font-size-code`] = "13px";
3241
3470
  return vars;
3242
3471
  }
3243
3472
  var inlineStyles = {
@@ -3493,19 +3722,95 @@ function useFileUpload({
3493
3722
  openFilePicker
3494
3723
  };
3495
3724
  }
3725
+
3726
+ // src/hooks/middleware.ts
3727
+ async function applyRequestMiddleware(middlewares, request) {
3728
+ let current = { ...request };
3729
+ for (const mw of middlewares) {
3730
+ if (mw.onRequest) {
3731
+ try {
3732
+ const result = await mw.onRequest(current);
3733
+ if (result?.error) {
3734
+ return { request: current, error: result.error };
3735
+ }
3736
+ if (result) {
3737
+ current = {
3738
+ ...current,
3739
+ prompt: result.prompt ?? current.prompt,
3740
+ sessionContext: result.sessionContext ?? current.sessionContext,
3741
+ metadata: {
3742
+ ...current.metadata,
3743
+ ...result.metadata
3744
+ }
3745
+ };
3746
+ }
3747
+ } catch (err) {
3748
+ const errorMessage = err instanceof Error ? err.message : "Middleware error";
3749
+ return { request: current, error: errorMessage };
3750
+ }
3751
+ }
3752
+ }
3753
+ return { request: current };
3754
+ }
3755
+ async function applyEventMiddleware(middlewares, event) {
3756
+ let current = event;
3757
+ for (const mw of middlewares) {
3758
+ if (!current) break;
3759
+ if (mw.onEvent) {
3760
+ try {
3761
+ current = await mw.onEvent(current);
3762
+ } catch (err) {
3763
+ console.error("[middleware] onEvent error:", err);
3764
+ }
3765
+ }
3766
+ }
3767
+ return current;
3768
+ }
3769
+ async function callMiddlewareComplete(middlewares, sessionId) {
3770
+ for (const mw of middlewares) {
3771
+ if (mw.onComplete) {
3772
+ try {
3773
+ await mw.onComplete(sessionId);
3774
+ } catch (err) {
3775
+ console.error("[middleware] onComplete error:", err);
3776
+ }
3777
+ }
3778
+ }
3779
+ }
3780
+ async function callMiddlewareError(middlewares, error) {
3781
+ for (const mw of middlewares) {
3782
+ if (mw.onError) {
3783
+ try {
3784
+ await mw.onError(error);
3785
+ } catch (err) {
3786
+ console.error("[middleware] onError error:", err);
3787
+ }
3788
+ }
3789
+ }
3790
+ }
3791
+
3792
+ // src/hooks/useAgentChat.ts
3496
3793
  function useAgentChat(options) {
3497
3794
  const {
3498
3795
  createStream,
3796
+ subscribeToSession,
3499
3797
  initialSessionId,
3500
3798
  initialEntries = [],
3501
3799
  onSessionStart,
3502
3800
  onSessionEnd,
3503
3801
  onError,
3504
- onSandboxLog
3802
+ onSandboxLog,
3803
+ onReconnect,
3804
+ maxReconnectAttempts = 3,
3805
+ reconnectBaseDelay = 1e3,
3806
+ onBeforeSend,
3807
+ onEvent,
3808
+ middleware
3505
3809
  } = options;
3506
3810
  const [historyEntries, setHistoryEntries] = react.useState(initialEntries);
3507
3811
  const [streamingEntries, setStreamingEntries] = react.useState([]);
3508
3812
  const [isStreaming, setIsStreaming] = react.useState(false);
3813
+ const [isReconnecting, setIsReconnecting] = react.useState(false);
3509
3814
  const [error, setError] = react.useState(null);
3510
3815
  const [sessionId, setSessionId] = react.useState(initialSessionId || null);
3511
3816
  const abortControllerRef = react.useRef(null);
@@ -3513,6 +3818,16 @@ function useAgentChat(options) {
3513
3818
  const currentTextIdRef = react.useRef(null);
3514
3819
  const pendingToolCallsRef = react.useRef(/* @__PURE__ */ new Map());
3515
3820
  const hadToolCallSinceTextRef = react.useRef(false);
3821
+ const reconnectAttemptsRef = react.useRef(0);
3822
+ const eventCountRef = react.useRef(0);
3823
+ const sessionIdRef = react.useRef(sessionId);
3824
+ const streamingEntriesRef = react.useRef([]);
3825
+ react.useEffect(() => {
3826
+ sessionIdRef.current = sessionId;
3827
+ }, [sessionId]);
3828
+ react.useEffect(() => {
3829
+ streamingEntriesRef.current = streamingEntries;
3830
+ }, [streamingEntries]);
3516
3831
  const entries = [...historyEntries, ...streamingEntries];
3517
3832
  const emitStreamingEntries = react.useCallback((newEntries) => {
3518
3833
  setStreamingEntries([...newEntries]);
@@ -3627,7 +3942,7 @@ function useAgentChat(options) {
3627
3942
  setError(event.error);
3628
3943
  onError?.(event.error);
3629
3944
  newEntries.push({
3630
- id: `error-${Date.now()}`,
3945
+ id: `error-${Date.now()}-${Math.random().toString(36).slice(2)}`,
3631
3946
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3632
3947
  entryType: { type: "error", message: event.error, code: event.code },
3633
3948
  content: event.error
@@ -3646,12 +3961,112 @@ function useAgentChat(options) {
3646
3961
  }
3647
3962
  return newEntries;
3648
3963
  }, [sessionId, onSessionStart, onSessionEnd, onError, onSandboxLog, createTextEntry, resetStreamingState]);
3964
+ const attemptReconnect = react.useCallback(async (targetSessionId, currentEntries) => {
3965
+ if (!subscribeToSession) {
3966
+ return false;
3967
+ }
3968
+ const attempt = reconnectAttemptsRef.current + 1;
3969
+ if (attempt > maxReconnectAttempts) {
3970
+ console.warn(`[useAgentChat] Max reconnection attempts (${maxReconnectAttempts}) exceeded`);
3971
+ return false;
3972
+ }
3973
+ reconnectAttemptsRef.current = attempt;
3974
+ setIsReconnecting(true);
3975
+ onReconnect?.(attempt, targetSessionId);
3976
+ const delay = reconnectBaseDelay * Math.pow(2, attempt - 1);
3977
+ console.log(`[useAgentChat] Reconnection attempt ${attempt}/${maxReconnectAttempts} in ${delay}ms`);
3978
+ await new Promise((resolve) => setTimeout(resolve, delay));
3979
+ const controller = new AbortController();
3980
+ abortControllerRef.current = controller;
3981
+ try {
3982
+ const stream = subscribeToSession(targetSessionId, controller.signal);
3983
+ let localStreamingEntries = [...currentEntries];
3984
+ for await (const event of stream) {
3985
+ if (controller.signal.aborted) break;
3986
+ eventCountRef.current++;
3987
+ localStreamingEntries = processEvent(event, localStreamingEntries);
3988
+ emitStreamingEntries(localStreamingEntries);
3989
+ if (event.type === "complete" || event.type === "session_end" || event.type === "error") {
3990
+ reconnectAttemptsRef.current = 0;
3991
+ setIsReconnecting(false);
3992
+ if (event.type === "complete" && localStreamingEntries.length > 0) {
3993
+ setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3994
+ setStreamingEntries([]);
3995
+ }
3996
+ return true;
3997
+ }
3998
+ }
3999
+ reconnectAttemptsRef.current = 0;
4000
+ setIsReconnecting(false);
4001
+ if (localStreamingEntries.length > 0) {
4002
+ setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
4003
+ setStreamingEntries([]);
4004
+ }
4005
+ return true;
4006
+ } catch (err) {
4007
+ const isAbort = err.name === "AbortError" || err.message?.includes("aborted") || err.message?.includes("BodyStreamBuffer");
4008
+ if (isAbort && !controller.signal.aborted) {
4009
+ console.log(`[useAgentChat] Reconnection stream interrupted, will retry...`);
4010
+ setIsReconnecting(false);
4011
+ return attemptReconnect(targetSessionId, streamingEntriesRef.current);
4012
+ }
4013
+ setIsReconnecting(false);
4014
+ if (!controller.signal.aborted) {
4015
+ const errorMessage = err instanceof Error ? err.message : "Reconnection failed";
4016
+ console.error(`[useAgentChat] Reconnection failed:`, errorMessage);
4017
+ }
4018
+ return false;
4019
+ }
4020
+ }, [subscribeToSession, maxReconnectAttempts, reconnectBaseDelay, onReconnect, processEvent, emitStreamingEntries]);
3649
4021
  const send = react.useCallback(async (prompt) => {
3650
4022
  if (isStreaming) return;
4023
+ let finalPrompt = prompt;
4024
+ let additionalMetadata = {};
4025
+ let additionalContext;
4026
+ if (onBeforeSend) {
4027
+ try {
4028
+ const result = await onBeforeSend({
4029
+ prompt,
4030
+ sessionId,
4031
+ entries: [...historyEntries, ...streamingEntries]
4032
+ });
4033
+ if (result?.cancel) {
4034
+ return;
4035
+ }
4036
+ if (result?.prompt !== void 0) finalPrompt = result.prompt;
4037
+ if (result?.metadata) additionalMetadata = result.metadata;
4038
+ if (result?.sessionContext) additionalContext = result.sessionContext;
4039
+ } catch (err) {
4040
+ const errorMessage = err instanceof Error ? err.message : "Send cancelled";
4041
+ setError(errorMessage);
4042
+ onError?.(errorMessage);
4043
+ return;
4044
+ }
4045
+ }
4046
+ if (middleware?.length) {
4047
+ const middlewareRequest = {
4048
+ prompt: finalPrompt,
4049
+ sessionId,
4050
+ metadata: additionalMetadata,
4051
+ sessionContext: additionalContext
4052
+ };
4053
+ const { request, error: middlewareError } = await applyRequestMiddleware(middleware, middlewareRequest);
4054
+ if (middlewareError) {
4055
+ setError(middlewareError);
4056
+ onError?.(middlewareError);
4057
+ await callMiddlewareError(middleware, middlewareError);
4058
+ return;
4059
+ }
4060
+ finalPrompt = request.prompt;
4061
+ additionalMetadata = request.metadata || {};
4062
+ additionalContext = request.sessionContext;
4063
+ }
3651
4064
  setIsStreaming(true);
3652
4065
  setError(null);
4066
+ reconnectAttemptsRef.current = 0;
4067
+ eventCountRef.current = 0;
3653
4068
  const userEntry = {
3654
- id: `user-${Date.now()}`,
4069
+ id: `user-${Date.now()}-${Math.random().toString(36).slice(2)}`,
3655
4070
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3656
4071
  entryType: { type: "user_message" },
3657
4072
  content: prompt
@@ -3661,39 +4076,91 @@ function useAgentChat(options) {
3661
4076
  const controller = new AbortController();
3662
4077
  abortControllerRef.current = controller;
3663
4078
  let localStreamingEntries = [];
4079
+ let completedSessionId = null;
3664
4080
  try {
3665
- const stream = createStream(prompt, sessionId || void 0, controller.signal);
4081
+ const stream = createStream(finalPrompt, sessionId || void 0, {
4082
+ signal: controller.signal,
4083
+ metadata: Object.keys(additionalMetadata).length > 0 ? additionalMetadata : void 0,
4084
+ sessionContext: additionalContext
4085
+ });
3666
4086
  for await (const event of stream) {
3667
4087
  if (controller.signal.aborted) break;
3668
- localStreamingEntries = processEvent(event, localStreamingEntries);
4088
+ eventCountRef.current++;
4089
+ if (event.type === "session_start" && event.sessionId) {
4090
+ completedSessionId = event.sessionId;
4091
+ }
4092
+ if (onEvent) {
4093
+ try {
4094
+ await onEvent(event);
4095
+ } catch (err) {
4096
+ console.error("[useAgentChat] onEvent error:", err);
4097
+ }
4098
+ }
4099
+ let processedEvent = event;
4100
+ if (middleware?.length) {
4101
+ processedEvent = await applyEventMiddleware(middleware, event);
4102
+ }
4103
+ if (!processedEvent) continue;
4104
+ localStreamingEntries = processEvent(processedEvent, localStreamingEntries);
3669
4105
  emitStreamingEntries(localStreamingEntries);
3670
4106
  }
3671
4107
  if (localStreamingEntries.length > 0) {
3672
4108
  setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3673
4109
  setStreamingEntries([]);
3674
4110
  }
4111
+ reconnectAttemptsRef.current = 0;
4112
+ if (middleware?.length && (completedSessionId || sessionIdRef.current)) {
4113
+ await callMiddlewareComplete(middleware, completedSessionId || sessionIdRef.current || "");
4114
+ }
3675
4115
  } catch (err) {
3676
- if (err.name !== "AbortError") {
4116
+ const isAbort = err.name === "AbortError" || err.message?.includes("aborted") || err.message?.includes("BodyStreamBuffer");
4117
+ if (isAbort && !controller.signal.aborted) {
4118
+ const currentSessionId = sessionIdRef.current;
4119
+ const hadEvents = eventCountRef.current > 0;
4120
+ console.log(`[useAgentChat] Stream interrupted. sessionId=${currentSessionId}, events=${eventCountRef.current}`);
4121
+ if (currentSessionId && hadEvents && subscribeToSession) {
4122
+ console.log(`[useAgentChat] Attempting auto-reconnection...`);
4123
+ const reconnected = await attemptReconnect(currentSessionId, streamingEntriesRef.current);
4124
+ if (reconnected) {
4125
+ return;
4126
+ }
4127
+ const errorMsg = "Connection lost. Please try again.";
4128
+ setError(errorMsg);
4129
+ onError?.(errorMsg);
4130
+ if (middleware?.length) {
4131
+ await callMiddlewareError(middleware, errorMsg);
4132
+ }
4133
+ }
4134
+ } else if (!isAbort) {
3677
4135
  const errorMessage = err instanceof Error ? err.message : "Unknown error";
3678
4136
  setError(errorMessage);
3679
4137
  onError?.(errorMessage);
4138
+ if (middleware?.length) {
4139
+ await callMiddlewareError(middleware, errorMessage);
4140
+ }
3680
4141
  }
3681
4142
  } finally {
3682
4143
  setIsStreaming(false);
4144
+ setIsReconnecting(false);
3683
4145
  abortControllerRef.current = null;
3684
4146
  resetStreamingState();
3685
4147
  }
3686
- }, [isStreaming, sessionId, createStream, processEvent, emitStreamingEntries, resetStreamingState, onError]);
4148
+ }, [isStreaming, sessionId, historyEntries, streamingEntries, createStream, subscribeToSession, processEvent, emitStreamingEntries, resetStreamingState, onError, attemptReconnect, onBeforeSend, onEvent, middleware]);
3687
4149
  const stop = react.useCallback(() => {
4150
+ reconnectAttemptsRef.current = maxReconnectAttempts + 1;
4151
+ setIsReconnecting(false);
3688
4152
  if (abortControllerRef.current) {
3689
4153
  abortControllerRef.current.abort();
3690
4154
  }
3691
- }, []);
4155
+ }, [maxReconnectAttempts]);
3692
4156
  const clear = react.useCallback(() => {
3693
4157
  setHistoryEntries([]);
3694
4158
  resetStreamingState();
3695
4159
  setSessionId(initialSessionId || null);
3696
4160
  setError(null);
4161
+ setIsReconnecting(false);
4162
+ reconnectAttemptsRef.current = 0;
4163
+ eventCountRef.current = 0;
3697
4164
  }, [initialSessionId, resetStreamingState]);
3698
4165
  const setEntries = react.useCallback((newEntries) => {
3699
4166
  resetStreamingState();
@@ -3709,6 +4176,7 @@ function useAgentChat(options) {
3709
4176
  return {
3710
4177
  entries,
3711
4178
  isStreaming,
4179
+ isReconnecting,
3712
4180
  error,
3713
4181
  sessionId,
3714
4182
  send,
@@ -3794,6 +4262,7 @@ exports.ActionIcon = ActionIcon;
3794
4262
  exports.AgentToolCard = AgentToolCard;
3795
4263
  exports.AlertCircleIcon = AlertCircleIcon;
3796
4264
  exports.AlertTriangleIcon = AlertTriangleIcon;
4265
+ exports.ArrowUpIcon = ArrowUpIcon;
3797
4266
  exports.AssistantMessage = AssistantMessage;
3798
4267
  exports.BotIcon = BotIcon;
3799
4268
  exports.BrainIcon = BrainIcon;
@@ -3813,6 +4282,7 @@ exports.CompactToolRow = CompactToolRow;
3813
4282
  exports.CompactToolStatusLine = CompactToolStatusLine;
3814
4283
  exports.CopyIcon = CopyIcon;
3815
4284
  exports.DEFAULT_DISPLAY_CONFIG = DEFAULT_DISPLAY_CONFIG;
4285
+ exports.DEFAULT_STYLE_CONFIG = DEFAULT_STYLE_CONFIG;
3816
4286
  exports.DisplayModeProvider = DisplayModeProvider;
3817
4287
  exports.DisplayModeToggle = DisplayModeToggle;
3818
4288
  exports.EditIcon = EditIcon;
@@ -3824,6 +4294,7 @@ exports.FileIcon = FileIcon;
3824
4294
  exports.FilePlusIcon = FilePlusIcon;
3825
4295
  exports.FolderSearchIcon = FolderSearchIcon;
3826
4296
  exports.GlobeIcon = GlobeIcon;
4297
+ exports.HomeIcon = HomeIcon;
3827
4298
  exports.InfoIcon = InfoIcon;
3828
4299
  exports.JsonDisplay = JsonDisplay;
3829
4300
  exports.LazyMarkdown = LazyMarkdown;
@@ -3834,6 +4305,7 @@ exports.LogsPanel = LogsPanel;
3834
4305
  exports.MessageEntry = MessageEntry;
3835
4306
  exports.MessageList = MessageList;
3836
4307
  exports.MessageSquareIcon = MessageSquareIcon;
4308
+ exports.MicrophoneIcon = MicrophoneIcon;
3837
4309
  exports.MoonIcon = MoonIcon;
3838
4310
  exports.OptionCards = OptionCards;
3839
4311
  exports.PaperclipIcon = PaperclipIcon;
@@ -3862,10 +4334,15 @@ exports.UserMessage = UserMessage;
3862
4334
  exports.XCircleIcon = XCircleIcon;
3863
4335
  exports.XIcon = XIcon;
3864
4336
  exports.allKeyframesCss = allKeyframesCss;
4337
+ exports.applyEventMiddleware = applyEventMiddleware;
4338
+ exports.applyRequestMiddleware = applyRequestMiddleware;
3865
4339
  exports.borderRadius = borderRadius;
4340
+ exports.callMiddlewareComplete = callMiddlewareComplete;
4341
+ exports.callMiddlewareError = callMiddlewareError;
3866
4342
  exports.cn = cn;
3867
4343
  exports.colors = colors;
3868
4344
  exports.createToolCall = createToolCall;
4345
+ exports.cssVars = cssVars;
3869
4346
  exports.extractTextContent = extractTextContent;
3870
4347
  exports.extractToolCallsFromGroup = extractToolCallsFromGroup;
3871
4348
  exports.formatElapsedTime = formatElapsedTime;