@nextclaw/agent-chat-ui 0.2.2 → 0.2.3

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.d.ts CHANGED
@@ -128,6 +128,7 @@ type ChatInputBarProps = {
128
128
  placeholder: string;
129
129
  disabled: boolean;
130
130
  onNodesChange: (nodes: ChatComposerNode[]) => void;
131
+ onFilesAdd?: (files: File[]) => Promise<void> | void;
131
132
  onSlashQueryChange?: (query: string | null) => void;
132
133
  };
133
134
  slashMenu: Pick<ChatSlashMenuProps, 'isLoading' | 'items' | 'texts'>;
@@ -155,6 +156,14 @@ type ChatMessagePartViewModel = {
155
156
  } | {
156
157
  type: 'tool-card';
157
158
  card: ChatToolPartViewModel;
159
+ } | {
160
+ type: 'file';
161
+ file: {
162
+ label: string;
163
+ mimeType: string;
164
+ dataUrl?: string;
165
+ isImage: boolean;
166
+ };
158
167
  } | {
159
168
  type: 'unknown';
160
169
  label: string;
package/dist/index.js CHANGED
@@ -1082,22 +1082,14 @@ var ChatComposerController = class {
1082
1082
  this.selection = { start: nextOffset, end: nextOffset };
1083
1083
  return this.getSnapshot();
1084
1084
  };
1085
+ this.insertFileToken = (tokenKey, label) => {
1086
+ return this.insertToken("file", tokenKey, label);
1087
+ };
1085
1088
  this.insertSkillToken = (tokenKey, label) => {
1086
1089
  if (this.getSelectedSkillKeys().includes(tokenKey)) {
1087
1090
  return this.getSnapshot();
1088
1091
  }
1089
- const trigger = this.getSlashTrigger();
1090
- const documentLength = this.getDocumentLength();
1091
- const replaceStart = trigger?.start ?? this.selection?.start ?? documentLength;
1092
- const replaceEnd = trigger?.end ?? this.selection?.end ?? replaceStart;
1093
- this.nodes = replaceChatComposerRange(
1094
- this.nodes,
1095
- replaceStart,
1096
- replaceEnd,
1097
- [createChatComposerTokenNode({ tokenKind: "skill", tokenKey, label })]
1098
- );
1099
- this.selection = { start: replaceStart + 1, end: replaceStart + 1 };
1100
- return this.getSnapshot();
1092
+ return this.insertToken("skill", tokenKey, label, this.getSlashTrigger());
1101
1093
  };
1102
1094
  this.syncSelectedSkills = (nextKeys, options) => {
1103
1095
  const selectedSkillKeys = this.getSelectedSkillKeys();
@@ -1150,6 +1142,19 @@ var ChatComposerController = class {
1150
1142
  this.getSelectedSkillKeys = () => {
1151
1143
  return extractChatComposerTokenKeys(this.nodes, "skill");
1152
1144
  };
1145
+ this.insertToken = (tokenKind, tokenKey, label, trigger = null) => {
1146
+ const documentLength = this.getDocumentLength();
1147
+ const replaceStart = trigger?.start ?? this.selection?.start ?? documentLength;
1148
+ const replaceEnd = trigger?.end ?? this.selection?.end ?? replaceStart;
1149
+ this.nodes = replaceChatComposerRange(
1150
+ this.nodes,
1151
+ replaceStart,
1152
+ replaceEnd,
1153
+ [createChatComposerTokenNode({ tokenKind, tokenKey, label })]
1154
+ );
1155
+ this.selection = { start: replaceStart + 1, end: replaceStart + 1 };
1156
+ return this.getSnapshot();
1157
+ };
1153
1158
  this.getSlashTrigger = () => {
1154
1159
  return resolveChatComposerSlashTrigger(this.nodes, this.selection);
1155
1160
  };
@@ -1443,7 +1448,13 @@ var ChatComposerViewController = class {
1443
1448
  }
1444
1449
  };
1445
1450
  this.handlePaste = (params) => {
1446
- const { event, commitSnapshot } = params;
1451
+ const { event, onFilesAdd, commitSnapshot } = params;
1452
+ const files = Array.from(event.clipboardData.files ?? []);
1453
+ if (files.length > 0 && onFilesAdd) {
1454
+ event.preventDefault();
1455
+ void onFilesAdd(files);
1456
+ return;
1457
+ }
1447
1458
  const text = event.clipboardData.getData("text/plain");
1448
1459
  if (!text) {
1449
1460
  return;
@@ -1497,6 +1508,9 @@ var ChatComposerRuntime = class {
1497
1508
  insertSlashItem: (item) => {
1498
1509
  this.viewController.insertSlashItem(item, this.commitSnapshot);
1499
1510
  },
1511
+ insertFileToken: (tokenKey, label) => {
1512
+ this.commitSnapshot(this.controller.insertFileToken(tokenKey, label));
1513
+ },
1500
1514
  syncSelectedSkills: (nextKeys, options) => {
1501
1515
  this.viewController.syncSelectedSkills(nextKeys, options, this.commitSnapshot);
1502
1516
  }
@@ -1558,6 +1572,12 @@ var ChatComposerRuntime = class {
1558
1572
  };
1559
1573
  this.handleKeyDown = (event) => {
1560
1574
  const config = this.requireConfig();
1575
+ if (this.rootElement && !this.isComposing) {
1576
+ const nextSnapshot = this.viewController.syncSelectionFromRoot(this.rootElement);
1577
+ this.selection = nextSnapshot.selection;
1578
+ this.selectedRange = nextSnapshot.selection;
1579
+ this.snapshot = nextSnapshot;
1580
+ }
1561
1581
  const activeSlashItem = config.slashItems[config.activeSlashIndex] ?? null;
1562
1582
  this.viewController.handleKeyDown({
1563
1583
  event,
@@ -1575,6 +1595,7 @@ var ChatComposerRuntime = class {
1575
1595
  this.handlePaste = (event) => {
1576
1596
  this.viewController.handlePaste({
1577
1597
  event,
1598
+ onFilesAdd: this.requireConfig().onFilesAdd,
1578
1599
  commitSnapshot: this.commitSnapshot
1579
1600
  });
1580
1601
  };
@@ -1605,6 +1626,7 @@ var ChatComposerRuntime = class {
1605
1626
  };
1606
1627
  this.syncSlashState = (nextSnapshot) => {
1607
1628
  const config = this.requireConfig();
1629
+ config.onSlashTriggerChange?.(nextSnapshot.slashTrigger);
1608
1630
  config.onSlashQueryChange?.(nextSnapshot.slashTrigger?.query ?? null);
1609
1631
  config.onSlashOpenChange(nextSnapshot.slashTrigger !== null);
1610
1632
  };
@@ -1630,7 +1652,9 @@ var ChatInputBarTokenizedComposer = forwardRef6(function ChatInputBarTokenizedCo
1630
1652
  slashItems,
1631
1653
  actions,
1632
1654
  onNodesChange,
1655
+ onFilesAdd,
1633
1656
  onSlashQueryChange,
1657
+ onSlashTriggerChange,
1634
1658
  onSlashOpenChange,
1635
1659
  onSlashActiveIndexChange,
1636
1660
  activeSlashIndex
@@ -1655,7 +1679,9 @@ var ChatInputBarTokenizedComposer = forwardRef6(function ChatInputBarTokenizedCo
1655
1679
  slashItems,
1656
1680
  actions,
1657
1681
  onNodesChange,
1682
+ onFilesAdd,
1658
1683
  onSlashQueryChange,
1684
+ onSlashTriggerChange,
1659
1685
  onSlashOpenChange,
1660
1686
  onSlashActiveIndexChange,
1661
1687
  activeSlashIndex,
@@ -1722,7 +1748,9 @@ function ChatInputBar(props) {
1722
1748
  const composerRef = useRef4(null);
1723
1749
  const [slashQuery, setSlashQuery] = useState4(null);
1724
1750
  const [activeSlashIndex, setActiveSlashIndex] = useState4(0);
1725
- const isSlashPanelOpen = slashQuery !== null;
1751
+ const [activeSlashTriggerStart, setActiveSlashTriggerStart] = useState4(null);
1752
+ const [dismissedSlashTriggerStart, setDismissedSlashTriggerStart] = useState4(null);
1753
+ const isSlashPanelOpen = activeSlashTriggerStart !== null && dismissedSlashTriggerStart !== activeSlashTriggerStart;
1726
1754
  const activeSlashItem = props.slashMenu.items[activeSlashIndex] ?? null;
1727
1755
  useEffect4(() => {
1728
1756
  setActiveSlashIndex((current) => {
@@ -1737,6 +1765,11 @@ function ChatInputBar(props) {
1737
1765
  setActiveSlashIndex(0);
1738
1766
  }
1739
1767
  }, [slashQuery]);
1768
+ useEffect4(() => {
1769
+ if (activeSlashTriggerStart === null && dismissedSlashTriggerStart !== null) {
1770
+ setDismissedSlashTriggerStart(null);
1771
+ }
1772
+ }, [activeSlashTriggerStart, dismissedSlashTriggerStart]);
1740
1773
  const toolbar = useMemo3(() => {
1741
1774
  if (!props.toolbar.skillPicker) {
1742
1775
  return props.toolbar;
@@ -1764,13 +1797,17 @@ function ChatInputBar(props) {
1764
1797
  actions: props.toolbar.actions,
1765
1798
  activeSlashIndex,
1766
1799
  onNodesChange: props.composer.onNodesChange,
1800
+ onFilesAdd: props.composer.onFilesAdd,
1767
1801
  onSlashQueryChange: (query) => {
1768
1802
  setSlashQuery(query);
1769
1803
  props.composer.onSlashQueryChange?.(query);
1770
1804
  },
1805
+ onSlashTriggerChange: (trigger) => {
1806
+ setActiveSlashTriggerStart(trigger?.start ?? null);
1807
+ },
1771
1808
  onSlashOpenChange: (open) => {
1772
- if (!open) {
1773
- setSlashQuery(null);
1809
+ if (!open && activeSlashTriggerStart !== null) {
1810
+ setDismissedSlashTriggerStart(activeSlashTriggerStart);
1774
1811
  }
1775
1812
  },
1776
1813
  onSlashActiveIndexChange: setActiveSlashIndex
@@ -1786,11 +1823,12 @@ function ChatInputBar(props) {
1786
1823
  activeItem: activeSlashItem,
1787
1824
  texts: props.slashMenu.texts,
1788
1825
  onSelectItem: (item) => {
1826
+ setDismissedSlashTriggerStart(null);
1789
1827
  composerRef.current?.insertSlashItem(item);
1790
1828
  },
1791
1829
  onOpenChange: (open) => {
1792
- if (!open) {
1793
- setSlashQuery(null);
1830
+ if (!open && activeSlashTriggerStart !== null) {
1831
+ setDismissedSlashTriggerStart(activeSlashTriggerStart);
1794
1832
  }
1795
1833
  },
1796
1834
  onSetActiveIndex: setActiveSlashIndex
@@ -2077,12 +2115,27 @@ function ChatMessageMarkdown(props) {
2077
2115
  return /* @__PURE__ */ jsx14("div", { className: cn("chat-markdown", isUser ? "chat-markdown-user" : "chat-markdown-assistant"), children: /* @__PURE__ */ jsx14(ReactMarkdown, { skipHtml: true, remarkPlugins: [remarkGfm], components: markdownComponents, children: trimMarkdown(props.text) }) });
2078
2116
  }
2079
2117
 
2080
- // src/components/chat/ui/chat-message-list/chat-reasoning-block.tsx
2118
+ // src/components/chat/ui/chat-message-list/chat-message-file.tsx
2081
2119
  import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
2120
+ function ChatMessageFile({ file }) {
2121
+ if (file.isImage && file.dataUrl) {
2122
+ return /* @__PURE__ */ jsxs8("figure", { className: "overflow-hidden rounded-2xl border border-black/8 bg-black/6", children: [
2123
+ /* @__PURE__ */ jsx15("img", { src: file.dataUrl, alt: file.label, className: "block max-h-80 w-full object-contain" }),
2124
+ /* @__PURE__ */ jsx15("figcaption", { className: "border-t border-black/8 px-3 py-2 text-xs opacity-80", children: file.label })
2125
+ ] });
2126
+ }
2127
+ return /* @__PURE__ */ jsxs8("div", { className: "rounded-2xl border border-black/8 bg-black/6 px-3 py-2 text-sm", children: [
2128
+ /* @__PURE__ */ jsx15("div", { className: "font-medium", children: file.label }),
2129
+ /* @__PURE__ */ jsx15("div", { className: "text-xs opacity-75", children: file.mimeType })
2130
+ ] });
2131
+ }
2132
+
2133
+ // src/components/chat/ui/chat-message-list/chat-reasoning-block.tsx
2134
+ import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
2082
2135
  function ChatReasoningBlock(props) {
2083
- return /* @__PURE__ */ jsxs8("details", { className: "mt-3", open: true, children: [
2084
- /* @__PURE__ */ jsx15("summary", { className: cn("cursor-pointer text-xs", props.isUser ? "text-primary-100" : "text-gray-500"), children: props.label }),
2085
- /* @__PURE__ */ jsx15(
2136
+ return /* @__PURE__ */ jsxs9("details", { className: "mt-3", open: true, children: [
2137
+ /* @__PURE__ */ jsx16("summary", { className: cn("cursor-pointer text-xs", props.isUser ? "text-primary-100" : "text-gray-500"), children: props.label }),
2138
+ /* @__PURE__ */ jsx16(
2086
2139
  "pre",
2087
2140
  {
2088
2141
  className: cn(
@@ -2097,87 +2150,90 @@ function ChatReasoningBlock(props) {
2097
2150
 
2098
2151
  // src/components/chat/ui/chat-message-list/chat-tool-card.tsx
2099
2152
  import { Clock3, FileSearch, Globe, Search as Search2, SendHorizontal, Terminal, Wrench as Wrench2 } from "lucide-react";
2100
- import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
2153
+ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
2101
2154
  var TOOL_OUTPUT_PREVIEW_MAX = 220;
2102
2155
  function renderToolIcon(toolName) {
2103
2156
  const lowered = toolName.toLowerCase();
2104
2157
  if (lowered.includes("exec") || lowered.includes("shell") || lowered.includes("command")) {
2105
- return /* @__PURE__ */ jsx16(Terminal, { className: "h-3.5 w-3.5" });
2158
+ return /* @__PURE__ */ jsx17(Terminal, { className: "h-3.5 w-3.5" });
2106
2159
  }
2107
2160
  if (lowered.includes("search")) {
2108
- return /* @__PURE__ */ jsx16(Search2, { className: "h-3.5 w-3.5" });
2161
+ return /* @__PURE__ */ jsx17(Search2, { className: "h-3.5 w-3.5" });
2109
2162
  }
2110
2163
  if (lowered.includes("fetch") || lowered.includes("http") || lowered.includes("web")) {
2111
- return /* @__PURE__ */ jsx16(Globe, { className: "h-3.5 w-3.5" });
2164
+ return /* @__PURE__ */ jsx17(Globe, { className: "h-3.5 w-3.5" });
2112
2165
  }
2113
2166
  if (lowered.includes("read") || lowered.includes("file")) {
2114
- return /* @__PURE__ */ jsx16(FileSearch, { className: "h-3.5 w-3.5" });
2167
+ return /* @__PURE__ */ jsx17(FileSearch, { className: "h-3.5 w-3.5" });
2115
2168
  }
2116
2169
  if (lowered.includes("message") || lowered.includes("send")) {
2117
- return /* @__PURE__ */ jsx16(SendHorizontal, { className: "h-3.5 w-3.5" });
2170
+ return /* @__PURE__ */ jsx17(SendHorizontal, { className: "h-3.5 w-3.5" });
2118
2171
  }
2119
2172
  if (lowered.includes("cron") || lowered.includes("schedule")) {
2120
- return /* @__PURE__ */ jsx16(Clock3, { className: "h-3.5 w-3.5" });
2173
+ return /* @__PURE__ */ jsx17(Clock3, { className: "h-3.5 w-3.5" });
2121
2174
  }
2122
- return /* @__PURE__ */ jsx16(Wrench2, { className: "h-3.5 w-3.5" });
2175
+ return /* @__PURE__ */ jsx17(Wrench2, { className: "h-3.5 w-3.5" });
2123
2176
  }
2124
2177
  function ChatToolCard({ card }) {
2125
2178
  const output = card.output?.trim() ?? "";
2126
2179
  const showDetails = output.length > TOOL_OUTPUT_PREVIEW_MAX || output.includes("\n");
2127
2180
  const preview = showDetails ? `${output.slice(0, TOOL_OUTPUT_PREVIEW_MAX)}...` : output;
2128
2181
  const showOutputSection = card.kind === "result" || card.hasResult;
2129
- return /* @__PURE__ */ jsxs9("div", { className: "rounded-xl border border-amber-200/80 bg-amber-50/60 px-3 py-2.5", children: [
2130
- /* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap items-center gap-2 text-xs font-semibold text-amber-800", children: [
2182
+ return /* @__PURE__ */ jsxs10("div", { className: "rounded-xl border border-amber-200/80 bg-amber-50/60 px-3 py-2.5", children: [
2183
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2 text-xs font-semibold text-amber-800", children: [
2131
2184
  renderToolIcon(card.toolName),
2132
- /* @__PURE__ */ jsx16("span", { children: card.titleLabel }),
2133
- /* @__PURE__ */ jsx16("span", { className: "font-mono text-[11px] text-amber-900/80", children: card.toolName })
2185
+ /* @__PURE__ */ jsx17("span", { children: card.titleLabel }),
2186
+ /* @__PURE__ */ jsx17("span", { className: "font-mono text-[11px] text-amber-900/80", children: card.toolName })
2134
2187
  ] }),
2135
- card.summary ? /* @__PURE__ */ jsx16("div", { className: "mt-1 break-words font-mono text-[11px] text-amber-800/90", children: card.summary }) : null,
2136
- showOutputSection ? /* @__PURE__ */ jsx16("div", { className: "mt-2", children: !output ? /* @__PURE__ */ jsx16("div", { className: "text-[11px] text-amber-700/80", children: card.emptyLabel }) : showDetails ? /* @__PURE__ */ jsxs9("details", { className: "group", children: [
2137
- /* @__PURE__ */ jsx16("summary", { className: "cursor-pointer text-[11px] text-amber-700", children: card.outputLabel }),
2138
- /* @__PURE__ */ jsx16("pre", { className: "mt-2 whitespace-pre-wrap break-words rounded-lg border border-amber-200 bg-amber-100/40 p-2 text-[11px] text-amber-900", children: output })
2139
- ] }) : /* @__PURE__ */ jsx16("pre", { className: "rounded-lg border border-amber-200 bg-amber-100/40 p-2 text-[11px] whitespace-pre-wrap break-words text-amber-900", children: preview }) }) : null
2188
+ card.summary ? /* @__PURE__ */ jsx17("div", { className: "mt-1 break-words font-mono text-[11px] text-amber-800/90", children: card.summary }) : null,
2189
+ showOutputSection ? /* @__PURE__ */ jsx17("div", { className: "mt-2", children: !output ? /* @__PURE__ */ jsx17("div", { className: "text-[11px] text-amber-700/80", children: card.emptyLabel }) : showDetails ? /* @__PURE__ */ jsxs10("details", { className: "group", children: [
2190
+ /* @__PURE__ */ jsx17("summary", { className: "cursor-pointer text-[11px] text-amber-700", children: card.outputLabel }),
2191
+ /* @__PURE__ */ jsx17("pre", { className: "mt-2 whitespace-pre-wrap break-words rounded-lg border border-amber-200 bg-amber-100/40 p-2 text-[11px] text-amber-900", children: output })
2192
+ ] }) : /* @__PURE__ */ jsx17("pre", { className: "rounded-lg border border-amber-200 bg-amber-100/40 p-2 text-[11px] whitespace-pre-wrap break-words text-amber-900", children: preview }) }) : null
2140
2193
  ] });
2141
2194
  }
2142
2195
 
2143
2196
  // src/components/chat/ui/chat-message-list/chat-unknown-part.tsx
2144
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
2197
+ import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
2145
2198
  function ChatUnknownPart(props) {
2146
- return /* @__PURE__ */ jsxs10("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-2 text-xs text-gray-600", children: [
2147
- /* @__PURE__ */ jsxs10("div", { className: "font-semibold text-gray-700", children: [
2199
+ return /* @__PURE__ */ jsxs11("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-2 text-xs text-gray-600", children: [
2200
+ /* @__PURE__ */ jsxs11("div", { className: "font-semibold text-gray-700", children: [
2148
2201
  props.label,
2149
2202
  ": ",
2150
2203
  props.rawType
2151
2204
  ] }),
2152
- props.text ? /* @__PURE__ */ jsx17("pre", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-gray-500", children: props.text }) : null
2205
+ props.text ? /* @__PURE__ */ jsx18("pre", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-gray-500", children: props.text }) : null
2153
2206
  ] });
2154
2207
  }
2155
2208
 
2156
2209
  // src/components/chat/ui/chat-message-list/chat-message.tsx
2157
- import { jsx as jsx18 } from "react/jsx-runtime";
2210
+ import { jsx as jsx19 } from "react/jsx-runtime";
2158
2211
  function ChatMessage(props) {
2159
2212
  const { message, texts } = props;
2160
2213
  const { role } = message;
2161
2214
  const isUser = role === "user";
2162
- return /* @__PURE__ */ jsx18(
2215
+ return /* @__PURE__ */ jsx19(
2163
2216
  "div",
2164
2217
  {
2165
2218
  className: cn(
2166
2219
  "inline-block w-fit max-w-full rounded-2xl border px-4 py-3 shadow-sm",
2167
2220
  isUser ? "border-primary bg-primary text-white" : role === "assistant" ? "border-gray-200 bg-white text-gray-900" : "border-orange-200/80 bg-orange-50/70 text-gray-900"
2168
2221
  ),
2169
- children: /* @__PURE__ */ jsx18("div", { className: "space-y-2", children: message.parts.map((part, index) => {
2222
+ children: /* @__PURE__ */ jsx19("div", { className: "space-y-2", children: message.parts.map((part, index) => {
2170
2223
  if (part.type === "markdown") {
2171
- return /* @__PURE__ */ jsx18(ChatMessageMarkdown, { text: part.text, role, texts }, `markdown-${index}`);
2224
+ return /* @__PURE__ */ jsx19(ChatMessageMarkdown, { text: part.text, role, texts }, `markdown-${index}`);
2172
2225
  }
2173
2226
  if (part.type === "reasoning") {
2174
- return /* @__PURE__ */ jsx18(ChatReasoningBlock, { label: part.label, text: part.text, isUser }, `reasoning-${index}`);
2227
+ return /* @__PURE__ */ jsx19(ChatReasoningBlock, { label: part.label, text: part.text, isUser }, `reasoning-${index}`);
2175
2228
  }
2176
2229
  if (part.type === "tool-card") {
2177
- return /* @__PURE__ */ jsx18("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx18(ChatToolCard, { card: part.card }) }, `tool-${index}`);
2230
+ return /* @__PURE__ */ jsx19("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx19(ChatToolCard, { card: part.card }) }, `tool-${index}`);
2231
+ }
2232
+ if (part.type === "file") {
2233
+ return /* @__PURE__ */ jsx19(ChatMessageFile, { file: part.file }, `file-${index}`);
2178
2234
  }
2179
2235
  if (part.type === "unknown") {
2180
- return /* @__PURE__ */ jsx18(ChatUnknownPart, { label: part.label, rawType: part.rawType, text: part.text }, `unknown-${index}`);
2236
+ return /* @__PURE__ */ jsx19(ChatUnknownPart, { label: part.label, rawType: part.rawType, text: part.text }, `unknown-${index}`);
2181
2237
  }
2182
2238
  return null;
2183
2239
  }) })
@@ -2186,9 +2242,9 @@ function ChatMessage(props) {
2186
2242
  }
2187
2243
 
2188
2244
  // src/components/chat/ui/chat-message-list/chat-message-meta.tsx
2189
- import { jsxs as jsxs11 } from "react/jsx-runtime";
2245
+ import { jsxs as jsxs12 } from "react/jsx-runtime";
2190
2246
  function ChatMessageMeta(props) {
2191
- return /* @__PURE__ */ jsxs11(
2247
+ return /* @__PURE__ */ jsxs12(
2192
2248
  "div",
2193
2249
  {
2194
2250
  className: cn(
@@ -2205,7 +2261,7 @@ function ChatMessageMeta(props) {
2205
2261
  }
2206
2262
 
2207
2263
  // src/components/chat/ui/chat-message-list/chat-message-list.tsx
2208
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
2264
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2209
2265
  var INVISIBLE_ONLY_TEXT_PATTERN = /\u200B|\u200C|\u200D|\u2060|\uFEFF/g;
2210
2266
  function hasRenderableText(value) {
2211
2267
  const trimmed = value.trim();
@@ -2227,21 +2283,21 @@ function ChatMessageList(props) {
2227
2283
  const hasRenderableAssistantDraft = visibleMessages.some(
2228
2284
  (message) => message.role === "assistant" && (message.status === "streaming" || message.status === "pending")
2229
2285
  );
2230
- return /* @__PURE__ */ jsxs12("div", { className: cn("space-y-5", props.className), children: [
2286
+ return /* @__PURE__ */ jsxs13("div", { className: cn("space-y-5", props.className), children: [
2231
2287
  visibleMessages.map((message) => {
2232
2288
  const isUser = message.role === "user";
2233
- return /* @__PURE__ */ jsxs12("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
2234
- !isUser ? /* @__PURE__ */ jsx19(ChatMessageAvatar, { role: message.role }) : null,
2235
- /* @__PURE__ */ jsxs12("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
2236
- /* @__PURE__ */ jsx19(ChatMessage, { message, texts: props.texts }),
2237
- /* @__PURE__ */ jsx19(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser })
2289
+ return /* @__PURE__ */ jsxs13("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
2290
+ !isUser ? /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: message.role }) : null,
2291
+ /* @__PURE__ */ jsxs13("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
2292
+ /* @__PURE__ */ jsx20(ChatMessage, { message, texts: props.texts }),
2293
+ /* @__PURE__ */ jsx20(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser })
2238
2294
  ] }),
2239
- isUser ? /* @__PURE__ */ jsx19(ChatMessageAvatar, { role: message.role }) : null
2295
+ isUser ? /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: message.role }) : null
2240
2296
  ] }, message.id);
2241
2297
  }),
2242
- props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs12("div", { className: "flex justify-start gap-3", children: [
2243
- /* @__PURE__ */ jsx19(ChatMessageAvatar, { role: "assistant" }),
2244
- /* @__PURE__ */ jsx19("div", { className: "rounded-2xl border border-gray-200 bg-white px-4 py-3 text-sm text-gray-500 shadow-sm", children: props.texts.typingLabel })
2298
+ props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs13("div", { className: "flex justify-start gap-3", children: [
2299
+ /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: "assistant" }),
2300
+ /* @__PURE__ */ jsx20("div", { className: "rounded-2xl border border-gray-200 bg-white px-4 py-3 text-sm text-gray-500 shadow-sm", children: props.texts.typingLabel })
2245
2301
  ] }) : null
2246
2302
  ] });
2247
2303
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/agent-chat-ui",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "private": false,
5
5
  "description": "Reusable Nextclaw agent chat UI primitives and default skin.",
6
6
  "type": "module",