@nextclaw/agent-chat-ui 0.2.17 → 0.2.19

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
@@ -151,17 +151,38 @@ type ChatInputBarProps = {
151
151
  toolbar: ChatInputBarToolbarProps;
152
152
  };
153
153
  type ChatMessageRole = "user" | "assistant" | "tool" | "system" | "message";
154
+ type ChatFileOperationLineViewModel = {
155
+ kind: "context" | "add" | "remove";
156
+ text: string;
157
+ oldLineNumber?: number;
158
+ newLineNumber?: number;
159
+ };
160
+ type ChatFileOperationBlockViewModel = {
161
+ key: string;
162
+ path: string;
163
+ display?: "preview" | "diff";
164
+ caption?: string;
165
+ lines: ChatFileOperationLineViewModel[];
166
+ rawText?: string;
167
+ truncated?: boolean;
168
+ };
154
169
  type ChatToolPartViewModel = {
155
170
  kind: "call" | "result";
156
171
  toolName: string;
157
172
  summary?: string;
173
+ inputLabel?: string;
174
+ input?: string;
158
175
  output?: string;
176
+ outputData?: unknown;
159
177
  hasResult: boolean;
160
178
  statusTone: "running" | "success" | "error" | "cancelled";
161
179
  statusLabel: string;
162
180
  titleLabel: string;
163
181
  outputLabel: string;
164
182
  emptyLabel: string;
183
+ fileOperation?: {
184
+ blocks: ChatFileOperationBlockViewModel[];
185
+ };
165
186
  };
