@nextclaw/agent-chat-ui 0.2.20 → 0.3.0

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
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { RefObject } from 'react';
2
+ import { ReactNode, RefObject } from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
5
  type ChatTexts = {
@@ -166,9 +166,18 @@ type ChatFileOperationBlockViewModel = {
166
166
  rawText?: string;
167
167
  truncated?: boolean;
168
168
  };
169
+ type ChatToolActionViewModel = {
170
+ kind: "open-session";
171
+ sessionId: string;
172
+ sessionKind: "child" | "session";
173
+ agentId?: string;
174
+ label?: string;
175
+ parentSessionId?: string;
176
+ };
169
177
  type ChatToolPartViewModel = {
170
178
  kind: "call" | "result";
171
179
  toolName: string;
180
+ agentId?: string;
172
181
  summary?: string;
173
182
  inputLabel?: string;
174
183
  input?: string;
@@ -180,6 +189,7 @@ type ChatToolPartViewModel = {
180
189
  titleLabel: string;
181
190
  outputLabel: string;
182
191
  emptyLabel: string;
192
+ action?: ChatToolActionViewModel;
183
193
  fileOperation?: {
184
194
  blocks: ChatFileOperationBlockViewModel[];
185
195
  };
@@ -246,6 +256,8 @@ type ChatMessageListProps = {
246
256
  hasAssistantDraft: boolean;
247
257
  texts: ChatMessageTexts;
248
258
  className?: string;
259
+ onToolAction?: (action: ChatToolActionViewModel) => void;
260
+ renderToolAgent?: (agentId: string) => ReactNode;
249
261
  };
250
262
 
251
263
  type ChatInputBarHandle = {
@@ -320,4 +332,4 @@ declare function resolveChatComposerSlashTrigger(nodes: ChatComposerNode[], sele
320
332
  end: number;
321
333
  } | null;
322
334
 
323
- export { type ChatComposerNode, type ChatComposerSelection, type ChatComposerTextNode, type ChatComposerTokenKind, type ChatComposerTokenNode, type ChatFileOperationBlockViewModel, type ChatFileOperationLineViewModel, type ChatInlineContentSegmentViewModel, type ChatInlineHint, type ChatInlineTokenViewModel, ChatInputBar, type ChatInputBarActionsProps, type ChatInputBarHandle, type ChatInputBarProps, type ChatInputBarToolbarProps, ChatMessageList, type ChatMessageListProps, type ChatMessagePartViewModel, type ChatMessageRole, type ChatMessageTexts, type ChatMessageViewModel, type ChatSelectedItem, type ChatSkillPickerOption, type ChatSkillPickerProps, type ChatSlashItem, type ChatSlashMenuProps, type ChatTexts, type ChatToolPartViewModel, type ChatToolbarAccessory, type ChatToolbarAccessoryIcon, type ChatToolbarIcon, type ChatToolbarSelect, type ChatToolbarSelectGroup, type ChatToolbarSelectOption, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
335
+ export { type ChatComposerNode, type ChatComposerSelection, type ChatComposerTextNode, type ChatComposerTokenKind, type ChatComposerTokenNode, type ChatFileOperationBlockViewModel, type ChatFileOperationLineViewModel, type ChatInlineContentSegmentViewModel, type ChatInlineHint, type ChatInlineTokenViewModel, ChatInputBar, type ChatInputBarActionsProps, type ChatInputBarHandle, type ChatInputBarProps, type ChatInputBarToolbarProps, ChatMessageList, type ChatMessageListProps, type ChatMessagePartViewModel, type ChatMessageRole, type ChatMessageTexts, type ChatMessageViewModel, type ChatSelectedItem, type ChatSkillPickerOption, type ChatSkillPickerProps, type ChatSlashItem, type ChatSlashMenuProps, type ChatTexts, type ChatToolActionViewModel, type ChatToolPartViewModel, type ChatToolbarAccessory, type ChatToolbarAccessoryIcon, type ChatToolbarIcon, type ChatToolbarSelect, type ChatToolbarSelectGroup, type ChatToolbarSelectOption, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
package/dist/index.js CHANGED
@@ -290,13 +290,20 @@ function ChatSlashMenu(props) {
290
290
  ] })
291
291
  }
292
292
  ),
293
- /* @__PURE__ */ jsx5("div", { className: "max-w-[320px] p-3.5", children: activeItem ? /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
293
+ /* @__PURE__ */ jsx5("div", { className: "min-w-0 p-2.5", children: activeItem ? /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
294
294
  /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
295
295
  /* @__PURE__ */ jsx5("span", { className: "inline-flex rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-semibold text-primary", children: activeItem.subtitle }),
296
296
  /* @__PURE__ */ jsx5("span", { className: "text-sm font-semibold text-gray-900", children: activeItem.title })
297
297
  ] }),
298
298
  /* @__PURE__ */ jsx5("p", { className: "text-xs leading-5 text-gray-600", children: activeItem.description }),
299
- /* @__PURE__ */ jsx5("div", { className: "space-y-1", children: activeItem.detailLines.map((line) => /* @__PURE__ */ jsx5("div", { className: "rounded-md bg-gray-50 px-2 py-1 text-[11px] text-gray-600", children: line }, line)) }),
299
+ /* @__PURE__ */ jsx5("div", { className: "space-y-1", children: activeItem.detailLines.map((line) => /* @__PURE__ */ jsx5(
300
+ "div",
301
+ {
302
+ className: "min-w-0 break-all rounded-md bg-gray-50 px-2 py-1 text-[11px] leading-5 text-gray-600",
303
+ children: line
304
+ },
305
+ line
306
+ )) }),
300
307
  /* @__PURE__ */ jsx5("div", { className: "pt-1 text-[11px] text-gray-500", children: texts.slashSkillHintLabel })
301
308
  ] }) : /* @__PURE__ */ jsx5("div", { className: "text-xs text-gray-500", children: texts.slashHintLabel }) })
