@ash-cloud/ash-ui 0.0.9 → 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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { lazy, createContext, useState, useRef, useMemo, useEffect, Suspense, useCallback, useContext } from 'react';
1
+ import { lazy, createContext, useState, useCallback, useRef, useMemo, useEffect, Suspense, Fragment as Fragment$1, useContext } from 'react';
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  // src/components/ToolCallCard.tsx
@@ -692,6 +692,26 @@ function ErrorIcon({ className }) {
692
692
  /* @__PURE__ */ jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
693
693
  ] });
694
694
  }
695
+ function MicrophoneIcon({ className }) {
696
+ return /* @__PURE__ */ jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
697
+ /* @__PURE__ */ jsx("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
698
+ /* @__PURE__ */ jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
699
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
700
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
701
+ ] });
702
+ }
703
+ function HomeIcon({ className }) {
704
+ return /* @__PURE__ */ jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
705
+ /* @__PURE__ */ jsx("path", { d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
706
+ /* @__PURE__ */ jsx("polyline", { points: "9 22 9 12 15 12 15 22" })
707
+ ] });
708
+ }
709
+ function ArrowUpIcon({ className }) {
710
+ return /* @__PURE__ */ jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
711
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
712
+ /* @__PURE__ */ jsx("polyline", { points: "5 12 12 5 19 12" })
713
+ ] });
714
+ }
695
715
  function StatusIndicator({ status, size = "sm", className }) {
696
716
  const sizeClasses = {
697
717
  sm: "w-2 h-2",
@@ -752,14 +772,41 @@ function CodeBlock({
752
772
  className
753
773
  }) {
754
774
  const [expanded, setExpanded] = useState(false);
775
+ const [copied, setCopied] = useState(false);
755
776
  const lines = children.split("\n");
756
777
  const isLong = lines.length > 10 || children.length > 500;
757
- return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
778
+ const handleCopy = useCallback(async () => {
779
+ try {
780
+ await navigator.clipboard.writeText(children);
781
+ setCopied(true);
782
+ setTimeout(() => setCopied(false), 2e3);
783
+ } catch (err) {
784
+ console.error("Failed to copy:", err);
785
+ }
786
+ }, [children]);
787
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative rounded-lg overflow-hidden border border-white/10", className), children: [
788
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2 bg-[var(--ash-code-header-bg)] border-b border-white/10", children: [
789
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-white/60 font-medium", children: language || "code" }),
790
+ /* @__PURE__ */ jsx(
791
+ "button",
792
+ {
793
+ onClick: handleCopy,
794
+ className: "flex items-center gap-1.5 text-xs text-white/60 hover:text-white transition-colors",
795
+ children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
796
+ /* @__PURE__ */ jsx(CheckIcon, { className: "w-3.5 h-3.5" }),
797
+ /* @__PURE__ */ jsx("span", { children: "Copied!" })
798
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
799
+ /* @__PURE__ */ jsx(CopyIcon, { className: "w-3.5 h-3.5" }),
800
+ /* @__PURE__ */ jsx("span", { children: "Copy" })
801
+ ] })
802
+ }
803
+ )
804
+ ] }),
758
805
  /* @__PURE__ */ jsx(
759
806
  "pre",
760
807
  {
761
808
  className: cn(
762
- "ash-tool-code-block text-xs font-mono text-white/90 p-3 rounded-xl overflow-x-auto whitespace-pre-wrap break-words",
809
+ "text-xs font-mono text-white/90 p-4 bg-black/30 overflow-x-auto whitespace-pre-wrap break-words",
763
810
  !expanded && isLong && "overflow-y-hidden"
764
811
  ),
765
812
  style: !expanded && isLong ? { maxHeight } : void 0,
@@ -943,7 +990,7 @@ function SectionHeader({ children }) {
943
990
  return /* @__PURE__ */ jsx("div", { className: "ash-tool-section-header", children });
944
991
  }
945
992
  function SectionContent({ children }) {
946
- return /* @__PURE__ */ jsx("div", { className: "px-3 py-2", children });
993
+ return /* @__PURE__ */ jsx("div", { className: "px-3 py-1.5", children });
947
994
  }
948
995
  function CommandRunDetails({ action }) {
949
996
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1064,7 +1111,7 @@ function WebSearchDetails({ action }) {
1064
1111
  /* @__PURE__ */ jsx(SectionContent, { children: /* @__PURE__ */ jsx("span", { className: "text-sm text-white/90", children: action.query }) })
1065
1112
  ] });
1066
1113
  }
1067
- function McpToolDetails({ action }) {
1114
+ function McpToolDetails({ action, isError }) {
1068
1115
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1069
1116
  /* @__PURE__ */ jsx(SectionHeader, { children: "TOOL" }),
1070
1117
  /* @__PURE__ */ jsx(SectionContent, { children: /* @__PURE__ */ jsxs("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: [
@@ -1077,20 +1124,20 @@ function McpToolDetails({ action }) {
1077
1124
  /* @__PURE__ */ jsx(SectionContent, { children: /* @__PURE__ */ jsx(JsonDisplay, { value: action.arguments }) })
1078
1125
  ] }),
1079
1126
  action.result && /* @__PURE__ */ jsxs(Fragment, { children: [
1080
- /* @__PURE__ */ jsx(SectionHeader, { children: "RESULT" }),
1081
- /* @__PURE__ */ jsx(SectionContent, { children: action.result.type === "markdown" ? /* @__PURE__ */ jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsx(JsonDisplay, { value: action.result.value }) })
1127
+ /* @__PURE__ */ jsx(SectionHeader, { children: isError ? "ERROR" : "RESULT" }),
1128
+ /* @__PURE__ */ jsx(SectionContent, { children: /* @__PURE__ */ jsx("div", { className: isError ? "text-red-400" : "", children: action.result.type === "markdown" ? /* @__PURE__ */ jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsx(JsonDisplay, { value: action.result.value }) }) })
1082
1129
  ] })
1083
1130
  ] });
1084
1131
  }
1085
- function GenericToolDetails({ action }) {
1132
+ function GenericToolDetails({ action, isError }) {
1086
1133
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1087
1134
  action.arguments && /* @__PURE__ */ jsxs(Fragment, { children: [
1088
1135
  /* @__PURE__ */ jsx(SectionHeader, { children: "ARGS" }),
1089
1136
  /* @__PURE__ */ jsx(SectionContent, { children: /* @__PURE__ */ jsx(JsonDisplay, { value: action.arguments }) })
1090
1137
  ] }),
1091
1138
  action.result && /* @__PURE__ */ jsxs(Fragment, { children: [
1092
- /* @__PURE__ */ jsx(SectionHeader, { children: "RESULT" }),
1093
- /* @__PURE__ */ jsx(SectionContent, { children: action.result.type === "markdown" ? /* @__PURE__ */ jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsx(JsonDisplay, { value: action.result.value }) })
1139
+ /* @__PURE__ */ jsx(SectionHeader, { children: isError ? "ERROR" : "RESULT" }),
1140
+ /* @__PURE__ */ jsx(SectionContent, { children: /* @__PURE__ */ jsx("div", { className: isError ? "text-red-400" : "", children: action.result.type === "markdown" ? /* @__PURE__ */ jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsx(JsonDisplay, { value: action.result.value }) }) })
1094
1141
  ] })
1095
1142
  ] });
1096
1143
  }
@@ -1162,7 +1209,7 @@ function TodoWriteDetails({ action }) {
1162
1209
  }) }) })
1163
1210
  ] });
1164
1211
  }