166
187
  type ChatInlineTokenViewModel = {
167
188
  kind: string;
@@ -299,4 +320,4 @@ declare function resolveChatComposerSlashTrigger(nodes: ChatComposerNode[], sele
299
320
  end: number;
300
321
  } | null;
301
322
 
302
- export { type ChatComposerNode, type ChatComposerSelection, type ChatComposerTextNode, type ChatComposerTokenKind, type ChatComposerTokenNode, 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 };
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 };
package/dist/index.js CHANGED
@@ -2314,8 +2314,8 @@ function ChatInlineTokenBadge({
2314
2314
  "span",
2315
2315
  {
2316
2316
  className: cn(
2317
- "mx-[2px] inline-flex h-7 max-w-full items-center gap-1.5 rounded-lg border px-2 align-baseline text-[11px] font-medium",
2318
- isSkill ? isUser ? "border-white/25 bg-white/16 text-white" : "border-primary/12 bg-primary/8 text-primary" : isUser ? "border-white/20 bg-white/12 text-white" : "border-slate-200/80 bg-slate-50 text-slate-700"
2317
+ "mx-[2px] inline-flex h-7 max-w-full items-center gap-1.5 rounded-xl border px-2.5 align-baseline text-[11px] font-medium shadow-[0_0_0_1px_rgba(15,23,42,0.03)]",
2318
+ isSkill ? isUser ? "border-emerald-200/35 bg-emerald-400/18 text-emerald-50/95" : "border-emerald-200/70 bg-emerald-50 text-emerald-700" : isUser ? "border-white/30 bg-white/18 text-white" : "border-slate-200/80 bg-slate-100 text-slate-700"
2319
2319
  ),
2320
2320
  title: label,
2321
2321
  children: [
@@ -2324,7 +2324,7 @@ function ChatInlineTokenBadge({
2324
2324
  {
2325
2325
  className: cn(
2326
2326
  "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center",
2327
- isSkill ? isUser ? "text-white/75" : "text-primary/70" : isUser ? "text-white/70" : "text-slate-500"
2327
+ isSkill ? isUser ? "text-emerald-100/90" : "text-emerald-600" : isUser ? "text-white/70" : "text-slate-500"
2328
2328
  ),
2329
2329
  children: /* @__PURE__ */ jsx15(ChatInlineSkillIcon, {})
2330
2330
  }
@@ -2801,9 +2801,10 @@ function ToolCardHeader({
2801
2801
  icon: Icon2,
2802
2802
  expanded,
2803
2803
  canExpand,
2804
+ hideSummary = false,
2804
2805
  onToggle
2805
2806
  }) {
2806
- const summaryPart = card.summary?.replace(/^(command|path|args|query|input):\s*/i, "");
2807
+ const summaryPart = hideSummary ? "" : card.summary?.replace(/^(command|path|args|query|input):\s*/i, "") ?? "";
2807
2808
  return /* @__PURE__ */ jsxs12(
2808
2809
  "div",
2809
2810
  {
@@ -2832,56 +2833,379 @@ function ToolCardHeader({
2832
2833
  );
2833
2834
  }
2834
2835
 
2835
- // src/components/chat/ui/chat-message-list/tool-card/tool-card-views.tsx
2836
- import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
2837
- function TerminalExecutionView({ card }) {
2836
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-file-operation.tsx
2837
+ import { Fragment as Fragment4 } from "react";
2838
+ import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
2839
+ function formatLineNumber(value) {
2840
+ return typeof value === "number" ? String(value) : "";
2841
+ }
2842
+ function readVisibleLineNumber(line) {
2843
+ return formatLineNumber(line.newLineNumber ?? line.oldLineNumber);
2844
+ }
2845
+ function isPreviewBlock(block) {
2846
+ return block.display === "preview";
2847
+ }
2848
+ function getCaptionTone(part) {
2849
+ if (/^\+\d+$/.test(part)) {
2850
+ return "text-emerald-700";
2851
+ }
2852
+ if (/^-\d+$/.test(part)) {
2853
+ return "text-rose-700";
2854
+ }
2855
+ return "text-stone-500";
2856
+ }
2857
+ function renderCaption(caption) {
2858
+ const parts = caption.split("\xB7").map((part) => part.trim()).filter((part) => /^\+\d+$/.test(part) || /^-\d+$/.test(part));
2859
+ if (parts.length === 0) {
2860
+ return null;
2861
+ }
2862
+ return /* @__PURE__ */ jsx21("div", { className: "flex shrink-0 items-center gap-x-1 whitespace-nowrap text-[10px] font-medium uppercase tracking-[0.08em]", children: parts.map((part, index) => /* @__PURE__ */ jsxs13(Fragment4, { children: [
2863
+ index > 0 ? /* @__PURE__ */ jsx21("span", { className: "text-stone-300", children: "\xB7" }) : null,
2864
+ /* @__PURE__ */ jsx21("span", { className: cn(getCaptionTone(part)), children: part })
2865
+ ] }, `${part}-${index}`)) });
2866
+ }
2867
+ function renderDiffGutterRow(line, index) {
2868
+ const gutterTone = line.kind === "add" ? "border-r border-emerald-200 bg-emerald-100 text-emerald-700" : line.kind === "remove" ? "border-r border-rose-200 bg-rose-100 text-rose-700" : "border-r border-stone-200 bg-stone-100 text-stone-500";
2869
+ return /* @__PURE__ */ jsx21(
2870
+ "div",
2871
+ {
2872
+ className: "font-mono text-[11px] leading-relaxed",
2873
+ children: /* @__PURE__ */ jsx21("span", { className: cn("flex h-full w-11 items-center justify-center px-1 py-1 tabular-nums", gutterTone), children: readVisibleLineNumber(line) })
2874
+ },
2875
+ `diff-gutter-${index}-${line.oldLineNumber ?? "x"}-${line.newLineNumber ?? "x"}`
2876
+ );
2877
+ }
2878
+ function renderPreviewGutterRow(line, index) {
2879
+ return /* @__PURE__ */ jsx21(
2880
+ "div",
2881
+ {
2882
+ className: "font-mono text-[11px] leading-relaxed",
2883
+ children: /* @__PURE__ */ jsx21("span", { className: "flex h-full w-11 items-center justify-center border-r border-stone-200 bg-stone-100 px-1 py-1.5 tabular-nums text-stone-500", children: readVisibleLineNumber(line) })
2884
+ },
2885
+ `preview-gutter-${index}-${line.oldLineNumber ?? "x"}-${line.newLineNumber ?? "x"}`
2886
+ );
2887
+ }
2888
+ function renderDiffCodeRow(line, index) {
2889
+ const rowTone = line.kind === "add" ? "border-l-2 border-emerald-300 bg-emerald-50 text-emerald-950" : line.kind === "remove" ? "border-l-2 border-rose-300 bg-rose-50 text-rose-950" : "border-l-2 border-transparent text-amber-950/80";
2890
+ return /* @__PURE__ */ jsx21(
2891
+ "div",
2892
+ {
2893
+ className: cn(
2894
+ "min-w-full whitespace-pre px-3 py-1 font-mono text-[11px] leading-relaxed",
2895
+ rowTone
2896
+ ),
2897
+ children: line.text || " "
2898
+ },
2899
+ `diff-code-${index}-${line.oldLineNumber ?? "x"}-${line.newLineNumber ?? "x"}`
2900
+ );
2901
+ }
2902
+ function renderPreviewCodeRow(line, index) {
2903
+ return /* @__PURE__ */ jsx21(
2904
+ "div",
2905
+ {
2906
+ className: "min-w-full whitespace-pre px-3 py-1.5 font-mono text-[11px] leading-relaxed text-amber-950/85",
2907
+ children: line.text || " "
2908
+ },
2909
+ `preview-code-${index}-${line.oldLineNumber ?? "x"}-${line.newLineNumber ?? "x"}`
2910
+ );
2911
+ }
2912
+ function FileOperationBlock({
2913
+ block,
2914
+ showPathRow,
2915
+ isFirst
2916
+ }) {
2917
+ const { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } = ChatUiPrimitives;
2918
+ const previewBlock = isPreviewBlock(block);
2919
+ const showMetaRow = showPathRow || Boolean(block.caption);
2920
+ return /* @__PURE__ */ jsxs13("section", { className: cn("overflow-hidden bg-white", !isFirst && "border-t border-stone-200/80"), children: [
2921
+ showMetaRow ? /* @__PURE__ */ jsxs13(
2922
+ "div",
2923
+ {
2924
+ className: cn(
2925
+ "flex items-center justify-between gap-4 border-b border-stone-200/80 px-4 text-stone-700",
2926
+ showPathRow ? "py-3" : "py-2"
2927
+ ),
2928
+ children: [
2929
+ /* @__PURE__ */ jsx21("div", { className: "min-w-0 flex-1", children: showPathRow ? /* @__PURE__ */ jsx21(TooltipProvider, { delayDuration: 250, children: /* @__PURE__ */ jsxs13(Tooltip, { children: [
2930
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx21(
2931
+ "div",
2932
+ {
2933
+ className: "truncate whitespace-nowrap font-mono text-[12px] font-medium text-stone-700",
2934
+ title: block.path,
2935
+ children: block.path
2936
+ }
2937
+ ) }),
2938
+ /* @__PURE__ */ jsx21(TooltipContent, { side: "top", className: "max-w-[420px] text-xs font-mono break-all", children: block.path })
2939
+ ] }) }) : null }),
2940
+ block.caption ? renderCaption(block.caption) : null
2941
+ ]
2942
+ }
2943
+ ) : null,
2944
+ block.lines.length > 0 ? /* @__PURE__ */ jsx21("div", { className: "max-h-72 overflow-auto bg-white custom-scrollbar-amber", children: /* @__PURE__ */ jsxs13(
2945
+ "div",
2946
+ {
2947
+ className: cn(
2948
+ "grid min-w-0 max-w-full",
2949
+ "grid-cols-[2.75rem_minmax(0,1fr)]"
2950
+ ),
2951
+ children: [
2952
+ /* @__PURE__ */ jsx21("div", { className: "overflow-hidden", children: block.lines.map(previewBlock ? renderPreviewGutterRow : renderDiffGutterRow) }),
2953
+ /* @__PURE__ */ jsx21("div", { className: "overflow-x-auto custom-scrollbar-amber bg-white", children: /* @__PURE__ */ jsx21("div", { className: "min-w-max", children: block.lines.map(previewBlock ? renderPreviewCodeRow : renderDiffCodeRow) }) })
2954
+ ]
2955
+ }
2956
+ ) }) : block.rawText ? /* @__PURE__ */ jsx21("pre", { className: "max-h-72 min-w-full w-max overflow-auto bg-white px-4 py-3 font-mono text-[11px] leading-relaxed text-amber-950/80 whitespace-pre custom-scrollbar-amber", children: block.rawText }) : null,
2957
+ block.truncated && !previewBlock ? /* @__PURE__ */ jsx21("div", { className: "border-t border-stone-200/85 bg-stone-50 px-4 py-2 text-[10px] text-stone-500", children: "Showing a shortened diff preview." }) : null
2958
+ ] });
2959
+ }
2960
+ function ToolCardFileOperationContent({
2961
+ card,
2962
+ className
2963
+ }) {
2964
+ const blocks = card.fileOperation?.blocks ?? [];
2838
2965
  const output = card.output?.trim() ?? "";
2839
- const isRunning = card.statusTone === "running";
2840
- const hasContent = !!(card.summary?.trim() || output.length > 0);
2841
- const wasEmptyRef = useRef6(!hasContent);
2842
- const [expanded, setExpanded] = useState7(hasContent && (isRunning || card.statusTone === "error"));
2966
+ if (blocks.length === 0 && !output) {
2967
+ return null;
2968
+ }
2969
+ return /* @__PURE__ */ jsxs13("div", { className: cn("overflow-hidden bg-white", className), children: [
2970
+ blocks.map((block, index) => {
2971
+ return /* @__PURE__ */ jsx21(
2972
+ FileOperationBlock,
2973
+ {
2974
+ block,
2975
+ showPathRow: true,
2976
+ isFirst: index === 0
2977
+ },
2978
+ block.key
2979
+ );
2980
+ }),
2981
+ output ? /* @__PURE__ */ jsx21("pre", { className: cn(
2982
+ "max-h-56 min-w-full w-max overflow-auto bg-white px-4 py-3 font-mono text-[11px] leading-relaxed text-amber-950/80 whitespace-pre custom-scrollbar-amber",
2983
+ blocks.length > 0 && "border-t border-stone-200/80"
2984
+ ), children: output }) : null
2985
+ ] });
2986
+ }
2987
+
2988
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-views.tsx
2989
+ import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
2990
+ var TOOL_CARD_AUTO_EXPAND_DELAY_MS = 200;
2991
+ var ANSI_ESCAPE_PREFIX = String.fromCharCode(27);
2992
+ var ANSI_ESCAPE_PATTERN = new RegExp(`${ANSI_ESCAPE_PREFIX}\\[[0-?]*[ -/]*[@-~]`, "g");
2993
+ function readNonEmptyString(value) {
2994
+ if (typeof value !== "string") {
2995
+ return null;
2996
+ }
2997
+ return value.length > 0 ? value : null;
2998
+ }
2999
+ function isRecord(value) {
3000
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3001
+ }
3002
+ function stripAnsi(value) {
3003
+ return value.replace(ANSI_ESCAPE_PATTERN, "");
3004
+ }
3005
+ function isStructuredTerminalRecord(record) {
3006
+ return "command" in record || "workingDir" in record || "exitCode" in record || "durationMs" in record || "stdout" in record || "stderr" in record || "aggregated_output" in record || "combinedOutput" in record;
3007
+ }
3008
+ function extractTerminalOutputFromRecord(record) {
3009
+ const aggregatedOutput = readNonEmptyString(record.aggregated_output) ?? readNonEmptyString(record.combinedOutput) ?? readNonEmptyString(record.output);
3010
+ if (aggregatedOutput) {
3011
+ return aggregatedOutput;
3012
+ }
3013
+ const stdout = readNonEmptyString(record.stdout);
3014
+ const stderr = readNonEmptyString(record.stderr);
3015
+ if (!stdout && !stderr) {
3016
+ return null;
3017
+ }
3018
+ return [stdout, stderr].filter((value) => Boolean(value)).join("\n");
3019
+ }
3020
+ function normalizeTerminalOutput(rawOutput, structuredOutput) {
3021
+ if (isRecord(structuredOutput)) {
3022
+ const terminalOutput = extractTerminalOutputFromRecord(structuredOutput);
3023
+ if (terminalOutput) {
3024
+ return stripAnsi(terminalOutput);
3025
+ }
3026
+ if (isStructuredTerminalRecord(structuredOutput)) {
3027
+ return "";
3028
+ }
3029
+ }
3030
+ if (!rawOutput) {
3031
+ return "";
3032
+ }
3033
+ const trimmed = rawOutput.trim();
3034
+ if (!trimmed.startsWith("{")) {
3035
+ return stripAnsi(rawOutput);
3036
+ }
3037
+ try {
3038
+ const parsed = JSON.parse(trimmed);
3039
+ if (!isRecord(parsed)) {
3040
+ return stripAnsi(rawOutput);
3041
+ }
3042
+ const terminalOutput = extractTerminalOutputFromRecord(parsed);
3043
+ if (terminalOutput) {
3044
+ return stripAnsi(terminalOutput);
3045
+ }
3046
+ if (isStructuredTerminalRecord(parsed)) {
3047
+ return "";
3048
+ }
3049
+ return stripAnsi(rawOutput);
3050
+ } catch {
3051
+ return stripAnsi(rawOutput);
3052
+ }
3053
+ }
3054
+ function useToolCardExpandedState({
3055
+ canExpand,
3056
+ isRunning,
3057
+ autoExpandWhileRunning = true,
3058
+ expandOnError = false,
3059
+ statusTone
3060
+ }) {
3061
+ const [expanded, setExpanded] = useState7(false);
2843
3062
  const [hasUserToggled, setHasUserToggled] = useState7(false);
3063
+ const expandTimerRef = useRef6(null);
2844
3064
  const prevRunningRef = useRef6(isRunning);
3065
+ const isFirstRenderRef = useRef6(true);
3066
+ useEffect7(() => {
3067
+ return () => {
3068
+ if (expandTimerRef.current !== null) {
3069
+ window.clearTimeout(expandTimerRef.current);
3070
+ }
3071
+ };
3072
+ }, []);
2845
3073
  useEffect7(() => {
2846
- if (wasEmptyRef.current && hasContent && isRunning) {
3074
+ if (expandOnError && statusTone === "error" && canExpand && !hasUserToggled) {
3075
+ if (expandTimerRef.current !== null) {
3076
+ window.clearTimeout(expandTimerRef.current);
3077
+ expandTimerRef.current = null;
3078
+ }
2847
3079
  setExpanded(true);
2848
- wasEmptyRef.current = false;
3080
+ prevRunningRef.current = isRunning;
3081
+ isFirstRenderRef.current = false;
3082
+ return;
2849
3083
  }
2850
- }, [hasContent, isRunning]);
2851
- useEffect7(() => {
2852
- if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2853
- setExpanded(false);
3084
+ if (autoExpandWhileRunning && isRunning && canExpand && !hasUserToggled && !expanded && (isFirstRenderRef.current || !prevRunningRef.current)) {
3085
+ expandTimerRef.current = window.setTimeout(() => {
3086
+ setExpanded(true);
3087
+ expandTimerRef.current = null;
3088
+ }, TOOL_CARD_AUTO_EXPAND_DELAY_MS);
3089
+ }
3090
+ if (!isRunning) {
3091
+ if (expandTimerRef.current !== null) {
3092
+ window.clearTimeout(expandTimerRef.current);
3093
+ expandTimerRef.current = null;
3094
+ }
3095
+ if (prevRunningRef.current && !hasUserToggled) {
3096
+ setExpanded(false);
3097
+ }
2854
3098
  }
2855
3099
  prevRunningRef.current = isRunning;
2856
- }, [isRunning, hasUserToggled]);
3100
+ isFirstRenderRef.current = false;
3101
+ }, [autoExpandWhileRunning, canExpand, expandOnError, expanded, hasUserToggled, isRunning, statusTone]);
2857
3102
  const onToggle = () => {
2858
- if (!output && !isRunning) return;
2859
- setExpanded(!expanded);
3103
+ if (!canExpand) {
3104
+ return;
3105
+ }
3106
+ if (expandTimerRef.current !== null) {
3107
+ window.clearTimeout(expandTimerRef.current);
3108
+ expandTimerRef.current = null;
3109
+ }
3110
+ setExpanded((current) => !current);
2860
3111
  setHasUserToggled(true);
2861
3112
  };
3113
+ return { expanded, onToggle };
3114
+ }
3115
+ function GenericToolSection({
3116
+ label,
3117
+ tone,
3118
+ children
3119
+ }) {
3120
+ const tones = {
3121
+ input: {
3122
+ shell: "border-stone-200/80 bg-stone-50/90",
3123
+ header: "border-stone-200/80 bg-stone-100/85 text-stone-500",
3124
+ dot: "bg-stone-400/80",
3125
+ body: "text-stone-700"
3126
+ },
3127
+ output: {
3128
+ shell: "border-amber-200/70 bg-white/90",
3129
+ header: "border-amber-200/70 bg-amber-50/90 text-amber-700",
3130
+ dot: "bg-amber-500/80",
3131
+ body: "text-amber-950/80"
3132
+ },
3133
+ error: {
3134
+ shell: "border-rose-200/80 bg-rose-50/85",
3135
+ header: "border-rose-200/80 bg-rose-100/80 text-rose-700",
3136
+ dot: "bg-rose-500/80",
3137
+ body: "text-rose-950/85"
3138
+ }
3139
+ };
3140
+ const style = tones[tone];
3141
+ return /* @__PURE__ */ jsxs14(
3142
+ "section",
3143
+ {
3144
+ className: cn(
3145
+ "overflow-hidden rounded-md border shadow-[inset_0_1px_0_rgba(255,255,255,0.45)]",
3146
+ style.shell
3147
+ ),
3148
+ children: [
3149
+ /* @__PURE__ */ jsxs14(
3150
+ "div",
3151
+ {
3152
+ className: cn(
3153
+ "flex items-center gap-2 border-b px-3 py-2 text-[10px] font-semibold uppercase tracking-[0.16em]",
3154
+ style.header
3155
+ ),
3156
+ children: [
3157
+ /* @__PURE__ */ jsx22("span", { className: cn("h-1.5 w-1.5 rounded-full", style.dot) }),
3158
+ /* @__PURE__ */ jsx22("span", { children: label })
3159
+ ]
3160
+ }
3161
+ ),
3162
+ /* @__PURE__ */ jsx22("div", { className: "w-full overflow-hidden", children: /* @__PURE__ */ jsx22(
3163
+ "pre",
3164
+ {
3165
+ className: cn(
3166
+ "w-full max-w-full min-w-0 max-h-64 overflow-x-auto overflow-y-auto px-3 py-2.5 font-mono text-[12px] leading-relaxed whitespace-pre custom-scrollbar-amber",
3167
+ style.body
3168
+ ),
3169
+ children
3170
+ }
3171
+ ) })
3172
+ ]
3173
+ }
3174
+ );
3175
+ }
3176
+ function TerminalExecutionView({ card }) {
3177
+ const output = normalizeTerminalOutput(card.output, card.outputData);
3178
+ const isRunning = card.statusTone === "running";
3179
+ const hasContent = !!(card.summary?.trim() || output.trim().length > 0);
3180
+ const { expanded, onToggle } = useToolCardExpandedState({
3181
+ canExpand: output.trim().length > 0 || isRunning,
3182
+ isRunning,
3183
+ expandOnError: hasContent,
3184
+ statusTone: card.statusTone
3185
+ });
2862
3186
  const commandPart = card.summary?.replace(/^(command|path|args|query|input):\s*/i, "");
2863
- return /* @__PURE__ */ jsxs13(ToolCardRoot, { children: [
2864
- /* @__PURE__ */ jsx21(
3187
+ return /* @__PURE__ */ jsxs14(ToolCardRoot, { children: [
3188
+ /* @__PURE__ */ jsx22(
2865
3189
  ToolCardHeader,
2866
3190
  {
2867
3191
  card,
2868
3192
  icon: Terminal,
2869
3193
  expanded,
2870
- canExpand: !!output || isRunning,
3194
+ canExpand: output.trim().length > 0 || isRunning,
2871
3195
  onToggle
2872
3196
  }
2873
3197
  ),
2874
- expanded && /* @__PURE__ */ jsxs13(Fragment4, { children: [
2875
- /* @__PURE__ */ jsx21("div", { className: "px-3 pb-2 font-mono w-full max-h-48 overflow-y-auto custom-scrollbar-amber min-h-0 text-[12px]", children: /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-2 leading-relaxed", children: [
2876
- /* @__PURE__ */ jsx21("span", { className: "text-amber-500/50 font-medium shrink-0 select-none mt-[1px]", children: "$" }),
2877
- /* @__PURE__ */ jsx21("div", { className: "flex-1 min-w-0", children: commandPart ? /* @__PURE__ */ jsxs13("div", { className: "text-amber-950/80 break-words whitespace-pre-wrap tracking-tight font-medium inline-block", children: [
3198
+ expanded && /* @__PURE__ */ jsxs14(Fragment5, { children: [
3199
+ /* @__PURE__ */ jsx22("div", { className: "px-3 pb-2 font-mono w-full max-h-48 overflow-y-auto custom-scrollbar-amber min-h-0 text-[12px]", children: /* @__PURE__ */ jsxs14("div", { className: "flex items-start gap-2 leading-relaxed", children: [
3200
+ /* @__PURE__ */ jsx22("span", { className: "text-amber-500/50 font-medium shrink-0 select-none mt-[1px]", children: "$" }),
3201
+ /* @__PURE__ */ jsx22("div", { className: "flex-1 min-w-0", children: commandPart ? /* @__PURE__ */ jsxs14("div", { className: "text-amber-950/80 break-words whitespace-pre-wrap tracking-tight font-medium inline-block", children: [
2878
3202
  commandPart,
2879
- isRunning && !output && /* @__PURE__ */ jsx21("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })
2880
- ] }) : /* @__PURE__ */ jsx21("div", { className: "h-3 w-32 bg-amber-200/30 rounded animate-pulse mt-2" }) })
3203
+ isRunning && !output && /* @__PURE__ */ jsx22("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })
3204
+ ] }) : /* @__PURE__ */ jsx22("div", { className: "h-3 w-32 bg-amber-200/30 rounded animate-pulse mt-2" }) })
2881
3205
  ] }) }),
2882
- output && /* @__PURE__ */ jsx21(ToolCardContent, { children: /* @__PURE__ */ jsxs13("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 px-0", children: [
3206
+ output && /* @__PURE__ */ jsx22(ToolCardContent, { children: /* @__PURE__ */ jsxs14("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 px-0", children: [
2883
3207
  output,
2884
- isRunning && /* @__PURE__ */ jsx21("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })
3208
+ isRunning && /* @__PURE__ */ jsx22("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })
2885
3209
  ] }) })
2886
3210
  ] })
2887
3211
  ] });
@@ -2889,75 +3213,50 @@ function TerminalExecutionView({ card }) {
2889
3213
  function FileOperationView({ card }) {
2890
3214
  const output = card.output?.trim() ?? "";
2891
3215
  const isRunning = card.statusTone === "running";
2892
- const [expanded, setExpanded] = useState7(false);
2893
- const [hasUserToggled, setHasUserToggled] = useState7(false);
2894
- const prevRunningRef = useRef6(isRunning);
2895
- useEffect7(() => {
2896
- if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2897
- setExpanded(false);
2898
- }
2899
- prevRunningRef.current = isRunning;
2900
- }, [isRunning, hasUserToggled]);
2901
- const onToggle = () => {
2902
- if (!output && !isRunning) return;
2903
- setExpanded(!expanded);
2904
- setHasUserToggled(true);
2905
- };
2906
- const isEdit = card.toolName === "edit_file" || card.toolName === "write_file";
2907
- const renderLine = (line, idx) => {
2908
- if (isEdit) {
2909
- if (line.startsWith("+") && !line.startsWith("+++")) {
2910
- return /* @__PURE__ */ jsxs13("div", { className: "bg-emerald-500/10 text-emerald-700 px-2 py-0.5 w-full break-all whitespace-pre-wrap", children: [
2911
- /* @__PURE__ */ jsx21("span", { className: "select-none opacity-40 mr-2 w-3 inline-block shrink-0", children: "+" }),
2912
- /* @__PURE__ */ jsx21("span", { children: line.slice(1) })
2913
- ] }, idx);
2914
- }
2915
- if (line.startsWith("-") && !line.startsWith("---")) {
2916
- return /* @__PURE__ */ jsxs13("div", { className: "bg-rose-500/10 text-rose-700 px-2 py-0.5 w-full break-all whitespace-pre-wrap", children: [
2917
- /* @__PURE__ */ jsx21("span", { className: "select-none opacity-40 mr-2 w-3 inline-block shrink-0", children: "-" }),
2918
- /* @__PURE__ */ jsx21("span", { className: "line-through decoration-rose-400/50", children: line.slice(1) })
2919
- ] }, idx);
2920
- }
2921
- }
2922
- return /* @__PURE__ */ jsx21("div", { className: "py-0.5 text-amber-950/80 w-full break-all whitespace-pre-wrap", children: /* @__PURE__ */ jsx21("span", { children: line }) }, idx);
2923
- };
2924
- const lines = output.split("\n");
2925
- const maxLines = 15;
2926
- const isLong = lines.length > maxLines;
2927
- const displayLines = !expanded && isLong ? lines.slice(0, maxLines) : lines;
2928
- return /* @__PURE__ */ jsxs13(ToolCardRoot, { children: [
2929
- /* @__PURE__ */ jsx21(
3216
+ const hasStructuredPreview = Boolean(card.fileOperation?.blocks.length);
3217
+ const hasContent = hasStructuredPreview || Boolean(output);
3218
+ const previewBlocks = card.fileOperation?.blocks ?? [];
3219
+ const previewLineCount = previewBlocks.reduce((count, block) => count + block.lines.length, 0);
3220
+ const previewCharCount = previewBlocks.reduce((count, block) => {
3221
+ if (block.rawText) {
3222
+ return count + block.rawText.length;
3223
+ }
3224
+ return count + block.lines.reduce((lineCount, line) => lineCount + line.text.length + 1, 0);
3225
+ }, 0);
3226
+ const shouldAutoExpandWhileRunning = !(isRunning && card.toolName === "write_file" && (previewLineCount > 24 || previewCharCount > 1200));
3227
+ const { expanded, onToggle } = useToolCardExpandedState({
3228
+ canExpand: hasContent || isRunning,
3229
+ isRunning,
3230
+ autoExpandWhileRunning: shouldAutoExpandWhileRunning,
3231
+ expandOnError: hasContent,
3232
+ statusTone: card.statusTone
3233
+ });
3234
+ const isEdit = card.toolName === "edit_file" || card.toolName === "write_file" || card.toolName === "apply_patch" || card.toolName === "file_change";
3235
+ return /* @__PURE__ */ jsxs14(ToolCardRoot, { children: [
3236
+ /* @__PURE__ */ jsx22(
2930
3237
  ToolCardHeader,
2931
3238
  {
2932
3239
  card,
2933
3240
  icon: isEdit ? Code2 : FileText,
2934
3241
  expanded,
2935
- canExpand: !!output || isRunning,
3242
+ canExpand: hasContent || isRunning,
3243
+ hideSummary: expanded && hasStructuredPreview,
2936
3244
  onToggle
2937
3245
  }
2938
3246
  ),
2939
- expanded && output && /* @__PURE__ */ jsx21(ToolCardContent, { children: /* @__PURE__ */ jsx21("div", { className: "font-mono text-[12px] leading-relaxed max-h-48 overflow-y-auto overflow-x-hidden min-w-0 custom-scrollbar-amber text-amber-950/80 w-full", children: displayLines.map(renderLine) }) })
3247
+ expanded && hasContent && /* @__PURE__ */ jsx22(ToolCardContent, { className: "border-t border-amber-200/20 bg-transparent px-0 pt-0 pb-0", children: /* @__PURE__ */ jsx22(ToolCardFileOperationContent, { card }) })
2940
3248
  ] });
2941
3249
  }
2942
3250
  function SearchSnippetView({ card }) {
2943
3251
  const isRunning = card.statusTone === "running";
2944
- const [expanded, setExpanded] = useState7(isRunning);
2945
- const [hasUserToggled, setHasUserToggled] = useState7(false);
2946
- const prevRunningRef = useRef6(isRunning);
2947
3252
  const output = card.output?.trim() ?? "";
2948
- useEffect7(() => {
2949
- if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2950
- setExpanded(false);
2951
- }
2952
- prevRunningRef.current = isRunning;
2953
- }, [isRunning, hasUserToggled]);
2954
- const onToggle = () => {
2955
- if (!output && !isRunning) return;
2956
- setExpanded(!expanded);
2957
- setHasUserToggled(true);
2958
- };
2959
- return /* @__PURE__ */ jsxs13(ToolCardRoot, { children: [
2960
- /* @__PURE__ */ jsx21(
3253
+ const { expanded, onToggle } = useToolCardExpandedState({
3254
+ canExpand: !!output || isRunning,
3255
+ isRunning,
3256
+ statusTone: card.statusTone
3257
+ });
3258
+ return /* @__PURE__ */ jsxs14(ToolCardRoot, { children: [
3259
+ /* @__PURE__ */ jsx22(
2961
3260
  ToolCardHeader,
2962
3261
  {
2963
3262
  card,
@@ -2967,51 +3266,58 @@ function SearchSnippetView({ card }) {
2967
3266
  onToggle
2968
3267
  }
2969
3268
  ),
2970
- expanded && output && /* @__PURE__ */ jsx21(ToolCardContent, { children: /* @__PURE__ */ jsx21("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 }) })
3269
+ expanded && output && /* @__PURE__ */ jsx22(ToolCardContent, { children: /* @__PURE__ */ jsx22("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 }) })
2971
3270
  ] });
2972
3271
  }
2973
3272
  function GenericToolCard({ card }) {
3273
+ const input = card.input?.trim() ?? "";
2974
3274
  const output = card.output?.trim() ?? "";
2975
3275
  const isRunning = card.statusTone === "running";
2976
- const [expanded, setExpanded] = useState7(isRunning);
2977
- const [hasUserToggled, setHasUserToggled] = useState7(false);
2978
- const prevRunningRef = useRef6(isRunning);
2979
- const showOutputSection = card.kind === "result" || card.hasResult;
2980
- useEffect7(() => {
2981
- if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2982
- setExpanded(false);
2983
- }
2984
- prevRunningRef.current = isRunning;
2985
- }, [isRunning, hasUserToggled]);
2986
- const onToggle = () => {
2987
- if (!showOutputSection) return;
2988
- setExpanded(!expanded);
2989
- setHasUserToggled(true);
2990
- };
2991
- return /* @__PURE__ */ jsxs13(ToolCardRoot, { children: [
2992
- /* @__PURE__ */ jsx21(
3276
+ const hasInputSection = input.length > 0;
3277
+ const hasOutputSection = output.length > 0;
3278
+ const hasContent = hasInputSection || hasOutputSection;
3279
+ const inputLabel = card.inputLabel?.trim() || "Input";
3280
+ const outputLabel = card.outputLabel?.trim() || "Output";
3281
+ const { expanded, onToggle } = useToolCardExpandedState({
3282
+ canExpand: hasContent || isRunning,
3283
+ isRunning,
3284
+ statusTone: card.statusTone
3285
+ });
3286
+ return /* @__PURE__ */ jsxs14(ToolCardRoot, { children: [
3287
+ /* @__PURE__ */ jsx22(
2993
3288
  ToolCardHeader,
2994
3289
  {
2995
3290
  card,
2996
3291
  icon: Globe,
2997
3292
  expanded,
2998
- canExpand: showOutputSection,
3293
+ canExpand: hasContent || isRunning,
2999
3294
  onToggle
3000
3295
  }
3001
3296
  ),
3002
- expanded && output && /* @__PURE__ */ jsx21(ToolCardContent, { children: /* @__PURE__ */ jsx21("pre", { className: "text-amber-950/80 whitespace-pre-wrap break-all overflow-y-auto overflow-x-hidden max-h-64 custom-scrollbar-amber w-full min-w-0 max-w-full leading-relaxed", children: output }) })
3297
+ expanded && hasContent && /* @__PURE__ */ jsxs14(ToolCardContent, { className: "bg-transparent px-2.5 py-2.5", children: [
3298
+ hasInputSection && /* @__PURE__ */ jsx22(GenericToolSection, { label: inputLabel, tone: "input", children: input }),
3299
+ hasInputSection && hasOutputSection && /* @__PURE__ */ jsx22("div", { className: "h-2" }),
3300
+ hasOutputSection && /* @__PURE__ */ jsx22(
3301
+ GenericToolSection,
3302
+ {
3303
+ label: outputLabel,
3304
+ tone: card.statusTone === "error" ? "error" : "output",
3305
+ children: output
3306
+ }
3307
+ )
3308
+ ] })
3003
3309
  ] });
3004
3310
  }
3005
3311
 
3006
3312
  // src/components/chat/ui/chat-message-list/chat-tool-card.tsx
3007
- import { jsx as jsx22 } from "react/jsx-runtime";
3313
+ import { jsx as jsx23 } from "react/jsx-runtime";
3008
3314
  function isTerminalTool(name) {
3009
3315
  const lowered = name.toLowerCase();
3010
- return lowered === "exec" || lowered === "execute_command" || lowered === "bash" || lowered === "shell" || lowered.includes("run_");
3316
+ return lowered === "exec" || lowered === "exec_command" || lowered === "execute_command" || lowered === "command_execution" || lowered === "bash" || lowered === "shell" || lowered.includes("run_");
3011
3317
  }
3012
3318
  function isFileEditTool(name) {
3013
3319
  const lowered = name.toLowerCase();
3014
- return lowered === "read_file" || lowered === "write_file" || lowered === "edit_file" || lowered === "apply_patch";
3320
+ return lowered === "read_file" || lowered === "write_file" || lowered === "edit_file" || lowered === "apply_patch" || lowered === "file_change";
3015
3321
  }
3016
3322
  function isSearchTool(name) {
3017
3323
  const lowered = name.toLowerCase();
@@ -3019,48 +3325,48 @@ function isSearchTool(name) {
3019
3325
  }
3020
3326
  function ChatToolCard({ card }) {
3021
3327
  if (isTerminalTool(card.toolName)) {
3022
- return /* @__PURE__ */ jsx22(TerminalExecutionView, { card });
3328
+ return /* @__PURE__ */ jsx23(TerminalExecutionView, { card });
3023
3329
  }
3024
3330
  if (isFileEditTool(card.toolName)) {
3025
- return /* @__PURE__ */ jsx22(FileOperationView, { card });
3331
+ return /* @__PURE__ */ jsx23(FileOperationView, { card });
3026
3332
  }
3027
3333
  if (isSearchTool(card.toolName)) {
3028
- return /* @__PURE__ */ jsx22(SearchSnippetView, { card });
3334
+ return /* @__PURE__ */ jsx23(SearchSnippetView, { card });
3029
3335
  }
3030
- return /* @__PURE__ */ jsx22(GenericToolCard, { card });
3336
+ return /* @__PURE__ */ jsx23(GenericToolCard, { card });
3031
3337
  }
3032
3338
 
3033
3339
  // src/components/chat/ui/chat-message-list/chat-unknown-part.tsx
3034
- import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
3340
+ import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
3035
3341
  function ChatUnknownPart(props) {
3036
- return /* @__PURE__ */ jsxs14("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-2 text-xs text-gray-600", children: [
3037
- /* @__PURE__ */ jsxs14("div", { className: "font-semibold text-gray-700", children: [
3342
+ return /* @__PURE__ */ jsxs15("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-2 text-xs text-gray-600", children: [
3343
+ /* @__PURE__ */ jsxs15("div", { className: "font-semibold text-gray-700", children: [
3038
3344
  props.label,
3039
3345
  ": ",
3040
3346
  props.rawType
3041
3347
  ] }),
3042
- props.text ? /* @__PURE__ */ jsx23("pre", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-gray-500", children: props.text }) : null
3348
+ props.text ? /* @__PURE__ */ jsx24("pre", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-gray-500", children: props.text }) : null
3043
3349
  ] });
3044
3350
  }
3045
3351
 
3046
3352
  // src/components/chat/ui/chat-message-list/chat-message.tsx
3047
- import { jsx as jsx24 } from "react/jsx-runtime";
3353
+ import { jsx as jsx25 } from "react/jsx-runtime";
3048
3354
  var ChatMessage = memo(function ChatMessage2(props) {
3049
3355
  const { message, texts } = props;
3050
3356
  const { role } = message;
3051
3357
  const isUser = role === "user";
3052
3358
  const isMessageInProgress = message.status === "pending" || message.status === "streaming";
3053
- return /* @__PURE__ */ jsx24(
3359
+ return /* @__PURE__ */ jsx25(
3054
3360
  "div",
3055
3361
  {
3056
3362
  className: cn(
3057
3363
  "inline-block w-fit max-w-full rounded-2xl border px-4 shadow-sm",
3058
3364
  isUser ? "border-primary bg-primary py-3 text-white" : role === "assistant" ? "border-gray-200 bg-white pb-3 pt-4 text-gray-900" : "border-orange-200/80 bg-orange-50/70 py-3 text-gray-900"
3059
3365
  ),
3060
- children: /* @__PURE__ */ jsx24("div", { className: "space-y-2", children: message.parts.map((part, index) => {
3366
+ children: /* @__PURE__ */ jsx25("div", { className: "space-y-2", children: message.parts.map((part, index) => {
3061
3367
  const { type } = part;
3062
3368
  if (type === "markdown") {
3063
- return /* @__PURE__ */ jsx24(
3369
+ return /* @__PURE__ */ jsx25(
3064
3370
  ChatMessageMarkdown,
3065
3371
  {
3066
3372
  text: part.text,
@@ -3071,7 +3377,7 @@ var ChatMessage = memo(function ChatMessage2(props) {
3071
3377
  );
3072
3378
  }
3073
3379
  if (type === "inline-content") {
3074
- return /* @__PURE__ */ jsx24(
3380
+ return /* @__PURE__ */ jsx25(
3075
3381
  ChatMessageInlineContent,
3076
3382
  {
3077
3383
  segments: part.segments,
@@ -3082,7 +3388,7 @@ var ChatMessage = memo(function ChatMessage2(props) {
3082
3388
  );
3083
3389
  }
3084
3390
  if (type === "reasoning") {
3085
- return /* @__PURE__ */ jsx24(
3391
+ return /* @__PURE__ */ jsx25(
3086
3392
  ChatReasoningBlock,
3087
3393
  {
3088
3394
  label: part.label,
@@ -3094,10 +3400,10 @@ var ChatMessage = memo(function ChatMessage2(props) {
3094
3400
  );
3095
3401
  }
3096
3402
  if (type === "tool-card") {
3097
- return /* @__PURE__ */ jsx24("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx24(ChatToolCard, { card: part.card }) }, `tool-${index}`);
3403
+ return /* @__PURE__ */ jsx25("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx25(ChatToolCard, { card: part.card }) }, `tool-${index}`);
3098
3404
  }
3099
3405
  if (type === "file") {
3100
- return /* @__PURE__ */ jsx24(
3406
+ return /* @__PURE__ */ jsx25(
3101
3407
  ChatMessageFile,
3102
3408
  {
3103
3409
  file: part.file,
@@ -3107,7 +3413,7 @@ var ChatMessage = memo(function ChatMessage2(props) {
3107
3413
  );
3108
3414
  }
3109
3415
  if (type === "unknown") {
3110
- return /* @__PURE__ */ jsx24(
3416
+ return /* @__PURE__ */ jsx25(
3111
3417
  ChatUnknownPart,
3112
3418
  {
3113
3419
  label: part.label,
@@ -3124,9 +3430,9 @@ var ChatMessage = memo(function ChatMessage2(props) {
3124
3430
  });
3125
3431
 
3126
3432
  // src/components/chat/ui/chat-message-list/chat-message-meta.tsx
3127
- import { jsxs as jsxs15 } from "react/jsx-runtime";
3433
+ import { jsxs as jsxs16 } from "react/jsx-runtime";
3128
3434
  function ChatMessageMeta(props) {
3129
- return /* @__PURE__ */ jsxs15(
3435
+ return /* @__PURE__ */ jsxs16(
3130
3436
  "div",
3131
3437
  {
3132
3438
  className: cn(
@@ -3145,7 +3451,7 @@ function ChatMessageMeta(props) {
3145
3451
  // src/components/chat/ui/chat-message-list/chat-message-action-copy.tsx
3146
3452
  import { useMemo as useMemo6 } from "react";
3147
3453
  import { Check as Check5, Copy as Copy2 } from "lucide-react";
3148
- import { jsx as jsx25 } from "react/jsx-runtime";
3454
+ import { jsx as jsx26 } from "react/jsx-runtime";
3149
3455
  function ChatMessageActionCopy({
3150
3456
  message,
3151
3457
  texts
@@ -3159,7 +3465,7 @@ function ChatMessageActionCopy({
3159
3465
  }, [message.parts]);
3160
3466
  const { copied, copy } = useCopyFeedback({ text: messageText });
3161
3467
  if (!messageText) return null;
3162
- return /* @__PURE__ */ jsx25(
3468
+ return /* @__PURE__ */ jsx26(
3163
3469
  "button",
3164
3470
  {
3165
3471
  type: "button",
@@ -3167,13 +3473,13 @@ function ChatMessageActionCopy({
3167
3473
  className: "text-gray-400 hover:text-gray-600 transition-colors p-1 rounded-md hover:bg-gray-100 flex items-center justify-center",
3168
3474
  "aria-label": copied ? texts.copiedMessageLabel : texts.copyMessageLabel,
3169
3475
  title: copied ? texts.copiedMessageLabel : texts.copyMessageLabel,
3170
- children: copied ? /* @__PURE__ */ jsx25(Check5, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx25(Copy2, { className: "h-3.5 w-3.5" })
3476
+ children: copied ? /* @__PURE__ */ jsx26(Check5, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx26(Copy2, { className: "h-3.5 w-3.5" })
3171
3477
  }
3172
3478
  );
3173
3479
  }
3174
3480
 
3175
3481
  // src/components/chat/ui/chat-message-list/chat-message-list.tsx
3176
- import { Fragment as Fragment5, jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
3482
+ import { Fragment as Fragment6, jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
3177
3483
  var INVISIBLE_ONLY_TEXT_PATTERN = /\u200B|\u200C|\u200D|\u2060|\uFEFF/g;
3178
3484
  function hasRenderableText(value) {
3179
3485
  const trimmed = value.trim();
@@ -3191,10 +3497,10 @@ function hasRenderableMessageContent(message) {
3191
3497
  });
3192
3498
  }
3193
3499
  function ChatMessageTypingFooter() {
3194
- return /* @__PURE__ */ jsx26("div", { className: "flex items-center gap-2 px-1 py-0.5 text-[11px] text-gray-400", children: /* @__PURE__ */ jsxs16("div", { className: "flex space-x-1 items-center h-full", children: [
3195
- /* @__PURE__ */ jsx26("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse" }),
3196
- /* @__PURE__ */ jsx26("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse [animation-delay:200ms]" }),
3197
- /* @__PURE__ */ jsx26("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse [animation-delay:400ms]" })
3500
+ return /* @__PURE__ */ jsx27("div", { className: "flex items-center gap-2 px-1 py-0.5 text-[11px] text-gray-400", children: /* @__PURE__ */ jsxs17("div", { className: "flex space-x-1 items-center h-full", children: [
3501
+ /* @__PURE__ */ jsx27("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse" }),
3502
+ /* @__PURE__ */ jsx27("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse [animation-delay:200ms]" }),
3503
+ /* @__PURE__ */ jsx27("div", { className: "h-1.5 w-1.5 rounded-full bg-gray-400 animate-pulse [animation-delay:400ms]" })
3198
3504
  ] }) });
3199
3505
  }
3200
3506
  function ChatMessageList(props) {
@@ -3202,25 +3508,25 @@ function ChatMessageList(props) {
3202
3508
  const hasRenderableAssistantDraft = visibleMessages.some(
3203
3509
  (message) => message.role === "assistant" && (message.status === "streaming" || message.status === "pending")
3204
3510
  );
3205
- return /* @__PURE__ */ jsxs16("div", { className: cn("space-y-5", props.className), children: [
3511
+ return /* @__PURE__ */ jsxs17("div", { className: cn("space-y-5", props.className), children: [
3206
3512
  visibleMessages.map((message) => {
3207
3513
  const isUser = message.role === "user";
3208
3514
  const isGenerating = !isUser && (message.status === "streaming" || message.status === "pending");
3209
- return /* @__PURE__ */ jsxs16("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
3210
- !isUser ? /* @__PURE__ */ jsx26(ChatMessageAvatar, { role: message.role }) : null,
3211
- /* @__PURE__ */ jsxs16("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
3212
- /* @__PURE__ */ jsx26(ChatMessage, { message, texts: props.texts }),
3213
- /* @__PURE__ */ jsx26("div", { className: cn("flex items-center gap-2", isUser && "justify-end"), children: isGenerating ? /* @__PURE__ */ jsx26(ChatMessageTypingFooter, {}) : /* @__PURE__ */ jsxs16(Fragment5, { children: [
3214
- /* @__PURE__ */ jsx26(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser }),
3215
- !isUser ? /* @__PURE__ */ jsx26(ChatMessageActionCopy, { message, texts: props.texts }) : null
3515
+ return /* @__PURE__ */ jsxs17("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
3516
+ !isUser ? /* @__PURE__ */ jsx27(ChatMessageAvatar, { role: message.role }) : null,
3517
+ /* @__PURE__ */ jsxs17("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
3518
+ /* @__PURE__ */ jsx27(ChatMessage, { message, texts: props.texts }),
3519
+ /* @__PURE__ */ jsx27("div", { className: cn("flex items-center gap-2", isUser && "justify-end"), children: isGenerating ? /* @__PURE__ */ jsx27(ChatMessageTypingFooter, {}) : /* @__PURE__ */ jsxs17(Fragment6, { children: [
3520
+ /* @__PURE__ */ jsx27(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser }),
3521
+ !isUser ? /* @__PURE__ */ jsx27(ChatMessageActionCopy, { message, texts: props.texts }) : null
3216
3522
  ] }) })
3217
3523
  ] }),
3218
- isUser ? /* @__PURE__ */ jsx26(ChatMessageAvatar, { role: message.role }) : null
3524
+ isUser ? /* @__PURE__ */ jsx27(ChatMessageAvatar, { role: message.role }) : null
3219
3525
  ] }, message.id);
3220
3526
  }),
3221
- props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs16("div", { className: "flex justify-start gap-3", children: [
3222
- /* @__PURE__ */ jsx26(ChatMessageAvatar, { role: "assistant" }),
3223
- /* @__PURE__ */ jsx26("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 })
3527
+ props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs17("div", { className: "flex justify-start gap-3", children: [
3528
+ /* @__PURE__ */ jsx27(ChatMessageAvatar, { role: "assistant" }),
3529
+ /* @__PURE__ */ jsx27("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 })
3224
3530
  ] }) : null
3225
3531
  ] });
3226
3532
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/agent-chat-ui",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "private": false,
5
5
  "description": "Reusable Nextclaw agent chat UI primitives and default skin.",
6
6
  "type": "module",