302
309
  ] })
@@ -2774,7 +2781,7 @@ function ToolCardContent({ children, className }) {
2774
2781
  }
2775
2782
 
2776
2783
  // src/components/chat/ui/chat-message-list/tool-card/tool-card-header.tsx
2777
- import { ChevronDown as ChevronDown2, ChevronRight } from "lucide-react";
2784
+ import { ArrowUpRight, ChevronDown as ChevronDown2, ChevronRight } from "lucide-react";
2778
2785
 
2779
2786
  // src/components/chat/ui/chat-message-list/tool-card/tool-card-status.tsx
2780
2787
  import { Check as Check4, Loader2, AlertTriangle, Minus } from "lucide-react";
@@ -2796,12 +2803,38 @@ function ToolStatusLabel({ card }) {
2796
2803
 
2797
2804
  // src/components/chat/ui/chat-message-list/tool-card/tool-card-header.tsx
2798
2805
  import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
2806
+ function ToolCardSessionActionButton({
2807
+ action,
2808
+ onAction
2809
+ }) {
2810
+ const isChildSession = action.sessionKind === "child";
2811
+ const label = isChildSession ? "Open child session" : "Open session";
2812
+ const handleClick = (event) => {
2813
+ event.stopPropagation();
2814
+ onAction(action);
2815
+ };
2816
+ return /* @__PURE__ */ jsx20(
2817
+ "button",
2818
+ {
2819
+ type: "button",
2820
+ onClick: handleClick,
2821
+ className: cn(
2822
+ "inline-flex items-center gap-1.5 rounded-full border px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.08em] transition-colors",
2823
+ "border-amber-200/80 bg-white/80 text-amber-800 hover:bg-amber-50"
2824
+ ),
2825
+ "aria-label": label,
2826
+ title: label,
2827
+ children: /* @__PURE__ */ jsx20(ArrowUpRight, { className: "h-3 w-3", strokeWidth: 2.5 })
2828
+ }
2829
+ );
2830
+ }
2799
2831
  function ToolCardHeader({
2800
2832
  card,
2801
2833
  icon: Icon2,
2802
2834
  expanded,
2803
2835
  canExpand,
2804
2836
  hideSummary = false,
2837
+ actionSlot,
2805
2838
  onToggle
2806
2839
  }) {
2807
2840
  const summaryPart = hideSummary ? "" : card.summary?.replace(/^(command|path|args|query|input):\s*/i, "") ?? "";
@@ -2825,6 +2858,7 @@ function ToolCardHeader({
2825
2858
  ] })
2826
2859
  ] }),