1165
- function ToolDetails({ actionType }) {
1212
+ function ToolDetails({ actionType, isError }) {
1166
1213
  switch (actionType.action) {
1167
1214
  case "command_run":
1168
1215
  return /* @__PURE__ */ jsx(CommandRunDetails, { action: actionType });
@@ -1181,9 +1228,9 @@ function ToolDetails({ actionType }) {
1181
1228
  case "web_search":
1182
1229
  return /* @__PURE__ */ jsx(WebSearchDetails, { action: actionType });
1183
1230
  case "mcp_tool":
1184
- return /* @__PURE__ */ jsx(McpToolDetails, { action: actionType });
1231
+ return /* @__PURE__ */ jsx(McpToolDetails, { action: actionType, isError });
1185
1232
  case "generic_tool":
1186
- return /* @__PURE__ */ jsx(GenericToolDetails, { action: actionType });
1233
+ return /* @__PURE__ */ jsx(GenericToolDetails, { action: actionType, isError });
1187
1234
  case "todo_write":
1188
1235
  return /* @__PURE__ */ jsx(TodoWriteDetails, { action: actionType });
1189
1236
  default:
@@ -1244,7 +1291,7 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1244
1291
  "div",
1245
1292
  {
1246
1293
  className: cn(
1247
- "rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
1294
+ "rounded-lg border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
1248
1295
  statusClasses[status],
1249
1296
  className
1250
1297
  ),
@@ -1254,35 +1301,35 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1254
1301
  {
1255
1302
  onClick: () => canExpand && setExpanded(!expanded),
1256
1303
  className: cn(
1257
- "w-full px-4 py-3 flex items-center justify-between transition-colors",
1304
+ "w-full px-3 py-2 flex items-center justify-between transition-colors",
1258
1305
  canExpand ? "hover:bg-white/5 cursor-pointer" : "cursor-default"
1259
1306
  ),
1260
1307
  disabled: !canExpand,
1261
1308
  children: [
1262
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0 flex-1", children: [
1309
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0 flex-1", children: [
1263
1310
  /* @__PURE__ */ jsx("div", { className: cn(
1264
- "w-6 h-6 rounded-lg flex items-center justify-center shrink-0",
1311
+ "w-5 h-5 rounded flex items-center justify-center shrink-0",
1265
1312
  status === "pending" ? "bg-yellow-500/20" : status === "failed" ? "bg-red-500/20" : "bg-[var(--ash-accent)]/20"
1266
1313
  ), children: /* @__PURE__ */ jsx(
1267
1314
  ActionIcon,
1268
1315
  {
1269
1316
  actionType,
1270
1317
  className: cn(
1271
- "w-3.5 h-3.5",
1318
+ "w-3 h-3",
1272
1319
  status === "pending" ? "text-yellow-400" : status === "failed" ? "text-red-400" : "text-[var(--ash-accent)]"
1273
1320
  )
1274
1321
  }
1275
1322
  ) }),
1276
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-white shrink-0", children: getActionLabel(actionType) }),
1277
- /* @__PURE__ */ jsx("span", { className: "font-mono text-sm truncate text-white/60 min-w-0", children: summary })
1323
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-medium text-white shrink-0", children: getActionLabel(actionType) }),
1324
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[12px] truncate text-white/50 min-w-0", children: summary })
1278
1325
  ] }),
1279
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
1326
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
1280
1327
  /* @__PURE__ */ jsx(StatusIndicator, { status, size: "sm" }),
1281
1328
  canExpand && /* @__PURE__ */ jsx(
1282
1329
  ChevronDownIcon,
1283
1330
  {
1284
1331
  className: cn(
1285
- "w-4 h-4 text-white/40 transition-transform duration-200",
1332
+ "w-3.5 h-3.5 text-white/30 transition-transform duration-200",
1286
1333
  expanded && "rotate-180"
1287
1334
  )
1288
1335
  }
@@ -1291,11 +1338,15 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1291
1338
  ]
1292
1339
  }
1293
1340
  ),
1294
- expanded && canExpand && /* @__PURE__ */ jsxs("div", { className: "border-t border-white/5 max-h-[400px] overflow-y-auto ash-scrollbar bg-black/20", children: [
1295
- /* @__PURE__ */ jsx(ToolDetails, { actionType }),
1296
- status === "success" && /* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-t border-white/5 bg-[var(--ash-accent)]/5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1297
- /* @__PURE__ */ jsx(CheckIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }),
1298
- /* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--ash-accent)] font-medium", children: "Completed successfully" })
1341
+ expanded && canExpand && /* @__PURE__ */ jsxs("div", { className: "border-t border-white/5 max-h-[350px] overflow-y-auto ash-scrollbar bg-black/20", children: [
1342
+ /* @__PURE__ */ jsx(ToolDetails, { actionType, isError: toolCall.isError || status === "failed" }),
1343
+ status === "success" && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-t border-white/5 bg-[var(--ash-accent)]/5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
1344
+ /* @__PURE__ */ jsx(CheckIcon, { className: "w-3.5 h-3.5 text-[var(--ash-accent)]" }),
1345
+ /* @__PURE__ */ jsx("span", { className: "text-[12px] text-[var(--ash-accent)] font-medium", children: "Completed" })
1346
+ ] }) }),
1347
+ status === "failed" && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 border-t border-red-500/20 bg-red-500/10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
1348
+ /* @__PURE__ */ jsx(XCircleIcon, { className: "w-3.5 h-3.5 text-red-400" }),
1349
+ /* @__PURE__ */ jsx("span", { className: "text-[12px] text-red-400 font-medium", children: "Failed" })
1299
1350
  ] }) })
1300
1351
  ] })
1301
1352
  ]
@@ -1303,15 +1354,69 @@ function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1303
1354
  );
1304
1355
  }
1305
1356
  var ReactMarkdown = lazy(() => import('react-markdown'));
1306
- function LazyMarkdown({ children, fallback, className }) {
1357
+ function LazyMarkdown({ children, fallback, components, className }) {
1307
1358
  const [mounted, setMounted] = useState(false);
1308
1359
  useEffect(() => {
1309
1360
  setMounted(true);
1310
1361
  }, []);
1362
+ const markdownComponents = useMemo(() => {
1363
+ if (!components) return void 0;
1364
+ return components;
1365
+ }, [components]);
1311
1366
  if (!mounted) {
1312
1367
  return /* @__PURE__ */ jsx("span", { className, children: fallback ?? children });
1313
1368
  }
1314
- return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("span", { className, children: fallback ?? children }), children: /* @__PURE__ */ jsx(ReactMarkdown, { children }) });
1369
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("span", { className, children: fallback ?? children }), children: /* @__PURE__ */ jsx(ReactMarkdown, { components: markdownComponents, children }) });
1370
+ }
1371
+ function DefaultMentionBadge({ segment }) {
1372
+ const bgColor = segment.color ? `${segment.color}20` : "rgba(34, 197, 94, 0.2)";
1373
+ const textColor = segment.color || "#22c55e";
1374
+ const borderColor = segment.color ? `${segment.color}40` : "rgba(34, 197, 94, 0.4)";
1375
+ return /* @__PURE__ */ jsxs(
1376
+ "span",
1377
+ {
1378
+ className: "inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium mx-0.5",
1379
+ style: {
1380
+ backgroundColor: bgColor,
1381
+ color: textColor,
1382
+ border: `1px solid ${borderColor}`
1383
+ },
1384
+ title: segment.id ? `ID: ${segment.id}` : void 0,
1385
+ children: [
1386
+ "@",
1387
+ segment.name
1388
+ ]
1389
+ }
1390
+ );
1391
+ }
1392
+ function RichContentRenderer({
1393
+ content,
1394
+ renderers,
1395
+ className
1396
+ }) {
1397
+ return /* @__PURE__ */ jsx("div", { className: cn("rich-content", className), children: content.map((segment, index) => {
1398
+ const key = `segment-${index}`;
1399
+ switch (segment.type) {
1400
+ case "text":
1401
+ return /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(LazyMarkdown, { children: segment.content }) }, key);
1402
+ case "mention":
1403
+ if (renderers?.renderMention) {
1404
+ return /* @__PURE__ */ jsx(Fragment$1, { children: renderers.renderMention({ segment }) }, key);
1405
+ }
1406
+ return /* @__PURE__ */ jsx(DefaultMentionBadge, { segment }, key);
1407
+ case "component":
1408
+ if (renderers?.renderComponent) {
1409
+ return /* @__PURE__ */ jsx(Fragment$1, { children: renderers.renderComponent({ segment }) }, key);
1410
+ }
1411
+ return /* @__PURE__ */ jsxs("code", { className: "text-xs text-orange-400", children: [
1412
+ "[component: ",
1413
+ segment.componentType,
1414
+ "]"
1415
+ ] }, key);
1416
+ default:
1417
+ return null;
1418
+ }
1419
+ }) });
1315
1420
  }
