@nextclaw/agent-chat-ui 0.2.8 → 0.2.10

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.
Files changed (2) hide show
  1. package/dist/index.js +301 -95
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2523,109 +2523,315 @@ function ChatReasoningBlock(props) {
2523
2523
  ] });
2524
2524
  }
2525
2525
 
2526
- // src/components/chat/ui/chat-message-list/chat-tool-card.tsx
2527
- import { AlertCircle, CheckCircle2, CircleSlash, Clock3, FileSearch, Globe, Loader2, Search as Search2, SendHorizontal, Terminal, Wrench as Wrench2 } from "lucide-react";
2528
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
2529
- var TOOL_OUTPUT_PREVIEW_MAX = 220;
2526
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-views.tsx
2527
+ import { Terminal, FileText, Code2, Search as Search2, Globe } from "lucide-react";
2528
+ import { useState as useState6, useEffect as useEffect6, useRef as useRef5 } from "react";
2529
+
2530
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-root.tsx
2531
+ import { jsx as jsx17 } from "react/jsx-runtime";
2532
+ function ToolCardRoot({ children, className }) {
2533
+ return /* @__PURE__ */ jsx17(
2534
+ "div",
2535
+ {
2536
+ className: cn(
2537
+ "my-2 rounded-lg border border-amber-200/50 bg-amber-100/30 shadow-sm overflow-hidden text-[12px]",
2538
+ "w-[280px] sm:w-[360px] md:w-[480px] min-w-full max-w-full transition-all flex flex-col",
2539
+ className
2540
+ ),
2541
+ children
2542
+ }
2543
+ );
2544
+ }
2545
+ function ToolCardContent({ children, className }) {
2546
+ return /* @__PURE__ */ jsx17("div", { className: cn("border-t border-amber-200/15 bg-amber-50/50 p-3 w-full overflow-hidden", className), children });
2547
+ }
2548
+
2549
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-header.tsx
2550
+ import { ChevronDown as ChevronDown2, ChevronRight } from "lucide-react";
2551
+
2552
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-status.tsx
2553
+ import { Check as Check4, Loader2, AlertTriangle, Minus } from "lucide-react";
2554
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
2530
2555
  var STATUS_STYLES = {
2531
- running: {
2532
- text: "text-amber-700/80"
2533
- },
2534
- success: {
2535
- text: "text-amber-700/80"
2536
- },
2537
- error: {
2538
- text: "text-amber-700/80"
2539
- },
2540
- cancelled: {
2541
- text: "text-amber-700/80"
2542
- }
2556
+ running: { text: "text-amber-500/80", icon: Loader2, spin: true },
2557
+ success: { text: "text-amber-500/80", icon: Check4, spin: false },
2558
+ error: { text: "text-amber-500/80", icon: AlertTriangle, spin: false },
2559
+ cancelled: { text: "text-amber-500/80", icon: Minus, spin: false }
2543
2560
  };