2827
2860
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-3 shrink-0", children: [
2861
+ actionSlot,
2828
2862
  /* @__PURE__ */ jsx20(ToolStatusLabel, { card }),
2829
2863
  canExpand && (expanded ? /* @__PURE__ */ jsx20(ChevronDown2, { className: "h-4 w-4 text-amber-400/80", strokeWidth: 3 }) : /* @__PURE__ */ jsx20(ChevronRight, { className: "h-4 w-4 text-amber-400/80", strokeWidth: 3 }))
2830
2864
  ] })
@@ -2832,6 +2866,12 @@ function ToolCardHeader({
2832
2866
  }
2833
2867
  );
2834
2868
  }
2869
+ function ToolCardHeaderSessionAction({
2870
+ action,
2871
+ onAction
2872
+ }) {
2873
+ return /* @__PURE__ */ jsx20(ToolCardSessionActionButton, { action, onAction });
2874
+ }
2835
2875
 
2836
2876
  // src/components/chat/hooks/use-sticky-bottom-scroll.ts
2837
2877
  import { useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useRef as useRef6 } from "react";
@@ -3497,7 +3537,11 @@ function SearchSnippetView({ card }) {
3497
3537
  expanded && output && /* @__PURE__ */ jsx23(ToolCardContent, { children: /* @__PURE__ */ jsx23("pre", { className: "font-mono text-[12px] text-amber-950/70 whitespace-pre-wrap break-all w-full max-w-full max-h-64 overflow-y-auto overflow-x-hidden min-w-0 custom-scrollbar-amber leading-relaxed", children: output }) })
3498
3538
  ] });
3499
3539
  }