1316
1421
  function OptionCards({ options, onSelect, className }) {
1317
1422
  return /* @__PURE__ */ jsx("div", { className: cn("grid gap-2 mt-3", className), style: {
@@ -1361,6 +1466,36 @@ function OptionCards({ options, onSelect, className }) {
1361
1466
  option.id
1362
1467
  )) });
1363
1468
  }
1469
+ var scaleClasses = {
1470
+ dense: {
1471
+ text: "text-xs",
1472
+ padding: "px-2 py-1",
1473
+ gap: "gap-1.5",
1474
+ avatar: "w-4 h-4",
1475
+ messageGap: "space-y-0.5"
1476
+ },
1477
+ compact: {
1478
+ text: "text-[13px]",
1479
+ padding: "px-3 py-2",
1480
+ gap: "gap-2",
1481
+ avatar: "w-5 h-5",
1482
+ messageGap: "space-y-1"
1483
+ },
1484
+ default: {
1485
+ text: "text-sm",
1486
+ padding: "px-4 py-3",
1487
+ gap: "gap-3",
1488
+ avatar: "w-6 h-6",
1489
+ messageGap: "space-y-2"
1490
+ },
1491
+ comfortable: {
1492
+ text: "text-[15px]",
1493
+ padding: "px-5 py-4",
1494
+ gap: "gap-4",
1495
+ avatar: "w-7 h-7",
1496
+ messageGap: "space-y-3"
1497
+ }
1498
+ };
1364
1499
  function parseFilesFromContent(content) {
1365
1500
  const fileMarker = "[Uploaded files available at /uploads/]";
1366
1501
  const markerIndex = content.indexOf(fileMarker);
@@ -1372,97 +1507,229 @@ function parseFilesFromContent(content) {
1372
1507
  const files = fileSection.split("\n").filter((line) => line.startsWith("- ")).map((line) => line.substring(2).trim());
1373
1508
  return { text, files };
1374
1509
  }
1375
- function UserMessage({ entry, className }) {
1510
+ function UserMessage({
1511
+ entry,
1512
+ variant = "bubble",
1513
+ scale = "compact",
1514
+ showAvatar = false,
1515
+ // Hidden by default for ChatGPT style
1516
+ showTimestamp = true,
1517
+ renderMetadata,
1518
+ metadata,
1519
+ className
1520
+ }) {
1376
1521
  const { text, files } = parseFilesFromContent(entry.content);
1377
- return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-3 justify-end ash-animate-fade-in", className), children: [
1378
- /* @__PURE__ */ jsxs("div", { className: "max-w-[85%]", children: [
1379
- /* @__PURE__ */ jsx("div", { className: "rounded-2xl p-4 bg-[var(--ash-accent)] text-[var(--ash-accent-foreground)]", children: /* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed whitespace-pre-wrap", children: text || "(files attached)" }) }),
1380
- files.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-2 pt-2 border-t border-[var(--ash-accent-foreground)]/20", children: [
1381
- /* @__PURE__ */ jsxs("div", { className: "text-xs text-[var(--ash-accent-foreground)]/60 mb-1 flex items-center gap-1", children: [
1382
- /* @__PURE__ */ jsx(PaperclipIcon, { className: "w-3 h-3" }),
1383
- "Attached Files"
1522
+ const s = scaleClasses[scale];
1523
+ const variantClasses = {
1524
+ bubble: "rounded-[20px] bg-[var(--ash-user-bg)] text-[var(--ash-user-text)] border border-[var(--ash-user-border)]",
1525
+ plain: "bg-transparent text-[var(--ash-text-primary)]",
1526
+ minimal: "bg-transparent text-[var(--ash-text-secondary)]"
1527
+ };
1528
+ return /* @__PURE__ */ jsxs(
1529
+ "div",
1530
+ {
1531
+ "data-message-role": "user",
1532
+ className: cn("flex justify-end ash-animate-fade-in pr-1", s.gap, className),
1533
+ children: [
1534
+ /* @__PURE__ */ jsxs("div", { className: "max-w-[85%] flex flex-col items-end", children: [
1535
+ /* @__PURE__ */ jsxs("div", { className: cn(variantClasses[variant], variant === "bubble" && "px-4 py-3"), children: [
1536
+ /* @__PURE__ */ jsx("p", { className: cn(s.text, "leading-snug whitespace-pre-wrap"), children: text || "(files attached)" }),
1537
+ files.length > 0 && variant === "bubble" && /* @__PURE__ */ 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__ */ jsxs(
1538
+ "span",
1539
+ {
1540
+ className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-[var(--ash-user-text)]/10 text-[11px]",
1541
+ title: file,
1542
+ children: [
1543
+ /* @__PURE__ */ jsx(PaperclipIcon, { className: "w-2.5 h-2.5 opacity-60" }),
1544
+ file.split(" (")[0]
1545
+ ]
1546
+ },
1547
+ index
1548
+ )) })
1549
+ ] }),
1550
+ renderMetadata ? renderMetadata({ entry, metadata }) : showTimestamp && entry.timestamp && /* @__PURE__ */ jsxs("div", { className: "text-[10px] text-[var(--ash-text-faint)] mt-0.5 mr-1 tabular-nums flex items-center gap-1.5", children: [
1551
+ metadata?.model && /* @__PURE__ */ jsx("span", { children: metadata.model }),
1552
+ metadata?.model && /* @__PURE__ */ jsx("span", { className: "opacity-50", children: "\xB7" }),
1553
+ formatTimestamp(entry.timestamp)
1554
+ ] })
1384
1555
  ] }),
1385
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: files.map((file, index) => /* @__PURE__ */ jsx(
1386
- "span",
1387
- {
1388
- className: "inline-flex items-center px-2 py-0.5 rounded-lg bg-[var(--ash-accent-foreground)]/10 text-xs",
1389
- title: file,
1390
- children: file.split(" (")[0]
1391
- },
1392
- index
1393
- )) })
1394
- ] }),
1395
- entry.timestamp && /* @__PURE__ */ jsx("div", { className: "text-xs text-white/40 mt-2", children: formatTimestamp(entry.timestamp) })
1396
- ] }),
1397
- /* @__PURE__ */ jsx("div", { className: "w-7 h-7 rounded-full bg-white/10 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx(UserIcon, { className: "w-4 h-4 text-white/50" }) })
1398
- ] });
1556
+ showAvatar && /* @__PURE__ */ 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__ */ jsx(UserIcon, { className: "w-3 h-3 text-[var(--ash-text-muted)]" }) })
1557
+ ]
1558
+ }
1559
+ );
1399
1560
  }