2544
- function renderToolIcon(toolName) {
2545
- const lowered = toolName.toLowerCase();
2546
- if (lowered.includes("exec") || lowered.includes("shell") || lowered.includes("command")) {
2547
- return /* @__PURE__ */ jsx17(Terminal, { className: "h-3.5 w-3.5" });
2548
- }
2549
- if (lowered.includes("search")) {
2550
- return /* @__PURE__ */ jsx17(Search2, { className: "h-3.5 w-3.5" });
2551
- }
2552
- if (lowered.includes("fetch") || lowered.includes("http") || lowered.includes("web")) {
2553
- return /* @__PURE__ */ jsx17(Globe, { className: "h-3.5 w-3.5" });
2554
- }
2555
- if (lowered.includes("read") || lowered.includes("file")) {
2556
- return /* @__PURE__ */ jsx17(FileSearch, { className: "h-3.5 w-3.5" });
2557
- }
2558
- if (lowered.includes("message") || lowered.includes("send")) {
2559
- return /* @__PURE__ */ jsx17(SendHorizontal, { className: "h-3.5 w-3.5" });
2560
- }
2561
- if (lowered.includes("cron") || lowered.includes("schedule")) {
2562
- return /* @__PURE__ */ jsx17(Clock3, { className: "h-3.5 w-3.5" });
2563
- }
2564
- return /* @__PURE__ */ jsx17(Wrench2, { className: "h-3.5 w-3.5" });
2561
+ function ToolStatusLabel({ card }) {
2562
+ const style = STATUS_STYLES[card.statusTone] || STATUS_STYLES.cancelled;
2563
+ const Icon2 = style.icon;
2564
+ return /* @__PURE__ */ jsxs10("span", { className: cn("inline-flex items-center gap-1.5 text-[11px] font-medium leading-none shrink-0", style.text), children: [
2565
+ /* @__PURE__ */ jsx18(Icon2, { className: cn("h-3.5 w-3.5", style.spin && "animate-spin"), strokeWidth: 3 }),
2566
+ card.statusTone === "running" ? card.statusLabel : null
2567
+ ] });
2565
2568
  }
2566
- function renderStatusMeta(card) {
2567
- const style = STATUS_STYLES[card.statusTone];
2568
- if (card.statusTone === "running") {
2569
- return /* @__PURE__ */ jsxs10("span", { className: cn("inline-flex items-center gap-1 text-[11px] font-medium leading-none", style.text), children: [
2570
- /* @__PURE__ */ jsx17(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
2571
- card.statusLabel
2572
- ] });
2573
- }
2574
- const icon = card.statusTone === "success" ? /* @__PURE__ */ jsx17(CheckCircle2, { className: "h-3.5 w-3.5" }) : card.statusTone === "error" ? /* @__PURE__ */ jsx17(AlertCircle, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx17(CircleSlash, { className: "h-3.5 w-3.5" });
2575
- return /* @__PURE__ */ jsxs10("span", { className: cn("inline-flex items-center gap-1 text-[11px] font-medium leading-none", style.text), children: [
2576
- icon,
2577
- card.statusTone === "success" ? null : card.statusLabel
2569
+
2570
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-header.tsx
2571
+ import { Fragment as Fragment2, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
2572
+ function ToolCardHeader({
2573
+ card,
2574
+ icon: Icon2,
2575
+ expanded,
2576
+ canExpand,
2577
+ onToggle
2578
+ }) {
2579
+ const summaryPart = card.summary?.replace(/^(command|path|args|query|input):\s*/i, "");
2580
+ return /* @__PURE__ */ jsxs11(
2581
+ "div",
2582
+ {
2583
+ className: cn(
2584
+ "flex items-center justify-between px-3 py-2.5 transition-colors bg-transparent",
2585
+ canExpand ? "cursor-pointer hover:bg-amber-100/30" : ""
2586
+ ),
2587
+ onClick: onToggle,
2588
+ children: [
2589
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 font-mono min-w-0 max-w-[calc(100%-80px)] text-amber-950/80", children: [
2590
+ /* @__PURE__ */ jsx19(Icon2, { className: "h-4 w-4 text-amber-600/80 shrink-0", strokeWidth: 3 }),
2591
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 min-w-0", children: [
2592
+ /* @__PURE__ */ jsx19("span", { className: "font-bold shrink-0 tracking-tight", children: card.toolName }),
2593
+ summaryPart && /* @__PURE__ */ jsxs11(Fragment2, { children: [
2594
+ /* @__PURE__ */ jsx19("span", { className: "text-amber-300 font-bold select-none shrink-0", children: "\u203A" }),
2595
+ /* @__PURE__ */ jsx19("span", { className: "truncate flex-1 min-w-0 font-normal", title: summaryPart, children: summaryPart })
2596
+ ] })
2597
+ ] })
2598
+ ] }),
2599
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-3 shrink-0", children: [
2600
+ /* @__PURE__ */ jsx19(ToolStatusLabel, { card }),
2601
+ canExpand && (expanded ? /* @__PURE__ */ jsx19(ChevronDown2, { className: "h-4 w-4 text-amber-400/80", strokeWidth: 3 }) : /* @__PURE__ */ jsx19(ChevronRight, { className: "h-4 w-4 text-amber-400/80", strokeWidth: 3 }))
2602
+ ] })
2603
+ ]
2604
+ }
2605
+ );
2606
+ }
2607
+
2608
+ // src/components/chat/ui/chat-message-list/tool-card/tool-card-views.tsx
2609
+ import { Fragment as Fragment3, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
2610
+ function TerminalExecutionView({ card }) {
2611
+ const output = card.output?.trim() ?? "";
2612
+ const isRunning = card.statusTone === "running";
2613
+ const hasContent = !!(card.summary?.trim() || output.length > 0);
2614
+ const wasEmptyRef = useRef5(!hasContent);
2615
+ const [expanded, setExpanded] = useState6(hasContent && (isRunning || card.statusTone === "error" || output.length < 500));
2616
+ const [hasUserToggled, setHasUserToggled] = useState6(false);
2617
+ const prevRunningRef = useRef5(isRunning);
2618
+ useEffect6(() => {
2619
+ if (wasEmptyRef.current && hasContent && isRunning) {
2620
+ setExpanded(true);
2621
+ wasEmptyRef.current = false;
2622
+ }
2623
+ }, [hasContent, isRunning]);
2624
+ useEffect6(() => {
2625
+ if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2626
+ setExpanded(false);
2627
+ }
2628
+ prevRunningRef.current = isRunning;
2629
+ }, [isRunning, hasUserToggled]);
2630
+ const onToggle = () => {
2631
+ if (!output && !isRunning) return;
2632
+ setExpanded(!expanded);
2633
+ setHasUserToggled(true);
2634
+ };
2635
+ const commandPart = card.summary?.replace(/^(command|path|args|query|input):\s*/i, "");
2636
+ return /* @__PURE__ */ jsxs12(ToolCardRoot, { children: [
2637
+ /* @__PURE__ */ jsx20(
2638
+ ToolCardHeader,
2639
+ {
2640
+ card,
2641
+ icon: Terminal,
2642
+ expanded,
2643
+ canExpand: !!output || isRunning,
2644
+ onToggle
2645
+ }
2646
+ ),
2647
+ expanded && /* @__PURE__ */ jsxs12(Fragment3, { children: [
2648
+ /* @__PURE__ */ jsx20("div", { className: "px-3 pb-0.5 font-mono w-full max-h-48 overflow-y-auto custom-scrollbar-amber min-h-0 text-[12px]", children: /* @__PURE__ */ jsxs12("div", { className: "flex items-start gap-2 leading-relaxed", children: [
2649
+ /* @__PURE__ */ jsx20("span", { className: "text-amber-500/50 font-medium shrink-0 select-none mt-[1px]", children: "$" }),
2650
+ /* @__PURE__ */ jsx20("div", { className: "flex-1 min-w-0", children: commandPart ? /* @__PURE__ */ jsx20("span", { className: "text-amber-950/80 break-words whitespace-pre-wrap tracking-tight font-medium", children: commandPart }) : /* @__PURE__ */ jsx20("div", { className: "h-3 w-32 bg-amber-200/30 rounded animate-pulse mt-2" }) })
2651
+ ] }) }),
2652
+ (output || isRunning && hasContent) && /* @__PURE__ */ jsx20(ToolCardContent, { className: "mt-2", children: /* @__PURE__ */ jsxs12("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: [
2653
+ output,
2654
+ isRunning && /* @__PURE__ */ jsx20("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })
2655
+ ] }) })
2656
+ ] })
2578
2657
  ] });
