@tangle-network/sandbox-ui 0.10.0 → 0.10.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.
@@ -7,6 +7,9 @@ import {
7
7
  getToolDisplayMetadata,
8
8
  getToolErrorText
9
9
  } from "./chunk-BX6AQMUS.js";
10
+ import {
11
+ OpenUIArtifactRenderer
12
+ } from "./chunk-ZNCEM5CD.js";
10
13
  import {
11
14
  CodeBlock,
12
15
  CopyButton,
@@ -979,6 +982,26 @@ var DEFAULT_BRANDING = {
979
982
  iconClass: "",
980
983
  textClass: "text-primary"
981
984
  };
985
+ var ASSISTANT_SHELL = "min-w-0 flex-1 space-y-3 rounded-[26px] border border-[var(--border-subtle)] bg-[color:color-mix(in_srgb,var(--bg-card)_94%,transparent)] px-5 py-4 shadow-[0_1px_2px_rgba(15,23,42,0.04)]";
986
+ function AssistantShell({
987
+ branding,
988
+ isStreaming,
989
+ children
990
+ }) {
991
+ return /* @__PURE__ */ jsxs12("div", { className: "flex gap-3", children: [
992
+ /* @__PURE__ */ jsx13("div", { className: "mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--brand-primary)] text-white", children: /* @__PURE__ */ jsx13(Bot2, { className: "h-4 w-4" }) }),
993
+ /* @__PURE__ */ jsxs12("div", { className: ASSISTANT_SHELL, children: [
994
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.14em] text-[var(--text-muted)]", children: [
995
+ /* @__PURE__ */ jsx13("span", { children: branding.label }),
996
+ isStreaming ? /* @__PURE__ */ jsxs12("span", { className: "inline-flex items-center gap-1.5 text-[var(--text-muted)]", children: [
997
+ /* @__PURE__ */ jsx13(Loader25, { className: "h-3 w-3 animate-spin" }),
998
+ "Thinking"
999
+ ] }) : null
1000
+ ] }),
1001
+ children
1002
+ ] })
1003
+ ] });
1004
+ }
982
1005
  var CATEGORY_ICON_MAP = {
983
1006
  command: Terminal3,
984
1007
  write: FileEdit3,
@@ -1001,6 +1024,67 @@ var CATEGORY_ORDER = [
1001
1024
  "todo",
1002
1025
  "other"
1003
1026
  ];
1027
+ var OPENUI_NODE_TYPES = /* @__PURE__ */ new Set([
1028
+ "heading",
1029
+ "text",
1030
+ "badge",
1031
+ "stat",
1032
+ "key_value",
1033
+ "code",
1034
+ "markdown",
1035
+ "table",
1036
+ "actions",
1037
+ "separator",
1038
+ "stack",
1039
+ "grid",
1040
+ "card"
1041
+ ]);
1042
+ function isOpenUINode(value) {
1043
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" && OPENUI_NODE_TYPES.has(value.type);
1044
+ }
1045
+ function extractOpenUISchema(output) {
1046
+ if (output == null) return null;
1047
+ if (isOpenUINode(output)) return [output];
1048
+ if (Array.isArray(output) && output.length > 0 && output.every(isOpenUINode)) {
1049
+ return output;
1050
+ }
1051
+ if (typeof output === "object" && !Array.isArray(output)) {
1052
+ const obj = output;
1053
+ for (const key of ["openui", "schema", "ui"]) {
1054
+ if (obj[key] == null) continue;
1055
+ const inner = obj[key];
1056
+ if (typeof inner === "string") {
1057
+ try {
1058
+ const parsed = JSON.parse(inner);
1059
+ return extractOpenUISchema(parsed);
1060
+ } catch {
1061
+ continue;
1062
+ }
1063
+ }
1064
+ const nested = extractOpenUISchema(inner);
1065
+ if (nested) return nested;
1066
+ }
1067
+ }
1068
+ if (typeof output === "string") {
1069
+ try {
1070
+ return extractOpenUISchema(JSON.parse(output));
1071
+ } catch {
1072
+ return null;
1073
+ }
1074
+ }
1075
+ return null;
1076
+ }
1077
+ function getOpenUISummary(output) {
1078
+ if (!output || typeof output !== "object" || Array.isArray(output)) {
1079
+ return null;
1080
+ }
1081
+ const summary = output.summary;
1082
+ return typeof summary === "string" && summary.trim() ? summary.trim() : null;
1083
+ }
1084
+ function isOpenUITool(part) {
1085
+ const normalized = part.tool.toLowerCase().replace(/^tool:/, "");
1086
+ return normalized.includes("openui");
1087
+ }
1004
1088
  function CategoryBadges({ categories }) {
1005
1089
  const sorted = useMemo5(
1006
1090
  () => CATEGORY_ORDER.filter((category) => categories.has(category)),
@@ -1072,10 +1156,66 @@ var RunGroup = memo11(
1072
1156
  return part.type === "text" && !part.synthetic && part.text.trim().length > 0;
1073
1157
  });
1074
1158
  if (!hasRenderableParts) {
1075
- return null;
1159
+ if (!isStreaming) {
1160
+ return null;
1161
+ }
1162
+ return /* @__PURE__ */ jsx13(AssistantShell, { branding, isStreaming: true, children: /* @__PURE__ */ jsx13("div", { className: "flex items-center gap-2 px-0.5 py-0.5 text-sm text-[var(--text-muted)]", children: /* @__PURE__ */ jsxs12("span", { className: "flex gap-[5px]", children: [
1163
+ /* @__PURE__ */ jsx13("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "0ms" } }),
1164
+ /* @__PURE__ */ jsx13("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "150ms" } }),
1165
+ /* @__PURE__ */ jsx13("span", { className: "h-2 w-2 animate-bounce rounded-full bg-[var(--brand-glow)]", style: { animationDelay: "300ms" } })
1166
+ ] }) }) });
1167
+ }
1168
+ const showTraceChrome = allParts.some(({ part }) => {
1169
+ if (part.type === "reasoning") {
1170
+ return true;
1171
+ }
1172
+ if (part.type === "tool") {
1173
+ return !isOpenUITool(part);
1174
+ }
1175
+ return false;
1176
+ });
1177
+ if (!showTraceChrome) {
1178
+ return /* @__PURE__ */ jsx13(AssistantShell, { branding, isStreaming, children: allParts.map(({ part, msgId, index }) => {
1179
+ const key = `${msgId}-${index}`;
1180
+ if (part.type === "tool" && isOpenUITool(part)) {
1181
+ const toolPart = part;
1182
+ const schema = extractOpenUISchema(toolPart.state.output);
1183
+ const summary = getOpenUISummary(toolPart.state.output);
1184
+ if (toolPart.state.status === "completed" && schema) {
1185
+ return /* @__PURE__ */ jsxs12(
1186
+ "div",
1187
+ {
1188
+ className: "overflow-hidden rounded-[22px] border border-[var(--border-subtle)] bg-[var(--bg-root)]",
1189
+ children: [
1190
+ summary ? /* @__PURE__ */ jsx13("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3 text-sm leading-6 text-foreground", children: summary }) : null,
1191
+ /* @__PURE__ */ jsx13("div", { className: "p-4", children: /* @__PURE__ */ jsx13(OpenUIArtifactRenderer, { schema }) })
1192
+ ]
1193
+ },
1194
+ key
1195
+ );
1196
+ }
1197
+ if (toolPart.state.status === "running") {
1198
+ return /* @__PURE__ */ jsxs12(
1199
+ "div",
1200
+ {
1201
+ className: "flex items-center gap-2 rounded-[18px] border border-[var(--border-subtle)] bg-[var(--bg-root)] px-4 py-3 text-sm text-muted-foreground",
1202
+ children: [
1203
+ /* @__PURE__ */ jsx13(Loader25, { className: "h-4 w-4 animate-spin text-primary" }),
1204
+ "Building view\u2026"
1205
+ ]
1206
+ },
1207
+ key
1208
+ );
1209
+ }
1210
+ }
1211
+ if (part.type === "text" && !part.synthetic && part.text.trim()) {
1212
+ return /* @__PURE__ */ jsx13("div", { className: "px-0.5", children: /* @__PURE__ */ jsx13(Markdown, { className: "tangle-prose text-[15px] leading-7 text-[var(--text-primary)]", children: part.text }) }, key);
1213
+ }
1214
+ return null;
1215
+ }) });
1076
1216
  }
1077
- return /* @__PURE__ */ jsx13(Collapsible3.Root, { open: !collapsed, onOpenChange: () => onToggle(), children: /* @__PURE__ */ jsxs12("div", { className: "rounded-[var(--radius-xl)] border border-border bg-card shadow-[var(--shadow-card)]", children: [
1078
- /* @__PURE__ */ jsxs12("div", { className: "flex items-start gap-3 px-4 py-4", children: [
1217
+ return /* @__PURE__ */ jsx13(Collapsible3.Root, { open: !collapsed, onOpenChange: () => onToggle(), children: /* @__PURE__ */ jsxs12("div", { className: "rounded-[28px] border border-[var(--border-subtle)] bg-[var(--bg-card)] shadow-none", children: [
1218
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-start gap-3 px-4 py-3.5", children: [
1079
1219
  /* @__PURE__ */ jsx13(Collapsible3.Trigger, { asChild: true, children: /* @__PURE__ */ jsx13(
1080
1220
  "button",
1081
1221
  {
@@ -1113,54 +1253,80 @@ var RunGroup = memo11(
1113
1253
  headerActions ? /* @__PURE__ */ jsx13("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-1.5 pt-1", children: headerActions }) : null
1114
1254
  ] }),
1115
1255
  collapsed && run.summaryText && /* @__PURE__ */ jsx13("div", { className: "px-4 pb-4 text-sm leading-6 text-muted-foreground line-clamp-2", children: run.summaryText }),
1116
- /* @__PURE__ */ jsx13(Collapsible3.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx13(
1117
- "div",
1118
- {
1119
- className: cn(
1120
- "space-y-3 border-t border-border px-4 pb-4 pt-3"
1121
- ),
1122
- children: allParts.map(({ part, msgId, index }, partIndex) => {
1123
- const key = `${msgId}-${index}`;
1124
- if (part.type === "tool") {
1125
- return /* @__PURE__ */ jsx13(
1126
- InlineToolItem,
1127
- {
1128
- part,
1129
- renderToolDetail,
1130
- groupPosition: getToolGroupPosition(partIndex, allParts),
1131
- actions: renderToolActions?.(part, {
1132
- run,
1133
- messageId: msgId,
1134
- partIndex: index
1135
- })
1136
- },
1137
- key
1138
- );
1139
- }
1140
- if (part.type === "reasoning") {
1141
- return /* @__PURE__ */ jsx13(
1142
- InlineThinkingItem,
1256
+ /* @__PURE__ */ jsx13(Collapsible3.Content, { className: "overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp", children: /* @__PURE__ */ jsx13("div", { className: cn("space-y-3 border-t border-[var(--border-subtle)] px-4 pb-4 pt-3"), children: allParts.map(({ part, msgId, index }, partIndex) => {
1257
+ const key = `${msgId}-${index}`;
1258
+ if (part.type === "tool") {
1259
+ if (isOpenUITool(part)) {
1260
+ const toolPart = part;
1261
+ const schema = extractOpenUISchema(toolPart.state.output);
1262
+ const summary = getOpenUISummary(toolPart.state.output);
1263
+ if (toolPart.state.status === "completed" && schema) {
1264
+ return /* @__PURE__ */ jsxs12(
1265
+ "div",
1143
1266
  {
1144
- part,
1145
- defaultOpen: isStreaming
1267
+ className: "overflow-hidden rounded-[24px] border border-[var(--border-subtle)] bg-[var(--bg-card)]",
1268
+ children: [
1269
+ summary ? /* @__PURE__ */ jsxs12("div", { className: "border-b border-[var(--border-subtle)] px-4 py-3", children: [
1270
+ /* @__PURE__ */ jsx13("div", { className: "text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground", children: "View" }),
1271
+ /* @__PURE__ */ jsx13("div", { className: "mt-1 text-sm leading-6 text-foreground", children: summary })
1272
+ ] }) : null,
1273
+ /* @__PURE__ */ jsx13("div", { className: "p-4", children: /* @__PURE__ */ jsx13(OpenUIArtifactRenderer, { schema }) })
1274
+ ]
1146
1275
  },
1147
1276
  key
1148
1277
  );
1149
1278
  }
1150
- if (part.type === "text" && !part.synthetic && part.text.trim()) {
1151
- return /* @__PURE__ */ jsx13(
1279
+ if (toolPart.state.status === "running") {
1280
+ return /* @__PURE__ */ jsxs12(
1152
1281
  "div",
1153
1282
  {
1154
- className: "px-1 py-1",
1155
- children: /* @__PURE__ */ jsx13(Markdown, { className: "tangle-prose text-[15px] leading-7", children: part.text })
1283
+ className: "flex items-center gap-3 rounded-[20px] border border-[var(--border-subtle)] bg-[var(--bg-card)] px-4 py-3 text-sm text-muted-foreground",
1284
+ children: [
1285
+ /* @__PURE__ */ jsx13(Loader25, { className: "h-4 w-4 animate-spin text-primary" }),
1286
+ "Building view\u2026"
1287
+ ]
1156
1288
  },
1157
1289
  key
1158
1290
  );
1159
1291
  }
1160
- return null;
1161
- })
1292
+ }
1293
+ return /* @__PURE__ */ jsx13(
1294
+ InlineToolItem,
1295
+ {
1296
+ part,
1297
+ renderToolDetail,
1298
+ groupPosition: getToolGroupPosition(partIndex, allParts),
1299
+ actions: renderToolActions?.(part, {
1300
+ run,
1301
+ messageId: msgId,
1302
+ partIndex: index
1303
+ })
1304
+ },
1305
+ key
1306
+ );
1307
+ }
1308
+ if (part.type === "reasoning") {
1309
+ return /* @__PURE__ */ jsx13(
1310
+ InlineThinkingItem,
1311
+ {
1312
+ part,
1313
+ defaultOpen: isStreaming
1314
+ },
1315
+ key
1316
+ );
1317
+ }
1318
+ if (part.type === "text" && !part.synthetic && part.text.trim()) {
1319
+ return /* @__PURE__ */ jsx13(
1320
+ "div",
1321
+ {
1322
+ className: "px-1 py-1",
1323
+ children: /* @__PURE__ */ jsx13(Markdown, { className: "tangle-prose text-[15px] leading-7", children: part.text })
1324
+ },
1325
+ key
1326
+ );
1162
1327
  }
1163
- ) })
1328
+ return null;
1329
+ }) }) })
1164
1330
  ] }) });
1165
1331
  }
1166
1332
  );
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  InlineThinkingItem,
8
8
  RunGroup
9
- } from "./chunk-EXSOPXIY.js";
9
+ } from "./chunk-QOL4ZB24.js";
10
10
  import {
11
11
  ToolCallGroup,
12
12
  ToolCallStep
@@ -16,7 +16,7 @@ import {
16
16
  } from "./chunk-BX6AQMUS.js";
17
17
  import {
18
18
  OpenUIArtifactRenderer
19
- } from "./chunk-PLTZB5BC.js";
19
+ } from "./chunk-ZNCEM5CD.js";
20
20
  import {
21
21
  Markdown
22
22
  } from "./chunk-T7HMZEVO.js";
@@ -30,10 +30,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
30
30
  var UserMessage = memo(({ message, parts, actions }) => {
31
31
  const textContent = parts.filter((p) => p.type === "text").map((p) => p.text).join("\n");
32
32
  if (!textContent.trim()) return null;
33
- return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs("div", { className: "flex max-w-[82%] flex-col items-end gap-2", children: [
34
- /* @__PURE__ */ jsxs("div", { className: "w-full rounded-[var(--radius-xl)] rounded-br-[var(--radius-sm)] border border-border bg-muted/50 px-4 py-3.5", children: [
35
- /* @__PURE__ */ jsx("div", { className: "mb-1.5 text-[11px] font-semibold uppercase tracking-[0.14em] text-primary", children: "You" }),
36
- /* @__PURE__ */ jsx("div", { className: "text-[15px] leading-7 text-foreground", children: /* @__PURE__ */ jsx(Markdown, { className: "tangle-prose", children: textContent }) })
33
+ return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs("div", { className: "flex max-w-[78%] flex-col items-end gap-2", children: [
34
+ /* @__PURE__ */ jsxs("div", { className: "w-full rounded-[26px] rounded-br-[12px] bg-[var(--brand-primary)] px-4 py-3 text-white shadow-[0_8px_20px_rgba(15,23,42,0.12)]", children: [
35
+ /* @__PURE__ */ jsx("div", { className: "mb-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-white/60", children: "You" }),
36
+ /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap text-[15px] leading-6.5 text-white", children: textContent })
37
37
  ] }),
38
38
  actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center justify-end gap-1.5 text-xs text-muted-foreground", children: actions }) : null
39
39
  ] }) });
@@ -55,7 +55,7 @@ var MessageList = memo2(
55
55
  renderUserMessageActions,
56
56
  renderToolActions
57
57
  }) => {
58
- return /* @__PURE__ */ jsx2("div", { className: "space-y-3", children: groups.map((group) => {
58
+ return /* @__PURE__ */ jsx2("div", { className: "space-y-4", children: groups.map((group) => {
59
59
  if (group.type === "user") {
60
60
  const messageParts = partMap[group.message.id] ?? [];
61
61
  return /* @__PURE__ */ jsx2(
@@ -396,7 +396,7 @@ function ChatInput({
396
396
  return /* @__PURE__ */ jsxs4(
397
397
  "div",
398
398
  {
399
- className: cn("px-4 py-3 relative", className),
399
+ className: cn("relative", className),
400
400
  onDragEnter: onAttach ? handleDragEnter : void 0,
401
401
  onDragLeave: onAttach ? handleDragLeave : void 0,
402
402
  onDragOver: onAttach ? handleDragOver : void 0,
@@ -468,12 +468,12 @@ function ChatInput({
468
468
  f.id
469
469
  ))
470
470
  ] }),
471
- /* @__PURE__ */ jsx5("div", { className: "rounded-[var(--radius-xl)] border border-[var(--chat-input-border,var(--border-default))] [background:var(--chat-input-bg,var(--depth-2))] shadow-[var(--chat-input-shadow,var(--shadow-card))] transition-all focus-within:border-[var(--chat-input-focus-border,var(--border-accent))] focus-within:shadow-[var(--chat-input-focus-shadow,var(--shadow-card))]", children: /* @__PURE__ */ jsxs4("div", { className: "rounded-[var(--radius-xl)] px-3 py-[var(--chat-input-py)]", children: [
471
+ /* @__PURE__ */ jsx5("div", { className: "rounded-[24px] border border-[var(--chat-input-border,var(--border-default))] [background:var(--chat-input-bg,var(--bg-card))] shadow-[var(--chat-input-shadow,0_1px_2px_rgba(15,23,42,0.05))] transition-all focus-within:border-[var(--chat-input-focus-border,var(--border-accent))] focus-within:shadow-[var(--chat-input-focus-shadow,0_10px_30px_rgba(15,23,42,0.08))]", children: /* @__PURE__ */ jsxs4("div", { className: "rounded-[24px] px-4 py-[var(--chat-input-py)]", children: [
472
472
  (inputLabel !== null || idleStatus !== null || streamingStatus !== null) && /* @__PURE__ */ jsxs4("div", { className: "mb-1.5 flex items-center justify-between gap-3 px-1", children: [
473
473
  inputLabel !== null && /* @__PURE__ */ jsx5("div", { className: "text-[var(--chat-label-size,11px)] font-[var(--chat-label-weight,600)] uppercase tracking-[var(--chat-label-tracking,0.16em)] text-[var(--text-muted)]", children: inputLabel }),
474
474
  (idleStatus !== null || streamingStatus !== null) && /* @__PURE__ */ jsx5("div", { className: "text-[var(--chat-label-size,11px)] text-[var(--text-muted)]", children: isStreaming ? streamingStatus ?? "" : idleStatus ?? "" })
475
475
  ] }),
476
- /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2", children: [
476
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2.5", children: [
477
477
  onAttach && /* @__PURE__ */ jsxs4(Fragment, { children: [
478
478
  /* @__PURE__ */ jsx5(
479
479
  "button",
@@ -535,7 +535,7 @@ function ChatInput({
535
535
  disabled: isStreaming || disabled,
536
536
  rows: 1,
537
537
  "aria-label": "Message input",
538
- className: "min-h-[32px] max-h-[120px] flex-1 resize-none bg-transparent text-[14px] leading-6 text-foreground placeholder:text-muted-foreground disabled:opacity-50 focus-visible:outline-none"
538
+ className: "min-h-[42px] max-h-[160px] flex-1 resize-none bg-transparent py-2 text-[15px] leading-6 text-foreground placeholder:text-muted-foreground disabled:opacity-50 focus-visible:outline-none"
539
539
  }
540
540
  ),
541
541
  isStreaming ? /* @__PURE__ */ jsx5(
@@ -547,15 +547,18 @@ function ChatInput({
547
547
  className: "mb-0.5 shrink-0 rounded-[var(--radius-lg)] border border-[var(--code-error)]/20 bg-[var(--code-error)]/14 p-2.5 text-[var(--code-error)] transition-colors hover:bg-[var(--code-error)]/24 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--code-error)]/50",
548
548
  children: /* @__PURE__ */ jsx5(Square, { className: "h-4 w-4" })
549
549
  }
550
- ) : /* @__PURE__ */ jsx5(
550
+ ) : /* @__PURE__ */ jsxs4(
551
551
  "button",
552
552
  {
553
553
  type: "button",
554
554
  onClick: handleSend,
555
555
  disabled: !value.trim() || disabled,
556
556
  "aria-label": "Send message",
557
- className: "mb-0.5 shrink-0 rounded-[var(--chat-send-radius,var(--radius-lg))] border border-[var(--chat-send-border,var(--border-accent))] [background:var(--chat-send-bg,var(--accent-surface-soft))] p-2.5 text-[var(--chat-send-color,var(--accent-text))] shadow-[var(--chat-send-shadow,none)] transition-all hover:translate-y-[-1px] hover:[background:var(--chat-send-hover-bg,var(--accent-surface-strong))] disabled:opacity-30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--chat-send-ring,var(--border-accent))]",
558
- children: /* @__PURE__ */ jsx5(Send, { className: "h-4 w-4" })
557
+ className: "mb-0.5 inline-flex shrink-0 items-center gap-1.5 rounded-full border border-[var(--chat-send-border,var(--border-accent))] [background:var(--chat-send-bg,var(--brand-primary))] px-3.5 py-2.5 text-sm font-medium text-[var(--chat-send-color,white)] shadow-[var(--chat-send-shadow,0_6px_16px_rgba(15,23,42,0.12))] transition-all hover:translate-y-[-1px] hover:[background:var(--chat-send-hover-bg,var(--brand-strong))] disabled:opacity-30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--chat-send-ring,var(--border-accent))]",
558
+ children: [
559
+ /* @__PURE__ */ jsx5(Send, { className: "h-4 w-4" }),
560
+ /* @__PURE__ */ jsx5("span", { children: "Send" })
561
+ ]
559
562
  }
560
563
  )
561
564
  ] })
@@ -861,16 +864,13 @@ var ChatContainer = memo3(
861
864
  },
862
865
  [onSend]
863
866
  );
864
- return /* @__PURE__ */ jsxs5("div", { className: cn("flex flex-col h-full", className), children: [
867
+ return /* @__PURE__ */ jsxs5("div", { className: cn("flex h-full flex-col", className), children: [
865
868
  /* @__PURE__ */ jsx6(
866
869
  "div",
867
870
  {
868
871
  ref: scrollRef,
869
- className: "flex-1 overflow-y-auto px-4 py-2 [scrollbar-gutter:stable]",
870
- children: messages.length === 0 ? /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxs5("div", { className: "max-w-md rounded-[var(--radius-xl)] border border-border bg-[linear-gradient(180deg,rgba(255,255,255,0.03),transparent)] px-5 py-5 text-center shadow-[var(--shadow-card)]", children: [
871
- /* @__PURE__ */ jsx6("div", { className: "text-sm font-semibold text-foreground", children: "Start the filing workflow" }),
872
- /* @__PURE__ */ jsx6("div", { className: "mt-2 text-sm leading-relaxed text-muted-foreground", children: "Ask the agent to analyze documents, generate forms, explain a calculation, or review the current filing package." })
873
- ] }) }) : presentation === "timeline" ? /* @__PURE__ */ jsx6(AgentTimeline, { items: timeline.items, isThinking: timeline.showThinking }) : /* @__PURE__ */ jsx6("div", { className: "mx-auto flex w-full max-w-5xl flex-col gap-3", children: /* @__PURE__ */ jsx6(
872
+ className: "flex-1 overflow-y-auto [scrollbar-gutter:stable]",
873
+ children: messages.length === 0 ? /* @__PURE__ */ jsx6("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx6("div", { className: "max-w-md text-center", children: /* @__PURE__ */ jsx6("div", { className: "text-sm font-medium text-muted-foreground", children: "Start a conversation." }) }) }) : presentation === "timeline" ? /* @__PURE__ */ jsx6("div", { className: "mx-auto flex min-h-full w-full max-w-3xl flex-col justify-end", children: /* @__PURE__ */ jsx6(AgentTimeline, { items: timeline.items, isThinking: timeline.showThinking }) }) : /* @__PURE__ */ jsx6("div", { className: "mx-auto flex min-h-full w-full max-w-3xl flex-col justify-end", children: /* @__PURE__ */ jsx6(
874
874
  MessageList,
875
875
  {
876
876
  groups,
@@ -886,13 +886,13 @@ var ChatContainer = memo3(
886
886
  ) })
887
887
  }
888
888
  ),
889
- !isAtBottom && /* @__PURE__ */ jsx6("div", { className: "flex justify-center -mt-10 relative z-10", children: /* @__PURE__ */ jsxs5(
889
+ !isAtBottom && /* @__PURE__ */ jsx6("div", { className: "relative z-10 -mt-10 flex justify-center", children: /* @__PURE__ */ jsxs5(
890
890
  "button",
891
891
  {
892
892
  onClick: scrollToBottom,
893
893
  className: cn(
894
894
  "flex items-center gap-1.5 px-3 py-1.5 rounded-full",
895
- "border border-border bg-card shadow-[var(--shadow-card)]",
895
+ "border border-border bg-card shadow-[0_6px_16px_rgba(15,23,42,0.08)]",
896
896
  "text-xs text-foreground transition-colors hover:bg-accent",
897
897
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60"
898
898
  ),
@@ -43,8 +43,23 @@ function InlineCode({ className, children, ...props }) {
43
43
 
44
44
  // src/openui/openui-artifact-renderer.tsx
45
45
  import { Fragment } from "react";
46
- import { ArrowRight, Minus } from "lucide-react";
46
+ import { Minus } from "lucide-react";
47
47
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
48
+ var NODE_TYPES = /* @__PURE__ */ new Set([
49
+ "heading",
50
+ "text",
51
+ "badge",
52
+ "stat",
53
+ "key_value",
54
+ "code",
55
+ "markdown",
56
+ "table",
57
+ "actions",
58
+ "separator",
59
+ "stack",
60
+ "grid",
61
+ "card"
62
+ ]);
48
63
  var GAP_STYLES = {
49
64
  sm: "gap-2",
50
65
  md: "gap-4",
@@ -71,6 +86,12 @@ function formatValue(value) {
71
86
  }
72
87
  return value;
73
88
  }
89
+ function asArray(value) {
90
+ return Array.isArray(value) ? value : [];
91
+ }
92
+ function isOpenUIComponentNode(value) {
93
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string" && NODE_TYPES.has(value.type);
94
+ }
74
95
  function renderActions(actions, onAction) {
75
96
  if (actions.length === 0) return null;
76
97
  return /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap items-center gap-2", children: actions.map((action) => /* @__PURE__ */ jsx2(
@@ -149,7 +170,10 @@ function renderNode(node, onAction) {
149
170
  )
150
171
  ] }) });
151
172
  case "key_value":
152
- return /* @__PURE__ */ jsx2("dl", { className: "grid gap-3 sm:grid-cols-2", children: node.items.map((item, index) => /* @__PURE__ */ jsxs(
173
+ if (asArray(node.items).length === 0) {
174
+ return null;
175
+ }
176
+ return /* @__PURE__ */ jsx2("dl", { className: "grid gap-3 sm:grid-cols-2", children: asArray(node.items).map((item, index) => /* @__PURE__ */ jsxs(
153
177
  "div",
154
178
  {
155
179
  className: "rounded-[var(--radius-lg)] border border-border bg-card px-4 py-3",
@@ -185,9 +209,12 @@ function renderNode(node, onAction) {
185
209
  case "markdown":
186
210
  return /* @__PURE__ */ jsx2("div", { className: "rounded-[var(--radius-lg)] border border-border bg-card p-5", children: /* @__PURE__ */ jsx2(Markdown, { className: "prose-sm max-w-none", children: node.content }) });
187
211
  case "table":
212
+ if (asArray(node.columns).length === 0) {
213
+ return null;
214
+ }
188
215
  return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-[var(--radius-lg)] border border-border bg-card", children: [
189
216
  /* @__PURE__ */ jsxs(Table, { children: [
190
- /* @__PURE__ */ jsx2(TableHeader, { children: /* @__PURE__ */ jsx2(TableRow, { className: "border-border", children: node.columns.map((column) => /* @__PURE__ */ jsx2(
217
+ /* @__PURE__ */ jsx2(TableHeader, { children: /* @__PURE__ */ jsx2(TableRow, { className: "border-border", children: asArray(node.columns).map((column) => /* @__PURE__ */ jsx2(
191
218
  TableHead,
192
219
  {
193
220
  className: cn(
@@ -198,7 +225,7 @@ function renderNode(node, onAction) {
198
225
  },
199
226
  column.key
200
227
  )) }) }),
201
- /* @__PURE__ */ jsx2(TableBody, { children: node.rows.map((row, rowIndex) => /* @__PURE__ */ jsx2(TableRow, { className: "border-border", children: node.columns.map((column) => /* @__PURE__ */ jsx2(
228
+ /* @__PURE__ */ jsx2(TableBody, { children: asArray(node.rows).map((row, rowIndex) => /* @__PURE__ */ jsx2(TableRow, { className: "border-border", children: asArray(node.columns).map((column) => /* @__PURE__ */ jsx2(
202
229
  TableCell,
203
230
  {
204
231
  className: cn(
@@ -213,7 +240,7 @@ function renderNode(node, onAction) {
213
240
  node.caption && /* @__PURE__ */ jsx2("div", { className: "border-t border-border px-4 py-2 text-xs text-muted-foreground", children: node.caption })
214
241
  ] });
215
242
  case "actions":
216
- return renderActions(node.actions, onAction);
243
+ return renderActions(asArray(node.actions), onAction);
217
244
  case "separator":
218
245
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
219
246
  /* @__PURE__ */ jsx2("div", { className: "h-px flex-1 bg-border" }),
@@ -221,6 +248,9 @@ function renderNode(node, onAction) {
221
248
  /* @__PURE__ */ jsx2("div", { className: "h-px flex-1 bg-border" })
222
249
  ] });
223
250
  case "stack":
251
+ if (asArray(node.children).length === 0) {
252
+ return null;
253
+ }
224
254
  return /* @__PURE__ */ jsx2(
225
255
  "div",
226
256
  {
@@ -231,14 +261,17 @@ function renderNode(node, onAction) {
231
261
  ALIGN_STYLES[node.align ?? "stretch"],
232
262
  node.wrap && "flex-wrap"
233
263
  ),
234
- children: node.children.map((child, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`))
264
+ children: asArray(node.children).map((child, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`))
235
265
  }
236
266
  );
237
267
  case "grid":
238
- return /* @__PURE__ */ jsx2("div", { className: cn("grid", GRID_STYLES[node.columns ?? 2], GAP_STYLES[node.gap ?? "md"]), children: node.children.map((child, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`)) });
268
+ if (asArray(node.children).length === 0) {
269
+ return null;
270
+ }
271
+ return /* @__PURE__ */ jsx2("div", { className: cn("grid", GRID_STYLES[node.columns ?? 2], GAP_STYLES[node.gap ?? "md"]), children: asArray(node.children).map((child, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`)) });
239
272
  case "card":
240
273
  return /* @__PURE__ */ jsxs(Card, { variant: "glass", className: "border-border shadow-[var(--shadow-card)]", children: [
241
- (node.eyebrow || node.title || node.description || node.badge || node.actions) && /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 p-4 pb-0", children: [
274
+ (node.eyebrow || node.title || node.description || node.badge || asArray(node.actions).length > 0) && /* @__PURE__ */ jsxs(CardHeader, { className: "gap-2 p-4 pb-0", children: [
242
275
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
243
276
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
244
277
  node.eyebrow && /* @__PURE__ */ jsx2("div", { className: "text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground", children: node.eyebrow }),
@@ -247,9 +280,9 @@ function renderNode(node, onAction) {
247
280
  ] }),
248
281
  node.badge && /* @__PURE__ */ jsx2(Badge, { variant: node.badge.tone ?? "outline", children: node.badge.label })
249
282
  ] }),
250
- node.actions && renderActions(node.actions, onAction)
283
+ asArray(node.actions).length > 0 && renderActions(asArray(node.actions), onAction)
251
284
  ] }),
252
- node.children && node.children.length > 0 && /* @__PURE__ */ jsx2(CardContent, { className: "space-y-4 p-4", children: node.children.map((child, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`)) })
285
+ asArray(node.children).length > 0 && /* @__PURE__ */ jsx2(CardContent, { className: "space-y-4 p-4", children: asArray(node.children).map((child, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(child, onAction) }, child.id ?? `${child.type}-${index}`)) })
253
286
  ] });
254
287
  }
255
288
  }
@@ -258,30 +291,23 @@ function OpenUIArtifactRenderer({
258
291
  onAction,
259
292
  className
260
293
  }) {
261
- const nodes = Array.isArray(schema) ? schema : [schema];
294
+ const nodes = (Array.isArray(schema) ? schema : [schema]).filter(isOpenUIComponentNode);
262
295
  if (nodes.length === 0) {
263
296
  return /* @__PURE__ */ jsx2(
264
297
  "div",
265
298
  {
266
299
  className: cn(
267
- "flex h-full min-h-[16rem] items-center justify-center rounded-[var(--radius-xl)] border border-dashed border-border bg-card p-6 text-center",
300
+ "flex min-h-[6rem] items-center justify-center rounded-[var(--radius-xl)] border border-dashed border-border bg-card p-5 text-center",
268
301
  className
269
302
  ),
270
303
  children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
271
304
  /* @__PURE__ */ jsx2("div", { className: "mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-muted/50 text-muted-foreground", children: /* @__PURE__ */ jsx2(Minus, { className: "h-4 w-4" }) }),
272
- /* @__PURE__ */ jsx2("div", { className: "text-sm font-medium text-foreground", children: "No structured artifact payload" }),
273
- /* @__PURE__ */ jsx2("div", { className: "text-sm text-muted-foreground", children: "Pass an OpenUI-like schema to render dynamic result panels with sandbox-ui primitives." })
305
+ /* @__PURE__ */ jsx2("div", { className: "text-sm font-medium text-foreground", children: "No view was generated." })
274
306
  ] })
275
307
  }
276
308
  );
277
309
  }
278
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4 p-4", className), children: [
279
- nodes.map((node, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(node, onAction) }, node.id ?? `${node.type}-${index}`)),
280
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-[var(--radius-lg)] border border-border bg-card px-3 py-2 text-xs text-muted-foreground", children: [
281
- /* @__PURE__ */ jsx2(ArrowRight, { className: "h-3.5 w-3.5" }),
282
- "Structured artifact rendered through sandbox-ui primitives"
283
- ] })
284
- ] });
310
+ return /* @__PURE__ */ jsx2("div", { className: cn("space-y-4", className), children: nodes.map((node, index) => /* @__PURE__ */ jsx2(Fragment, { children: renderNode(node, onAction) }, node.id ?? `${node.type}-${index}`)) });
285
311
  }
286
312
 
287
313
  export {