1400
- function AssistantMessage({ entry, onOptionSelect, className }) {
1401
- const parsedOptions = parseOptionsFromContent(entry.content);
1561
+ function AssistantMessage({
1562
+ entry,
1563
+ variant = "bubble",
1564
+ scale = "compact",
1565
+ showAvatar = true,
1566
+ showTimestamp = true,
1567
+ onOptionSelect,
1568
+ richContentRenderers,
1569
+ markdownComponents,
1570
+ renderMetadata,
1571
+ metadata,
1572
+ className
1573
+ }) {
1574
+ const parsedOptions = !entry.richContent ? parseOptionsFromContent(entry.content) : null;
1575
+ const s = scaleClasses[scale];
1402
1576
  const handleOptionSelect = (option) => {
1403
1577
  if (onOptionSelect) {
1404
1578
  onOptionSelect(`Option ${option.id}: ${option.label}`);
1405
1579
  }
1406
1580
  };
1407
- return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1408
- /* @__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)]" }) }),
1409
- /* @__PURE__ */ jsxs("div", { className: "flex-1 max-w-[85%]", children: [
1410
- /* @__PURE__ */ jsx("div", { className: "ash-card-glass rounded-2xl p-4", children: /* @__PURE__ */ jsx("div", { className: "ash-message-content prose prose-sm prose-invert max-w-none text-sm leading-relaxed", children: parsedOptions ? /* @__PURE__ */ jsxs(Fragment, { children: [
1411
- parsedOptions.preamble && /* @__PURE__ */ jsx(LazyMarkdown, { children: parsedOptions.preamble }),
1412
- /* @__PURE__ */ jsx(
1413
- OptionCards,
1414
- {
1415
- options: parsedOptions.options,
1416
- onSelect: handleOptionSelect
1417
- }
1418
- )
1419
- ] }) : /* @__PURE__ */ jsx(LazyMarkdown, { children: entry.content }) }) }),
1420
- entry.timestamp && /* @__PURE__ */ jsx("div", { className: "text-xs text-white/40 mt-2", children: formatTimestamp(entry.timestamp) })
1421
- ] })
1422
- ] });
1581
+ const variantClasses = {
1582
+ bubble: "rounded-[16px] bg-[var(--ash-assistant-bg)] border border-[var(--ash-assistant-border)]",
1583
+ plain: "bg-transparent",
1584
+ minimal: "bg-transparent"
1585
+ };
1586
+ const renderContent = (textContent) => {
1587
+ if (entry.richContent && entry.richContent.length > 0) {
1588
+ return /* @__PURE__ */ jsx(
1589
+ RichContentRenderer,
1590
+ {
1591
+ content: entry.richContent,
1592
+ renderers: richContentRenderers
1593
+ }
1594
+ );
1595
+ }
1596
+ return /* @__PURE__ */ jsx(LazyMarkdown, { components: markdownComponents, children: textContent || entry.content });
1597
+ };
1598
+ return /* @__PURE__ */ jsxs(
1599
+ "div",
1600
+ {
1601
+ "data-message-role": "assistant",
1602
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1603
+ children: [
1604
+ showAvatar && /* @__PURE__ */ 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__ */ jsx(SparklesIcon, { className: "w-3 h-3 text-white" }) }),
1605
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1606
+ /* @__PURE__ */ jsx("div", { className: cn(variantClasses[variant], variant === "bubble" && "px-4 py-3"), children: /* @__PURE__ */ 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__ */ jsxs(Fragment, { children: [
1607
+ parsedOptions.preamble && renderContent(parsedOptions.preamble),
1608
+ /* @__PURE__ */ jsx(
1609
+ OptionCards,
1610
+ {
1611
+ options: parsedOptions.options,
1612
+ onSelect: handleOptionSelect
1613
+ }
1614
+ )
1615
+ ] }) : renderContent() }) }),
1616
+ renderMetadata ? renderMetadata({ entry, metadata }) : showTimestamp && entry.timestamp && /* @__PURE__ */ jsxs("div", { className: "text-[10px] text-[var(--ash-text-faint)] mt-0.5 ml-1 tabular-nums flex items-center gap-1.5", children: [
1617
+ metadata?.model && /* @__PURE__ */ jsx("span", { children: metadata.model }),
1618
+ metadata?.model && /* @__PURE__ */ jsx("span", { className: "opacity-50", children: "\xB7" }),
1619
+ formatTimestamp(entry.timestamp)
1620
+ ] })
1621
+ ] })
1622
+ ]
1623
+ }
1624
+ );
1423
1625
  }
1424
- function ThinkingMessage({ entry, className }) {
1425
- return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1426
- /* @__PURE__ */ jsx("div", { className: "w-7 h-7 rounded-full bg-purple-500/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx(SparklesIcon, { className: "w-4 h-4 text-purple-400" }) }),
1427
- /* @__PURE__ */ jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxs("div", { className: "rounded-2xl p-4 bg-purple-500/10 border border-purple-500/30", children: [
1428
- /* @__PURE__ */ jsx("div", { className: "text-xs text-purple-400 mb-2 font-medium", children: "Thinking" }),
1429
- /* @__PURE__ */ jsx("div", { className: "text-sm text-purple-300 italic opacity-80 whitespace-pre-wrap leading-relaxed", children: entry.content })
1430
- ] }) })
1431
- ] });
1626
+ function ThinkingMessage({ entry, scale = "compact", showAvatar = true, className }) {
1627
+ const s = scaleClasses[scale];
1628
+ return /* @__PURE__ */ jsxs(
1629
+ "div",
1630
+ {
1631
+ "data-message-role": "thinking",
1632
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1633
+ children: [
1634
+ showAvatar && /* @__PURE__ */ 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__ */ jsx(SparklesIcon, { className: "w-3 h-3 text-purple-400" }) }),
1635
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: cn("rounded-lg bg-[var(--ash-thinking-bg)] border border-[var(--ash-thinking-border)]", s.padding), children: [
1636
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] text-[var(--ash-thinking-text)] opacity-80 mb-1 font-medium uppercase tracking-wide", children: "Thinking" }),
1637
+ /* @__PURE__ */ jsx("div", { className: cn(s.text, "text-[var(--ash-thinking-text)] opacity-70 italic whitespace-pre-wrap leading-relaxed"), children: entry.content })
1638
+ ] }) })
1639
+ ]
1640
+ }
1641
+ );
1432
1642
  }
1433
- function ToolCallMessage({ entry, defaultExpanded = false, className }) {
1643
+ function ToolCallMessage({ entry, scale = "compact", showAvatar = true, defaultExpanded = false, className }) {
1434
1644
  if (entry.entryType.type !== "tool_call") return null;
1435
- return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1436
- /* @__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)]" }) }),
1437
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(ToolCallCard, { toolCall: entry.entryType.toolCall, defaultExpanded }) })
1438
- ] });
1645
+ const s = scaleClasses[scale];
1646
+ return /* @__PURE__ */ jsxs(
1647
+ "div",
1648
+ {
1649
+ "data-message-role": "tool",
1650
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1651
+ children: [
1652
+ showAvatar && /* @__PURE__ */ 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__ */ jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
1653
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(ToolCallCard, { toolCall: entry.entryType.toolCall, defaultExpanded }) })
1654
+ ]
1655
+ }
1656
+ );
1439
1657
  }