2579
2658
  }
2580
- function ChatToolCard({ card }) {
2659
+ function FileOperationView({ card }) {
2660
+ const output = card.output?.trim() ?? "";
2661
+ const isRunning = card.statusTone === "running";
2662
+ const [expanded, setExpanded] = useState6(false);
2663
+ const [hasUserToggled, setHasUserToggled] = useState6(false);
2664
+ const prevRunningRef = useRef5(isRunning);
2665
+ useEffect6(() => {
2666
+ if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2667
+ setExpanded(false);
2668
+ }
2669
+ prevRunningRef.current = isRunning;
2670
+ }, [isRunning, hasUserToggled]);
2671
+ const onToggle = () => {
2672
+ if (!output && !isRunning) return;
2673
+ setExpanded(!expanded);
2674
+ setHasUserToggled(true);
2675
+ };
2676
+ const isEdit = card.toolName === "edit_file" || card.toolName === "write_file";
2677
+ const renderLine = (line, idx) => {
2678
+ if (isEdit) {
2679
+ if (line.startsWith("+") && !line.startsWith("+++")) {
2680
+ return /* @__PURE__ */ jsxs12("div", { className: "bg-emerald-500/10 text-emerald-700 px-2 py-0.5 w-full break-all whitespace-pre-wrap", children: [
2681
+ /* @__PURE__ */ jsx20("span", { className: "select-none opacity-40 mr-2 w-3 inline-block shrink-0", children: "+" }),
2682
+ /* @__PURE__ */ jsx20("span", { children: line.slice(1) })
2683
+ ] }, idx);
2684
+ }
2685
+ if (line.startsWith("-") && !line.startsWith("---")) {
2686
+ return /* @__PURE__ */ jsxs12("div", { className: "bg-rose-500/10 text-rose-700 px-2 py-0.5 w-full break-all whitespace-pre-wrap", children: [
2687
+ /* @__PURE__ */ jsx20("span", { className: "select-none opacity-40 mr-2 w-3 inline-block shrink-0", children: "-" }),
2688
+ /* @__PURE__ */ jsx20("span", { className: "line-through decoration-rose-400/50", children: line.slice(1) })
2689
+ ] }, idx);
2690
+ }
2691
+ }
2692
+ return /* @__PURE__ */ jsxs12("div", { className: "px-2 py-0.5 text-amber-950/80 w-full break-all whitespace-pre-wrap", children: [
2693
+ /* @__PURE__ */ jsx20("span", { className: "select-none opacity-0 mr-2 w-3 inline-block shrink-0", children: " " }),
2694
+ /* @__PURE__ */ jsx20("span", { children: line })
2695
+ ] }, idx);
2696
+ };
2697
+ const lines = output.split("\n");
2698
+ const maxLines = 15;
2699
+ const isLong = lines.length > maxLines;
2700
+ const displayLines = !expanded && isLong ? lines.slice(0, maxLines) : lines;
2701
+ return /* @__PURE__ */ jsxs12(ToolCardRoot, { children: [
2702
+ /* @__PURE__ */ jsx20(
2703
+ ToolCardHeader,
2704
+ {
2705
+ card,
2706
+ icon: isEdit ? Code2 : FileText,
2707
+ expanded,
2708
+ canExpand: !!output || isRunning,
2709
+ onToggle
2710
+ }
2711
+ ),
2712
+ expanded && output && /* @__PURE__ */ jsx20(ToolCardContent, { children: /* @__PURE__ */ jsx20("div", { className: "font-mono text-[12px] leading-relaxed py-2 max-h-48 overflow-y-auto overflow-x-hidden min-w-0 custom-scrollbar-amber text-amber-950/80 w-full px-1", children: displayLines.map(renderLine) }) })
2713
+ ] });
2714
+ }
2715
+ function SearchSnippetView({ card }) {
2716
+ const isRunning = card.statusTone === "running";
2717
+ const [expanded, setExpanded] = useState6(isRunning);
2718
+ const [hasUserToggled, setHasUserToggled] = useState6(false);
2719
+ const prevRunningRef = useRef5(isRunning);
2581
2720
  const output = card.output?.trim() ?? "";
2582
- const showDetails = output.length > TOOL_OUTPUT_PREVIEW_MAX || output.includes("\n");
2583
- const preview = showDetails ? `${output.slice(0, TOOL_OUTPUT_PREVIEW_MAX)}...` : output;
2721
+ useEffect6(() => {
2722
+ if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2723
+ setExpanded(false);
2724
+ }
2725
+ prevRunningRef.current = isRunning;
2726
+ }, [isRunning, hasUserToggled]);
2727
+ const onToggle = () => {
2728
+ if (!output && !isRunning) return;
2729
+ setExpanded(!expanded);
2730
+ setHasUserToggled(true);
2731
+ };
2732
+ return /* @__PURE__ */ jsxs12(ToolCardRoot, { children: [
2733
+ /* @__PURE__ */ jsx20(
2734
+ ToolCardHeader,
2735
+ {
2736
+ card,
2737
+ icon: Search2,
2738
+ expanded,
2739
+ canExpand: !!output || isRunning,
2740
+ onToggle
2741
+ }
2742
+ ),
2743
+ expanded && output && /* @__PURE__ */ jsx20(ToolCardContent, { children: /* @__PURE__ */ jsx20("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 py-2 leading-relaxed", children: output }) })
2744
+ ] });
2745
+ }
2746
+ function GenericToolCard({ card }) {
2747
+ const output = card.output?.trim() ?? "";
2748
+ const isRunning = card.statusTone === "running";
2749
+ const [expanded, setExpanded] = useState6(isRunning);
2750
+ const [hasUserToggled, setHasUserToggled] = useState6(false);
2751
+ const prevRunningRef = useRef5(isRunning);
2584
2752
  const showOutputSection = card.kind === "result" || card.hasResult;
2585
- return /* @__PURE__ */ jsxs10("div", { className: "rounded-xl border border-amber-200/80 bg-amber-50/60 px-3 py-2.5", children: [
2586
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2 text-xs font-semibold text-amber-800", children: [
2587
- renderToolIcon(card.toolName),
2588
- /* @__PURE__ */ jsx17("span", { children: card.titleLabel }),
2589
- /* @__PURE__ */ jsx17("span", { className: "font-mono text-[11px] text-amber-900/80", children: card.toolName }),
2590
- renderStatusMeta(card)
2591
- ] }),
2592
- card.summary ? /* @__PURE__ */ jsx17("div", { className: "mt-1 break-words font-mono text-[11px] text-amber-800/90", children: card.summary }) : null,
2593
- 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: [
2594
- /* @__PURE__ */ jsx17("summary", { className: "cursor-pointer text-[11px] text-amber-700", children: card.outputLabel }),
2595
- /* @__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 })
2596
- ] }) : /* @__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
2753
+ useEffect6(() => {
2754
+ if (prevRunningRef.current && !isRunning && !hasUserToggled) {
2755
+ setExpanded(false);
2756
+ }
2757
+ prevRunningRef.current = isRunning;
2758
+ }, [isRunning, hasUserToggled]);
2759
+ const onToggle = () => {
2760
+ if (!showOutputSection) return;
2761
+ setExpanded(!expanded);
2762
+ setHasUserToggled(true);
2763
+ };
2764
+ return /* @__PURE__ */ jsxs12(ToolCardRoot, { children: [
2765
+ /* @__PURE__ */ jsx20(
2766
+ ToolCardHeader,
2767
+ {
2768
+ card,
2769
+ icon: Globe,
2770
+ expanded,
2771
+ canExpand: showOutputSection,
2772
+ onToggle
2773
+ }
2774
+ ),
2775
+ expanded && output && /* @__PURE__ */ jsx20(ToolCardContent, { children: /* @__PURE__ */ jsx20("pre", { className: "mt-1 text-amber-950/80 whitespace-pre-wrap break-all overflow-y-auto overflow-x-hidden max-h-64 custom-scrollbar-amber py-1 w-full min-w-0 max-w-full leading-relaxed", children: output }) })
2597
2776
  ] });
2598
2777
  }
2599
2778
 
2779
+ // src/components/chat/ui/chat-message-list/chat-tool-card.tsx
2780
+ import { jsx as jsx21 } from "react/jsx-runtime";
2781
+ function isTerminalTool(name) {
2782
+ const lowered = name.toLowerCase();
2783
+ return lowered === "exec" || lowered === "execute_command" || lowered === "bash" || lowered === "shell" || lowered.includes("run_");
2784
+ }
2785
+ function isFileEditTool(name) {
2786
+ const lowered = name.toLowerCase();
2787
+ return lowered === "read_file" || lowered === "write_file" || lowered === "edit_file" || lowered === "apply_patch";
2788
+ }
2789
+ function isSearchTool(name) {
2790
+ const lowered = name.toLowerCase();
2791
+ return lowered === "grep_search" || lowered === "find_files" || lowered.includes("search");
2792
+ }
2793
+ function ChatToolCard({ card }) {
2794
+ if (isTerminalTool(card.toolName)) {
2795
+ return /* @__PURE__ */ jsx21(TerminalExecutionView, { card });
2796
+ }
2797
+ if (isFileEditTool(card.toolName)) {
2798
+ return /* @__PURE__ */ jsx21(FileOperationView, { card });
2799
+ }
2800
+ if (isSearchTool(card.toolName)) {
2801
+ return /* @__PURE__ */ jsx21(SearchSnippetView, { card });
2802
+ }
2803
+ return /* @__PURE__ */ jsx21(GenericToolCard, { card });
2804
+ }
2805
+
2600
2806
  // src/components/chat/ui/chat-message-list/chat-unknown-part.tsx
2601
- import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
2807
+ import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
2602
2808
  function ChatUnknownPart(props) {
2603
- 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: [
2604
- /* @__PURE__ */ jsxs11("div", { className: "font-semibold text-gray-700", children: [
2809
+ return /* @__PURE__ */ jsxs13("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-2 text-xs text-gray-600", children: [
2810
+ /* @__PURE__ */ jsxs13("div", { className: "font-semibold text-gray-700", children: [
2605
2811
  props.label,
2606
2812
  ": ",
2607
2813
  props.rawType
2608
2814
  ] }),
2609
- props.text ? /* @__PURE__ */ jsx18("pre", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-gray-500", children: props.text }) : null
2815
+ props.text ? /* @__PURE__ */ jsx22("pre", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-gray-500", children: props.text }) : null
2610
2816
  ] });
2611
2817
  }
2612
2818
 
2613
2819
  // src/components/chat/ui/chat-message-list/chat-message.tsx
2614
- import { jsx as jsx19 } from "react/jsx-runtime";
2820
+ import { jsx as jsx23 } from "react/jsx-runtime";
2615
2821
  function ChatMessage(props) {
2616
2822
  const { message, texts } = props;
2617
2823
  const { role } = message;
2618
2824
  const isUser = role === "user";
2619
- return /* @__PURE__ */ jsx19(
2825
+ return /* @__PURE__ */ jsx23(
2620
2826
  "div",
2621
2827
  {
2622
2828
  className: cn(
2623
2829
  "inline-block w-fit max-w-full rounded-2xl border px-4 py-3 shadow-sm",
2624
2830
  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"
2625
2831
  ),
2626
- children: /* @__PURE__ */ jsx19("div", { className: "space-y-2", children: message.parts.map((part, index) => {
2832
+ children: /* @__PURE__ */ jsx23("div", { className: "space-y-2", children: message.parts.map((part, index) => {
2627
2833
  if (part.type === "markdown") {
2628
- return /* @__PURE__ */ jsx19(
2834
+ return /* @__PURE__ */ jsx23(
2629
2835
  ChatMessageMarkdown,
2630
2836
  {
2631
2837
  text: part.text,
@@ -2636,7 +2842,7 @@ function ChatMessage(props) {
2636
2842
  );
2637
2843
  }
2638
2844
  if (part.type === "reasoning") {
2639
- return /* @__PURE__ */ jsx19(
2845
+ return /* @__PURE__ */ jsx23(
2640
2846
  ChatReasoningBlock,
2641
2847
  {
2642
2848
  label: part.label,
@@ -2647,10 +2853,10 @@ function ChatMessage(props) {
2647
2853
  );
2648
2854
  }
2649
2855
  if (part.type === "tool-card") {
2650
- return /* @__PURE__ */ jsx19("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx19(ChatToolCard, { card: part.card }) }, `tool-${index}`);
2856
+ return /* @__PURE__ */ jsx23("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx23(ChatToolCard, { card: part.card }) }, `tool-${index}`);
2651
2857
  }
2652
2858
  if (part.type === "file") {
2653
- return /* @__PURE__ */ jsx19(
2859
+ return /* @__PURE__ */ jsx23(
2654
2860
  ChatMessageFile,
2655
2861
  {
2656
2862
  file: part.file,
@@ -2660,7 +2866,7 @@ function ChatMessage(props) {
2660
2866
  );
2661
2867
  }
2662
2868
  if (part.type === "unknown") {
2663
- return /* @__PURE__ */ jsx19(
2869
+ return /* @__PURE__ */ jsx23(
2664
2870
  ChatUnknownPart,
2665
2871
  {
2666
2872
  label: part.label,
@@ -2677,9 +2883,9 @@ function ChatMessage(props) {
2677
2883
  }
2678
2884
 
2679
2885
  // src/components/chat/ui/chat-message-list/chat-message-meta.tsx
2680
- import { jsxs as jsxs12 } from "react/jsx-runtime";
2886
+ import { jsxs as jsxs14 } from "react/jsx-runtime";
2681
2887
  function ChatMessageMeta(props) {
2682
- return /* @__PURE__ */ jsxs12(
2888
+ return /* @__PURE__ */ jsxs14(
2683
2889
  "div",
2684
2890
  {
2685
2891
  className: cn(
@@ -2696,7 +2902,7 @@ function ChatMessageMeta(props) {
2696
2902
  }
2697
2903
 
2698
2904
  // src/components/chat/ui/chat-message-list/chat-message-list.tsx
2699
- import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2905
+ import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
2700
2906
  var INVISIBLE_ONLY_TEXT_PATTERN = /\u200B|\u200C|\u200D|\u2060|\uFEFF/g;
2701
2907
  function hasRenderableText(value) {
2702
2908
  const trimmed = value.trim();
@@ -2718,36 +2924,36 @@ function ChatMessageList(props) {
2718
2924
  const hasRenderableAssistantDraft = visibleMessages.some(
2719
2925
  (message) => message.role === "assistant" && (message.status === "streaming" || message.status === "pending")
2720
2926
  );
2721
- return /* @__PURE__ */ jsxs13("div", { className: cn("space-y-5", props.className), children: [
2927
+ return /* @__PURE__ */ jsxs15("div", { className: cn("space-y-5", props.className), children: [
2722
2928
  visibleMessages.map((message) => {
2723
2929
  const isUser = message.role === "user";
2724
- return /* @__PURE__ */ jsxs13("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
2725
- !isUser ? /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: message.role }) : null,
2726
- /* @__PURE__ */ jsxs13("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
2727
- /* @__PURE__ */ jsx20(ChatMessage, { message, texts: props.texts }),
2728
- /* @__PURE__ */ jsx20(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser })
2930
+ return /* @__PURE__ */ jsxs15("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
2931
+ !isUser ? /* @__PURE__ */ jsx24(ChatMessageAvatar, { role: message.role }) : null,
2932
+ /* @__PURE__ */ jsxs15("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
2933
+ /* @__PURE__ */ jsx24(ChatMessage, { message, texts: props.texts }),
2934
+ /* @__PURE__ */ jsx24(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser })
2729
2935
  ] }),
2730
- isUser ? /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: message.role }) : null
2936
+ isUser ? /* @__PURE__ */ jsx24(ChatMessageAvatar, { role: message.role }) : null
2731
2937
  ] }, message.id);
2732
2938
  }),
2733
- props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs13("div", { className: "flex justify-start gap-3", children: [
2734
- /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: "assistant" }),
2735
- /* @__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 })
2939
+ props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs15("div", { className: "flex justify-start gap-3", children: [
2940
+ /* @__PURE__ */ jsx24(ChatMessageAvatar, { role: "assistant" }),
2941
+ /* @__PURE__ */ jsx24("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 })
2736
2942
  ] }) : null
2737
2943
  ] });
2738
2944
  }
2739
2945
 
2740
2946
  // src/components/chat/hooks/use-sticky-bottom-scroll.ts
2741
- import { useCallback as useCallback2, useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useRef as useRef5 } from "react";
2947
+ import { useCallback as useCallback2, useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useRef as useRef6 } from "react";
2742
2948
  var DEFAULT_STICKY_THRESHOLD_PX = 10;
2743
2949
  function scrollElementToBottom(element) {
2744
2950
  element.scrollTop = element.scrollHeight;
2745
2951
  }
2746
2952
  function useStickyBottomScroll(params) {
2747
- const isStickyRef = useRef5(true);
2748
- const isProgrammaticScrollRef = useRef5(false);
2749
- const previousResetKeyRef = useRef5(null);
2750
- const pendingInitialScrollRef = useRef5(false);
2953
+ const isStickyRef = useRef6(true);
2954
+ const isProgrammaticScrollRef = useRef6(false);
2955
+ const previousResetKeyRef = useRef6(null);
2956
+ const pendingInitialScrollRef = useRef6(false);
2751
2957
  const onScroll = useCallback2(() => {
2752
2958
  if (isProgrammaticScrollRef.current) {
2753
2959
  isProgrammaticScrollRef.current = false;
@@ -2760,7 +2966,7 @@ function useStickyBottomScroll(params) {
2760
2966
  const distanceFromBottom = element.scrollHeight - element.scrollTop - element.clientHeight;
2761
2967
  isStickyRef.current = distanceFromBottom <= (params.stickyThresholdPx ?? DEFAULT_STICKY_THRESHOLD_PX);
2762
2968
  }, [params.scrollRef, params.stickyThresholdPx]);
2763
- useEffect6(() => {
2969
+ useEffect7(() => {
2764
2970
  if (previousResetKeyRef.current === params.resetKey) {
2765
2971
  return;
2766
2972
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/agent-chat-ui",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "private": false,
5
5
  "description": "Reusable Nextclaw agent chat UI primitives and default skin.",
6
6
  "type": "module",