3500
- function GenericToolCard({ card }) {
3540
+ function GenericToolCard({
3541
+ card,
3542
+ onToolAction,
3543
+ renderToolAgent
3544
+ }) {
3501
3545
  const input = card.input?.trim() ?? "";
3502
3546
  const output = card.output?.trim() ?? "";
3503
3547
  const isRunning = card.statusTone === "running";
@@ -3519,6 +3563,16 @@ function GenericToolCard({ card }) {
3519
3563
  icon: Globe,
3520
3564
  expanded,
3521
3565
  canExpand: hasContent || isRunning,
3566
+ actionSlot: card.agentId || card.action && onToolAction ? /* @__PURE__ */ jsxs15(Fragment5, { children: [
3567
+ card.agentId && renderToolAgent ? renderToolAgent(card.agentId) : null,
3568
+ card.action && onToolAction ? /* @__PURE__ */ jsx23(
3569
+ ToolCardHeaderSessionAction,
3570
+ {
3571
+ action: card.action,
3572
+ onAction: onToolAction
3573
+ }
3574
+ ) : null
3575
+ ] }) : void 0,
3522
3576
  onToggle
3523
3577
  }
3524
3578
  ),
@@ -3551,7 +3605,11 @@ function isSearchTool(name) {
3551
3605
  const lowered = name.toLowerCase();
3552
3606
  return lowered === "grep_search" || lowered === "find_files" || lowered.includes("search");
3553
3607
  }
3554
- function ChatToolCard({ card }) {
3608
+ function ChatToolCard({
3609
+ card,
3610
+ onToolAction,
3611
+ renderToolAgent
3612
+ }) {
3555
3613
  if (isTerminalTool(card.toolName)) {
3556
3614
  return /* @__PURE__ */ jsx24(TerminalExecutionView, { card });
3557
3615
  }
@@ -3561,7 +3619,14 @@ function ChatToolCard({ card }) {
3561
3619
  if (isSearchTool(card.toolName)) {
3562
3620
  return /* @__PURE__ */ jsx24(SearchSnippetView, { card });
3563
3621
  }
3564
- return /* @__PURE__ */ jsx24(GenericToolCard, { card });
3622
+ return /* @__PURE__ */ jsx24(
3623
+ GenericToolCard,
3624
+ {
3625
+ card,
3626
+ onToolAction,
3627
+ renderToolAgent
3628
+ }
3629
+ );
3565
3630
  }
3566
3631
 
3567
3632
  // src/components/chat/ui/chat-message-list/chat-unknown-part.tsx
@@ -3580,7 +3645,7 @@ function ChatUnknownPart(props) {
3580
3645
  // src/components/chat/ui/chat-message-list/chat-message.tsx
3581
3646
  import { jsx as jsx26 } from "react/jsx-runtime";
3582
3647
  var ChatMessage = memo(function ChatMessage2(props) {
3583
- const { message, texts } = props;
3648
+ const { message, texts, onToolAction, renderToolAgent } = props;
3584
3649
  const { role } = message;
3585
3650
  const isUser = role === "user";
3586
3651
  const isMessageInProgress = message.status === "pending" || message.status === "streaming";
@@ -3628,7 +3693,14 @@ var ChatMessage = memo(function ChatMessage2(props) {
3628
3693
  );
3629
3694
  }
3630
3695
  if (type === "tool-card") {
3631
- return /* @__PURE__ */ jsx26("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx26(ChatToolCard, { card: part.card }) }, `tool-${index}`);
3696
+ return /* @__PURE__ */ jsx26("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx26(
3697
+ ChatToolCard,
3698
+ {
3699
+ card: part.card,
3700
+ onToolAction,
3701
+ renderToolAgent
3702
+ }
3703
+ ) }, `tool-${index}`);
3632
3704
  }
3633
3705
  if (type === "file") {
3634
3706
  return /* @__PURE__ */ jsx26(
@@ -3743,7 +3815,15 @@ function ChatMessageList(props) {
3743
3815
  return /* @__PURE__ */ jsxs18("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
3744
3816
  !isUser ? /* @__PURE__ */ jsx28(ChatMessageAvatar, { role: message.role }) : null,
3745
3817
  /* @__PURE__ */ jsxs18("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
3746
- /* @__PURE__ */ jsx28(ChatMessage, { message, texts: props.texts }),
3818
+ /* @__PURE__ */ jsx28(
3819
+ ChatMessage,
3820
+ {
3821
+ message,
3822
+ texts: props.texts,
3823
+ onToolAction: props.onToolAction,
3824
+ renderToolAgent: props.renderToolAgent
3825
+ }
3826
+ ),
3747
3827
  /* @__PURE__ */ jsx28("div", { className: cn("flex items-center gap-2", isUser && "justify-end"), children: isGenerating ? /* @__PURE__ */ jsx28(ChatMessageTypingFooter, {}) : /* @__PURE__ */ jsxs18(Fragment6, { children: [
3748
3828
  /* @__PURE__ */ jsx28(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser }),
3749
3829
  !isUser ? /* @__PURE__ */ jsx28(ChatMessageActionCopy, { message, texts: props.texts }) : null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/agent-chat-ui",
3
- "version": "0.2.20",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Reusable Nextclaw agent chat UI primitives and default skin.",
6
6
  "type": "module",