1440
- function ErrorMessage({ entry, className }) {
1658
+ function ErrorMessage({ entry, scale = "compact", showAvatar = true, className }) {
1441
1659
  if (entry.entryType.type !== "error") return null;
1442
- return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1443
- /* @__PURE__ */ jsx("div", { className: "w-7 h-7 rounded-full bg-red-500/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx(AlertTriangleIcon, { className: "w-4 h-4 text-red-400" }) }),
1444
- /* @__PURE__ */ jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxs("div", { className: "rounded-2xl p-4 bg-red-500/10 border border-red-500/30", children: [
1445
- /* @__PURE__ */ jsx("div", { className: "text-xs text-red-400 mb-2 font-medium", children: "Error" }),
1446
- /* @__PURE__ */ jsx("p", { className: "text-sm text-red-300", children: entry.entryType.message }),
1447
- entry.entryType.code && /* @__PURE__ */ jsxs("p", { className: "text-xs text-red-400/70 mt-2 font-mono", children: [
1448
- "Code: ",
1449
- entry.entryType.code
1450
- ] })
1451
- ] }) })
1452
- ] });
1660
+ const s = scaleClasses[scale];
1661
+ return /* @__PURE__ */ jsxs(
1662
+ "div",
1663
+ {
1664
+ "data-message-role": "error",
1665
+ className: cn("flex ash-animate-fade-in pl-1", s.gap, className),
1666
+ children: [
1667
+ showAvatar && /* @__PURE__ */ 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__ */ jsx(AlertTriangleIcon, { className: "w-3 h-3 text-red-400" }) }),
1668
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: cn("rounded-lg bg-[var(--ash-error-bg)] border border-[var(--ash-error-border)]", s.padding), children: [
1669
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] text-[var(--ash-error-text)] opacity-80 mb-1 font-medium uppercase tracking-wide", children: "Error" }),
1670
+ /* @__PURE__ */ jsx("p", { className: cn(s.text, "text-[var(--ash-error-text)] opacity-90"), children: entry.entryType.message }),
1671
+ entry.entryType.code && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-[var(--ash-error-text)] opacity-60 mt-1 font-mono", children: [
1672
+ "Code: ",
1673
+ entry.entryType.code
1674
+ ] })
1675
+ ] }) })
1676
+ ]
1677
+ }
1678
+ );
1453
1679
  }
1454
- function MessageEntry({ entry, onOptionSelect, defaultExpanded, className }) {
1680
+ function MessageEntry({
1681
+ entry,
1682
+ onOptionSelect,
1683
+ defaultExpanded,
1684
+ richContentRenderers,
1685
+ markdownComponents,
1686
+ userVariant,
1687
+ assistantVariant,
1688
+ scale,
1689
+ showAvatars = true,
1690
+ showTimestamp = true,
1691
+ renderMetadata,
1692
+ metadata,
1693
+ className
1694
+ }) {
1455
1695
  switch (entry.entryType.type) {
1456
1696
  case "user_message":
1457
- return /* @__PURE__ */ jsx(UserMessage, { entry, className });
1697
+ return /* @__PURE__ */ jsx(
1698
+ UserMessage,
1699
+ {
1700
+ entry,
1701
+ variant: userVariant,
1702
+ scale,
1703
+ showAvatar: showAvatars,
1704
+ showTimestamp,
1705
+ renderMetadata,
1706
+ metadata,
1707
+ className
1708
+ }
1709
+ );
1458
1710
  case "assistant_message":
1459
- return /* @__PURE__ */ jsx(AssistantMessage, { entry, onOptionSelect, className });
1711
+ return /* @__PURE__ */ jsx(
1712
+ AssistantMessage,
1713
+ {
1714
+ entry,
1715
+ variant: assistantVariant,
1716
+ scale,
1717
+ showAvatar: showAvatars,
1718
+ showTimestamp,
1719
+ onOptionSelect,
1720
+ richContentRenderers,
1721
+ markdownComponents,
1722
+ renderMetadata,
1723
+ metadata,
1724
+ className
1725
+ }
1726
+ );
1460
1727
  case "thinking":
1461
- return /* @__PURE__ */ jsx(ThinkingMessage, { entry, className });
1728
+ return /* @__PURE__ */ jsx(ThinkingMessage, { entry, scale, showAvatar: showAvatars, className });
1462
1729
  case "tool_call":
1463
- return /* @__PURE__ */ jsx(ToolCallMessage, { entry, defaultExpanded, className });
1730
+ return /* @__PURE__ */ jsx(ToolCallMessage, { entry, scale, showAvatar: showAvatars, defaultExpanded, className });
1464
1731
  case "error":
1465
- return /* @__PURE__ */ jsx(ErrorMessage, { entry, className });
1732
+ return /* @__PURE__ */ jsx(ErrorMessage, { entry, scale, showAvatar: showAvatars, className });
1466
1733
  default:
1467
1734
  return null;
1468
1735
  }
@@ -2081,6 +2348,13 @@ var DEFAULT_DISPLAY_CONFIG = {
2081
2348
  defaultExpanded: false,
2082
2349
  animationDuration: 300
2083
2350
  };
2351
+ var DEFAULT_STYLE_CONFIG = {
2352
+ userVariant: "bubble",
2353
+ assistantVariant: "bubble",
2354
+ scale: "compact",
2355
+ showTimestamp: true,
2356
+ showAvatars: true
2357
+ };
2084
2358
  var DisplayModeContext = createContext(null);
2085
2359
  function DisplayModeProvider({ children, initialConfig }) {
2086
2360
  const [config, setConfigState] = useState({
@@ -2133,6 +2407,15 @@ function MessageList({
2133
2407
  renderWidget,
2134
2408
  onWidgetAction,
2135
2409
  autoScroll = true,
2410
+ richContentRenderers,
2411
+ userVariant,
2412
+ assistantVariant,
2413
+ scale,
2414
+ showAvatars = true,
2415
+ showTimestamp = true,
2416
+ markdownComponents,
2417
+ renderMetadata,
2418
+ metadata,
2136
2419
  className
2137
2420
  }) {
2138
2421
  const contextConfig = useDisplayConfig();
@@ -2167,7 +2450,8 @@ function MessageList({
2167
2450
  }
2168
2451
  return groupEntriesForCompactMode(entries, config);
2169
2452
  }, [entries, config]);
2170
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("flex-1 overflow-y-auto p-4 space-y-4 ash-scrollbar", className), children: [
2453
+ const densityClass = scale ? `ash-density-${scale}` : void 0;
2454
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn("flex-1 overflow-y-auto px-3 py-2 space-y-1 ash-scrollbar", densityClass, className), children: [
2171
2455
  groupedEntries.map((groupedEntry) => {
2172
2456
  if (groupedEntry.type === "single") {
2173
2457
  const entry = groupedEntry.entry;
@@ -2183,12 +2467,29 @@ function MessageList({
2183
2467
  return /* @__PURE__ */ jsx("div", { className: "ash-animate-fade-in", children: widgetContent }, entry.id);
2184
2468
  }
2185
2469
  }
2186
- return /* @__PURE__ */ jsx(MessageEntry, { entry, onOptionSelect, defaultExpanded: config.defaultExpanded }, entry.id);
2470
+ return /* @__PURE__ */ jsx(
2471
+ MessageEntry,
2472
+ {
2473
+ entry,
2474
+ onOptionSelect,
2475
+ defaultExpanded: config.defaultExpanded,
2476
+ richContentRenderers,
2477
+ markdownComponents,
2478
+ userVariant,
2479
+ assistantVariant,
2480
+ scale,
2481
+ showAvatars,
2482
+ showTimestamp,
2483
+ renderMetadata,
2484
+ metadata
2485
+ },
2486
+ entry.id
2487
+ );
2187
2488
  }
2188
2489
  const toolCalls = extractToolCallsFromGroup(groupedEntry.entries);
2189
- return /* @__PURE__ */ jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2190
- /* @__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)]" }) }),
2191
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: config.mode === "accordion" ? /* @__PURE__ */ jsx(
2490
+ return /* @__PURE__ */ jsxs("div", { className: "ash-message-row flex ash-animate-fade-in pl-1", style: { gap: "var(--ash-message-gap, 0.5rem)" }, children: [
2491
+ showAvatars && /* @__PURE__ */ 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__ */ jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
2492
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: config.mode === "accordion" ? /* @__PURE__ */ jsx(
2192
2493
  StepAccordion,
2193
2494
  {
2194
2495
  toolCalls,
@@ -2204,13 +2505,13 @@ function MessageList({
2204
2505
  ) })
2205
2506
  ] }, groupedEntry.id);
2206
2507
  }),
2207
- streamingContent && /* @__PURE__ */ jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2208
- /* @__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)]" }) }),
2209
- /* @__PURE__ */ jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsx("div", { className: "rounded-xl p-3 bg-white/5 text-white/80", children: /* @__PURE__ */ jsx(StreamingText, { content: streamingContent, isStreaming: true }) }) })
2508
+ streamingContent && /* @__PURE__ */ jsxs("div", { className: "ash-message-row flex ash-animate-fade-in pl-1", style: { gap: "var(--ash-message-gap, 0.5rem)" }, children: [
2509
+ showAvatars && /* @__PURE__ */ 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__ */ jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
2510
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ 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__ */ jsx(StreamingText, { content: streamingContent, isStreaming: true }) }) })
2210
2511
  ] }),
2211
- loading && !streamingContent && /* @__PURE__ */ jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2212
- /* @__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)]" }) }),
2213
- /* @__PURE__ */ jsx("div", { className: "rounded-xl p-3 bg-white/5", children: /* @__PURE__ */ jsx(LoadingIndicator, { variant: "dots" }) })
2512
+ loading && !streamingContent && /* @__PURE__ */ jsxs("div", { className: "ash-message-row flex ash-animate-fade-in pl-1", style: { gap: "var(--ash-message-gap, 0.5rem)" }, children: [
2513
+ showAvatars && /* @__PURE__ */ 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__ */ jsx(BotIcon, { className: "w-3 h-3 text-[var(--ash-accent)]" }) }),
2514
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg px-3 py-1.5 bg-white/[0.03]", children: /* @__PURE__ */ jsx(LoadingIndicator, { variant: "dots" }) })
2214
2515
  ] }),
2215
2516
  /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
2216
2517
  ] });
@@ -3027,6 +3328,21 @@ var typography = {
3027
3328
  relaxed: "1.75"
3028
3329
  }
3029
3330
  };
3331
+ var cssVars = {
3332
+ fontSize: {
3333
+ base: "--ash-font-size-base",
3334
+ sm: "--ash-font-size-sm",
3335
+ xs: "--ash-font-size-xs",
3336
+ code: "--ash-font-size-code"
3337
+ },
3338
+ accent: "--ash-accent",
3339
+ accentForeground: "--ash-accent-foreground",
3340
+ surfaceDark: "--ash-surface-dark",
3341
+ surfaceDarker: "--ash-surface-darker",
3342
+ surfaceCard: "--ash-surface-card",
3343
+ surfaceElevated: "--ash-surface-elevated",
3344
+ surfaceBorder: "--ash-surface-border"
3345
+ };
3030
3346
  var keyframes = {
3031
3347
  slideUp: {
3032
3348
  from: { opacity: "0", transform: "translateY(20px)" },
@@ -3145,6 +3461,10 @@ function tokensToCssVariables(prefix = "ash") {
3145
3461
  if (key !== "DEFAULT") vars[`--${prefix}-border-${key}`] = value;
3146
3462
  else vars[`--${prefix}-border`] = value;
3147
3463
  });
3464
+ vars[`--${prefix}-font-size-base`] = "14px";
3465
+ vars[`--${prefix}-font-size-sm`] = "12px";
3466
+ vars[`--${prefix}-font-size-xs`] = "11px";
3467
+ vars[`--${prefix}-font-size-code`] = "13px";
3148
3468
  return vars;
3149
3469
  }
3150
3470
  var inlineStyles = {
@@ -3400,19 +3720,95 @@ function useFileUpload({
3400
3720
  openFilePicker
3401
3721
  };
3402
3722
  }
3723
+
3724
+ // src/hooks/middleware.ts
3725
+ async function applyRequestMiddleware(middlewares, request) {
3726
+ let current = { ...request };
3727
+ for (const mw of middlewares) {
3728
+ if (mw.onRequest) {
3729
+ try {
3730
+ const result = await mw.onRequest(current);
3731
+ if (result?.error) {
3732
+ return { request: current, error: result.error };
3733
+ }
3734
+ if (result) {
3735
+ current = {
3736
+ ...current,
3737
+ prompt: result.prompt ?? current.prompt,
3738
+ sessionContext: result.sessionContext ?? current.sessionContext,
3739
+ metadata: {
3740
+ ...current.metadata,
3741
+ ...result.metadata
3742
+ }
3743
+ };
3744
+ }
3745
+ } catch (err) {
3746
+ const errorMessage = err instanceof Error ? err.message : "Middleware error";
3747
+ return { request: current, error: errorMessage };
3748
+ }
3749
+ }
3750
+ }
3751
+ return { request: current };
3752
+ }
3753
+ async function applyEventMiddleware(middlewares, event) {
3754
+ let current = event;
3755
+ for (const mw of middlewares) {
3756
+ if (!current) break;
3757
+ if (mw.onEvent) {
3758
+ try {
3759
+ current = await mw.onEvent(current);
3760
+ } catch (err) {
3761
+ console.error("[middleware] onEvent error:", err);
3762
+ }
3763
+ }
3764
+ }
3765
+ return current;
3766
+ }
3767
+ async function callMiddlewareComplete(middlewares, sessionId) {
3768
+ for (const mw of middlewares) {
3769
+ if (mw.onComplete) {
3770
+ try {
3771
+ await mw.onComplete(sessionId);
3772
+ } catch (err) {
3773
+ console.error("[middleware] onComplete error:", err);
3774
+ }
3775
+ }
3776
+ }
3777
+ }
3778
+ async function callMiddlewareError(middlewares, error) {
3779
+ for (const mw of middlewares) {
3780
+ if (mw.onError) {
3781
+ try {
3782
+ await mw.onError(error);
3783
+ } catch (err) {
3784
+ console.error("[middleware] onError error:", err);
3785
+ }
3786
+ }
3787
+ }
3788
+ }
3789
+
3790
+ // src/hooks/useAgentChat.ts
3403
3791
  function useAgentChat(options) {
3404
3792
  const {
3405
3793
  createStream,
3794
+ subscribeToSession,
3406
3795
  initialSessionId,
3407
3796
  initialEntries = [],
3408
3797
  onSessionStart,
3409
3798
  onSessionEnd,
3410
3799
  onError,
3411
- onSandboxLog
3800
+ onSandboxLog,
3801
+ onReconnect,
3802
+ maxReconnectAttempts = 3,
3803
+ reconnectBaseDelay = 1e3,
3804
+ onBeforeSend,
3805
+ onEvent,
3806
+ middleware
3412
3807
  } = options;
3413
3808
  const [historyEntries, setHistoryEntries] = useState(initialEntries);
3414
3809
  const [streamingEntries, setStreamingEntries] = useState([]);
3415
3810
  const [isStreaming, setIsStreaming] = useState(false);
3811
+ const [isReconnecting, setIsReconnecting] = useState(false);
3416
3812
  const [error, setError] = useState(null);
3417
3813
  const [sessionId, setSessionId] = useState(initialSessionId || null);
3418
3814
  const abortControllerRef = useRef(null);
@@ -3420,6 +3816,16 @@ function useAgentChat(options) {
3420
3816
  const currentTextIdRef = useRef(null);
3421
3817
  const pendingToolCallsRef = useRef(/* @__PURE__ */ new Map());
3422
3818
  const hadToolCallSinceTextRef = useRef(false);
3819
+ const reconnectAttemptsRef = useRef(0);
3820
+ const eventCountRef = useRef(0);
3821
+ const sessionIdRef = useRef(sessionId);
3822
+ const streamingEntriesRef = useRef([]);
3823
+ useEffect(() => {
3824
+ sessionIdRef.current = sessionId;
3825
+ }, [sessionId]);
3826
+ useEffect(() => {
3827
+ streamingEntriesRef.current = streamingEntries;
3828
+ }, [streamingEntries]);
3423
3829
  const entries = [...historyEntries, ...streamingEntries];
3424
3830
  const emitStreamingEntries = useCallback((newEntries) => {
3425
3831
  setStreamingEntries([...newEntries]);
@@ -3534,7 +3940,7 @@ function useAgentChat(options) {
3534
3940
  setError(event.error);
3535
3941
  onError?.(event.error);
3536
3942
  newEntries.push({
3537
- id: `error-${Date.now()}`,
3943
+ id: `error-${Date.now()}-${Math.random().toString(36).slice(2)}`,
3538
3944
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3539
3945
  entryType: { type: "error", message: event.error, code: event.code },
3540
3946
  content: event.error
@@ -3553,12 +3959,112 @@ function useAgentChat(options) {
3553
3959
  }
3554
3960
  return newEntries;
3555
3961
  }, [sessionId, onSessionStart, onSessionEnd, onError, onSandboxLog, createTextEntry, resetStreamingState]);
3962
+ const attemptReconnect = useCallback(async (targetSessionId, currentEntries) => {
3963
+ if (!subscribeToSession) {
3964
+ return false;
3965
+ }
3966
+ const attempt = reconnectAttemptsRef.current + 1;
3967
+ if (attempt > maxReconnectAttempts) {
3968
+ console.warn(`[useAgentChat] Max reconnection attempts (${maxReconnectAttempts}) exceeded`);
3969
+ return false;
3970
+ }
3971
+ reconnectAttemptsRef.current = attempt;
3972
+ setIsReconnecting(true);
3973
+ onReconnect?.(attempt, targetSessionId);
3974
+ const delay = reconnectBaseDelay * Math.pow(2, attempt - 1);
3975
+ console.log(`[useAgentChat] Reconnection attempt ${attempt}/${maxReconnectAttempts} in ${delay}ms`);
3976
+ await new Promise((resolve) => setTimeout(resolve, delay));
3977
+ const controller = new AbortController();
3978
+ abortControllerRef.current = controller;
3979
+ try {
3980
+ const stream = subscribeToSession(targetSessionId, controller.signal);
3981
+ let localStreamingEntries = [...currentEntries];
3982
+ for await (const event of stream) {
3983
+ if (controller.signal.aborted) break;
3984
+ eventCountRef.current++;
3985
+ localStreamingEntries = processEvent(event, localStreamingEntries);
3986
+ emitStreamingEntries(localStreamingEntries);
3987
+ if (event.type === "complete" || event.type === "session_end" || event.type === "error") {
3988
+ reconnectAttemptsRef.current = 0;
3989
+ setIsReconnecting(false);
3990
+ if (event.type === "complete" && localStreamingEntries.length > 0) {
3991
+ setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3992
+ setStreamingEntries([]);
3993
+ }
3994
+ return true;
3995
+ }
3996
+ }
3997
+ reconnectAttemptsRef.current = 0;
3998
+ setIsReconnecting(false);
3999
+ if (localStreamingEntries.length > 0) {
4000
+ setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
4001
+ setStreamingEntries([]);
4002
+ }
4003
+ return true;
4004
+ } catch (err) {
4005
+ const isAbort = err.name === "AbortError" || err.message?.includes("aborted") || err.message?.includes("BodyStreamBuffer");
4006
+ if (isAbort && !controller.signal.aborted) {
4007
+ console.log(`[useAgentChat] Reconnection stream interrupted, will retry...`);
4008
+ setIsReconnecting(false);
4009
+ return attemptReconnect(targetSessionId, streamingEntriesRef.current);
4010
+ }
4011
+ setIsReconnecting(false);
4012
+ if (!controller.signal.aborted) {
4013
+ const errorMessage = err instanceof Error ? err.message : "Reconnection failed";
4014
+ console.error(`[useAgentChat] Reconnection failed:`, errorMessage);
4015
+ }
4016
+ return false;
4017
+ }
4018
+ }, [subscribeToSession, maxReconnectAttempts, reconnectBaseDelay, onReconnect, processEvent, emitStreamingEntries]);
3556
4019
  const send = useCallback(async (prompt) => {
3557
4020
  if (isStreaming) return;
4021
+ let finalPrompt = prompt;
4022
+ let additionalMetadata = {};
4023
+ let additionalContext;
4024
+ if (onBeforeSend) {
4025
+ try {
4026
+ const result = await onBeforeSend({
4027
+ prompt,
4028
+ sessionId,
4029
+ entries: [...historyEntries, ...streamingEntries]
4030
+ });
4031
+ if (result?.cancel) {
4032
+ return;
4033
+ }
4034
+ if (result?.prompt !== void 0) finalPrompt = result.prompt;
4035
+ if (result?.metadata) additionalMetadata = result.metadata;
4036
+ if (result?.sessionContext) additionalContext = result.sessionContext;
4037
+ } catch (err) {
4038
+ const errorMessage = err instanceof Error ? err.message : "Send cancelled";
4039
+ setError(errorMessage);
4040
+ onError?.(errorMessage);
4041
+ return;
4042
+ }
4043
+ }
4044
+ if (middleware?.length) {
4045
+ const middlewareRequest = {
4046
+ prompt: finalPrompt,
4047
+ sessionId,
4048
+ metadata: additionalMetadata,
4049
+ sessionContext: additionalContext
4050
+ };
4051
+ const { request, error: middlewareError } = await applyRequestMiddleware(middleware, middlewareRequest);
4052
+ if (middlewareError) {
4053
+ setError(middlewareError);
4054
+ onError?.(middlewareError);
4055
+ await callMiddlewareError(middleware, middlewareError);
4056
+ return;
4057
+ }
4058
+ finalPrompt = request.prompt;
4059
+ additionalMetadata = request.metadata || {};
4060
+ additionalContext = request.sessionContext;
4061
+ }
3558
4062
  setIsStreaming(true);
3559
4063
  setError(null);
4064
+ reconnectAttemptsRef.current = 0;
4065
+ eventCountRef.current = 0;
3560
4066
  const userEntry = {
3561
- id: `user-${Date.now()}`,
4067
+ id: `user-${Date.now()}-${Math.random().toString(36).slice(2)}`,
3562
4068
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3563
4069
  entryType: { type: "user_message" },
3564
4070
  content: prompt
@@ -3568,39 +4074,91 @@ function useAgentChat(options) {
3568
4074
  const controller = new AbortController();
3569
4075
  abortControllerRef.current = controller;
3570
4076
  let localStreamingEntries = [];
4077
+ let completedSessionId = null;
3571
4078
  try {
3572
- const stream = createStream(prompt, sessionId || void 0, controller.signal);
4079
+ const stream = createStream(finalPrompt, sessionId || void 0, {
4080
+ signal: controller.signal,
4081
+ metadata: Object.keys(additionalMetadata).length > 0 ? additionalMetadata : void 0,
4082
+ sessionContext: additionalContext
4083
+ });
3573
4084
  for await (const event of stream) {
3574
4085
  if (controller.signal.aborted) break;
3575
- localStreamingEntries = processEvent(event, localStreamingEntries);
4086
+ eventCountRef.current++;
4087
+ if (event.type === "session_start" && event.sessionId) {
4088
+ completedSessionId = event.sessionId;
4089
+ }
4090
+ if (onEvent) {
4091
+ try {
4092
+ await onEvent(event);
4093
+ } catch (err) {
4094
+ console.error("[useAgentChat] onEvent error:", err);
4095
+ }
4096
+ }
4097
+ let processedEvent = event;
4098
+ if (middleware?.length) {
4099
+ processedEvent = await applyEventMiddleware(middleware, event);
4100
+ }
4101
+ if (!processedEvent) continue;
4102
+ localStreamingEntries = processEvent(processedEvent, localStreamingEntries);
3576
4103
  emitStreamingEntries(localStreamingEntries);
3577
4104
  }
3578
4105
  if (localStreamingEntries.length > 0) {
3579
4106
  setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3580
4107
  setStreamingEntries([]);
3581
4108
  }
4109
+ reconnectAttemptsRef.current = 0;
4110
+ if (middleware?.length && (completedSessionId || sessionIdRef.current)) {
4111
+ await callMiddlewareComplete(middleware, completedSessionId || sessionIdRef.current || "");
4112
+ }
3582
4113
  } catch (err) {
3583
- if (err.name !== "AbortError") {
4114
+ const isAbort = err.name === "AbortError" || err.message?.includes("aborted") || err.message?.includes("BodyStreamBuffer");
4115
+ if (isAbort && !controller.signal.aborted) {
4116
+ const currentSessionId = sessionIdRef.current;
4117
+ const hadEvents = eventCountRef.current > 0;
4118
+ console.log(`[useAgentChat] Stream interrupted. sessionId=${currentSessionId}, events=${eventCountRef.current}`);
4119
+ if (currentSessionId && hadEvents && subscribeToSession) {
4120
+ console.log(`[useAgentChat] Attempting auto-reconnection...`);
4121
+ const reconnected = await attemptReconnect(currentSessionId, streamingEntriesRef.current);
4122
+ if (reconnected) {
4123
+ return;
4124
+ }
4125
+ const errorMsg = "Connection lost. Please try again.";
4126
+ setError(errorMsg);
4127
+ onError?.(errorMsg);
4128
+ if (middleware?.length) {
4129
+ await callMiddlewareError(middleware, errorMsg);
4130
+ }
4131
+ }
4132
+ } else if (!isAbort) {
3584
4133
  const errorMessage = err instanceof Error ? err.message : "Unknown error";
3585
4134
  setError(errorMessage);
3586
4135
  onError?.(errorMessage);
4136
+ if (middleware?.length) {
4137
+ await callMiddlewareError(middleware, errorMessage);
4138
+ }
3587
4139
  }
3588
4140
  } finally {
3589
4141
  setIsStreaming(false);
4142
+ setIsReconnecting(false);
3590
4143
  abortControllerRef.current = null;
3591
4144
  resetStreamingState();
3592
4145
  }
3593
- }, [isStreaming, sessionId, createStream, processEvent, emitStreamingEntries, resetStreamingState, onError]);
4146
+ }, [isStreaming, sessionId, historyEntries, streamingEntries, createStream, subscribeToSession, processEvent, emitStreamingEntries, resetStreamingState, onError, attemptReconnect, onBeforeSend, onEvent, middleware]);
3594
4147
  const stop = useCallback(() => {
4148
+ reconnectAttemptsRef.current = maxReconnectAttempts + 1;
4149
+ setIsReconnecting(false);
3595
4150
  if (abortControllerRef.current) {
3596
4151
  abortControllerRef.current.abort();
3597
4152
  }
3598
- }, []);
4153
+ }, [maxReconnectAttempts]);
3599
4154
  const clear = useCallback(() => {
3600
4155
  setHistoryEntries([]);
3601
4156
  resetStreamingState();
3602
4157
  setSessionId(initialSessionId || null);
3603
4158
  setError(null);
4159
+ setIsReconnecting(false);
4160
+ reconnectAttemptsRef.current = 0;
4161
+ eventCountRef.current = 0;
3604
4162
  }, [initialSessionId, resetStreamingState]);
3605
4163
  const setEntries = useCallback((newEntries) => {
3606
4164
  resetStreamingState();
@@ -3616,6 +4174,7 @@ function useAgentChat(options) {
3616
4174
  return {
3617
4175
  entries,
3618
4176
  isStreaming,
4177
+ isReconnecting,
3619
4178
  error,
3620
4179
  sessionId,
3621
4180
  send,
@@ -3697,6 +4256,6 @@ function useLongTextConversion({
3697
4256
  };
3698
4257
  }
3699
4258
 
3700
- export { ActionIcon, AgentToolCard, AlertCircleIcon, AlertTriangleIcon, AssistantMessage, BotIcon, BrainIcon, BugIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CircleIcon, ClipboardListIcon, ClockIcon, CodeBlock, CodeIcon, CompactToolRow, CompactToolStatusLine, CopyIcon, DEFAULT_DISPLAY_CONFIG, DisplayModeProvider, DisplayModeToggle, EditIcon, EnvVarsPanel, ErrorIcon, ErrorMessage, FileBadge, FileIcon, FilePlusIcon, FolderSearchIcon, GlobeIcon, InfoIcon, JsonDisplay, LazyMarkdown, 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, formatElapsedTime, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, inlineStyles, isAgentToolAction, 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 };
4259
+ export { ActionIcon, AgentToolCard, AlertCircleIcon, AlertTriangleIcon, ArrowUpIcon, AssistantMessage, BotIcon, BrainIcon, BugIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CircleIcon, ClipboardListIcon, ClockIcon, CodeBlock, CodeIcon, CompactToolRow, CompactToolStatusLine, CopyIcon, DEFAULT_DISPLAY_CONFIG, DEFAULT_STYLE_CONFIG, DisplayModeProvider, DisplayModeToggle, EditIcon, EnvVarsPanel, ErrorIcon, ErrorMessage, FileBadge, FileIcon, FilePlusIcon, FolderSearchIcon, GlobeIcon, HomeIcon, InfoIcon, JsonDisplay, LazyMarkdown, ListChecksIcon, LoaderIcon, LoadingIndicator, LogsPanel, MessageEntry, MessageList, MessageSquareIcon, MicrophoneIcon, MoonIcon, OptionCards, PaperclipIcon, PlugIcon, RichContentRenderer, SearchIcon, SendIcon, SparklesIcon, SpinnerIcon, StatusIndicator, StepAccordion, StopCircleIcon, StreamingText, SunIcon, TerminalIcon, ThemeProvider, ThinkingMessage, TodoPanel, ToolCallCard, ToolCallMessage, ToolExecutionGroup, ToolIcon, TypewriterText, UserIcon, UserMessage, XCircleIcon, XIcon, allKeyframesCss, applyEventMiddleware, applyRequestMiddleware, borderRadius, callMiddlewareComplete, callMiddlewareError, cn, colors, createToolCall, cssVars, extractTextContent, extractToolCallsFromGroup, formatElapsedTime, formatFileSize, formatTimestamp, formatToolName, generateToolSummary, getActionIcon, getActionLabel, groupEntriesForCompactMode, inlineStyles, isAgentToolAction, 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 };
3701
4260
  //# sourceMappingURL=index.js.map
3702
4261
  //# sourceMappingURL=index.js.map