@nextclaw/agent-chat-ui 0.6.3 → 0.6.4
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 +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +586 -393
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { twMerge } from "tailwind-merge";
|
|
|
5
5
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
7
7
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
8
|
-
import { AlertTriangle, AppWindow, ArrowUp, ArrowUpRight, Bot, Brain, BrainCircuit, CalendarClock, Check, ChevronDown, ChevronRight, ChevronUp, Code2, Copy, Expand, ExternalLink, Eye, File, FileArchive, FileAudio2, FileCode2, FileImage, FileJson2, FileSpreadsheet, FileText, FileVideo2, FolderTree, Globe, Globe2, ImageIcon, Loader2, MessageSquare, Minus, Paperclip, Puzzle, Search, Send, Settings, Sparkles, Star, Terminal, User, Wrench, X } from "lucide-react";
|
|
8
|
+
import { AlertTriangle, AppWindow, ArrowUp, ArrowUpRight, Bot, Brain, BrainCircuit, CalendarClock, Check, ChevronDown, ChevronRight, ChevronUp, Code2, Copy, Expand, ExternalLink, Eye, File, FileArchive, FileAudio2, FileCode2, FileImage, FileJson2, FileSpreadsheet, FileText, FileVideo2, FolderTree, Globe, Globe2, ImageIcon, ListChecks, Loader2, MessageSquare, Minus, Paperclip, Puzzle, Search, Send, Settings, Sparkles, Star, Terminal, User, Workflow, Wrench, X } from "lucide-react";
|
|
9
9
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
10
10
|
import { cva } from "class-variance-authority";
|
|
11
11
|
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
@@ -513,7 +513,7 @@ const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref
|
|
|
513
513
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
514
514
|
className: "flex min-h-0 flex-col border-r border-gray-200",
|
|
515
515
|
children: [!isLoading && filterViews.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
516
|
-
className: "flex shrink-0 gap-
|
|
516
|
+
className: "flex shrink-0 gap-0.5 overflow-x-auto px-2 pb-1.5 pt-2",
|
|
517
517
|
children: filterViews.map(({ count, key, label }) => {
|
|
518
518
|
const isActive = key === resolvedActiveFilterKey;
|
|
519
519
|
return /* @__PURE__ */ jsxs("button", {
|
|
@@ -521,9 +521,9 @@ const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref
|
|
|
521
521
|
"aria-pressed": isActive,
|
|
522
522
|
onPointerDown: (event) => event.preventDefault(),
|
|
523
523
|
onClick: () => handleFilterSelect(key),
|
|
524
|
-
className: `inline-flex shrink-0 items-center gap-1 rounded-
|
|
524
|
+
className: `inline-flex h-7 shrink-0 items-center gap-1 rounded-md px-2 text-[11px] font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 ${isActive ? "bg-gray-100 text-gray-900" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}`,
|
|
525
525
|
children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", {
|
|
526
|
-
className: isActive ? "text-
|
|
526
|
+
className: isActive ? "text-gray-500" : "text-gray-400",
|
|
527
527
|
children: count
|
|
528
528
|
})]
|
|
529
529
|
}, key);
|
|
@@ -532,58 +532,52 @@ const ChatInputSurfaceMenu = forwardRef(function ChatInputSurfaceMenu(props, ref
|
|
|
532
532
|
ref: listRef,
|
|
533
533
|
role: "listbox",
|
|
534
534
|
"aria-label": texts.sectionLabel,
|
|
535
|
-
className: `custom-scrollbar min-h-0 flex-1 overflow-y-auto overscroll-contain ${!isLoading && filterViews.length > 0 ? "px-2
|
|
535
|
+
className: `custom-scrollbar min-h-0 flex-1 overflow-y-auto overscroll-contain ${!isLoading && filterViews.length > 0 ? "px-2 pb-2" : "p-2"}`,
|
|
536
536
|
children: isLoading ? /* @__PURE__ */ jsx("div", {
|
|
537
537
|
className: "p-2 text-xs text-gray-500",
|
|
538
538
|
children: texts.loadingLabel
|
|
539
539
|
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [!hasItemSections ? /* @__PURE__ */ jsx("div", {
|
|
540
|
-
className: "mb-
|
|
540
|
+
className: "mb-1 px-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
541
541
|
children: texts.sectionLabel
|
|
542
542
|
}) : null, visibleItems.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
543
|
-
className: "px-
|
|
543
|
+
className: "px-1.5 text-xs text-gray-400",
|
|
544
544
|
children: texts.emptyLabel
|
|
545
|
-
}) : /* @__PURE__ */ jsx("div", {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
children: subtitle
|
|
579
|
-
})]
|
|
580
|
-
})]
|
|
581
|
-
}, key);
|
|
582
|
-
})
|
|
583
|
-
})] })
|
|
545
|
+
}) : /* @__PURE__ */ jsx("div", { children: visibleItems.map((item, index) => {
|
|
546
|
+
const { key, sectionKey, sectionLabel, title, subtitle } = item;
|
|
547
|
+
const isActive = index === activeIndexInRange;
|
|
548
|
+
const previousItem = visibleItems[index - 1];
|
|
549
|
+
return /* @__PURE__ */ jsxs("div", { children: [hasItemSections && Boolean(sectionLabel?.trim()) && previousItem?.sectionKey !== sectionKey ? /* @__PURE__ */ jsx("div", {
|
|
550
|
+
className: "px-1.5 pb-1 pt-1.5 text-[11px] font-semibold uppercase tracking-wide text-gray-500",
|
|
551
|
+
children: sectionLabel
|
|
552
|
+
}) : null, /* @__PURE__ */ jsxs("button", {
|
|
553
|
+
type: "button",
|
|
554
|
+
role: "option",
|
|
555
|
+
"aria-selected": isActive,
|
|
556
|
+
"data-input-surface-index": index,
|
|
557
|
+
onPointerMove: (event) => {
|
|
558
|
+
if (event.pointerType !== "touch") setActiveIndexForCurrentItems(index);
|
|
559
|
+
},
|
|
560
|
+
onPointerDown: (event) => {
|
|
561
|
+
if (event.button > 0) return;
|
|
562
|
+
event.preventDefault();
|
|
563
|
+
onSelectItem(item);
|
|
564
|
+
},
|
|
565
|
+
onClick: (event) => {
|
|
566
|
+
if (event.detail === 0) onSelectItem(item);
|
|
567
|
+
},
|
|
568
|
+
className: `flex w-full items-center gap-1.5 rounded-md px-1.5 py-1 text-left leading-4 transition-colors ${isActive ? "bg-gray-100 text-gray-900" : "text-gray-700 hover:bg-gray-100"}`,
|
|
569
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
570
|
+
className: "truncate text-xs font-medium",
|
|
571
|
+
children: title
|
|
572
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
573
|
+
className: "truncate text-xs text-gray-500",
|
|
574
|
+
children: subtitle
|
|
575
|
+
})]
|
|
576
|
+
})] }, key);
|
|
577
|
+
}) })] })
|
|
584
578
|
})]
|
|
585
579
|
}), /* @__PURE__ */ jsx("div", {
|
|
586
|
-
className: "custom-scrollbar min-h-0 min-w-0 overflow-y-auto overscroll-contain p-2.5",
|
|
580
|
+
className: "custom-scrollbar min-h-0 min-w-0 select-text overflow-y-auto overscroll-contain p-2.5",
|
|
587
581
|
onPointerDown: onDetailsPointerDown,
|
|
588
582
|
children: activeItem ? /* @__PURE__ */ jsxs("div", {
|
|
589
583
|
className: "space-y-3",
|
|
@@ -640,15 +634,13 @@ function resolveInputSurfaceTriggerIdentity(currentIdentity, trigger, reason) {
|
|
|
640
634
|
function ChatInputSurfaceHost(props) {
|
|
641
635
|
const { children, inputSurface, onInputSurfaceTriggerChange, onSelectItem, triggerSpecs = [CHAT_INPUT_SURFACE_SLASH_TRIGGER_SPEC] } = props;
|
|
642
636
|
const menuRef = useRef(null);
|
|
637
|
+
const isDetailsInteractionRef = useRef(false);
|
|
643
638
|
const [activeTriggerIdentity, setActiveTriggerIdentity] = useState(null);
|
|
644
639
|
const isOpen = Boolean(inputSurface) && activeTriggerIdentity !== null;
|
|
645
640
|
const setActiveTrigger = useCallback((identity, trigger) => {
|
|
646
641
|
setActiveTriggerIdentity(identity);
|
|
647
642
|
onInputSurfaceTriggerChange?.(identity ? trigger : null);
|
|
648
643
|
}, [onInputSurfaceTriggerChange]);
|
|
649
|
-
const closeInputSurface = useCallback(() => {
|
|
650
|
-
setActiveTrigger(null, null);
|
|
651
|
-
}, [setActiveTrigger]);
|
|
652
644
|
const handleInputSurfaceSnapshotChange = useCallback((nodes, selection, reason) => {
|
|
653
645
|
const trigger = resolveChatComposerActiveInputSurfaceTrigger(nodes, selection, triggerSpecs);
|
|
654
646
|
const nextIdentity = resolveInputSurfaceTriggerIdentity(activeTriggerIdentity, trigger, reason);
|
|
@@ -662,8 +654,8 @@ function ChatInputSurfaceHost(props) {
|
|
|
662
654
|
return menuRef.current?.handleKeyDown(event) ?? false;
|
|
663
655
|
}, []);
|
|
664
656
|
const handleInputSurfaceOpenChange = useCallback((open) => {
|
|
665
|
-
if (!open)
|
|
666
|
-
}, [
|
|
657
|
+
if (!open && !isDetailsInteractionRef.current) setActiveTrigger(null, null);
|
|
658
|
+
}, [setActiveTrigger]);
|
|
667
659
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [children(useMemo(() => ({
|
|
668
660
|
onInputSurfaceKeyDown: handleInputSurfaceKeyDown,
|
|
669
661
|
onInputSurfaceOpenChange: handleInputSurfaceOpenChange,
|
|
@@ -680,11 +672,16 @@ function ChatInputSurfaceHost(props) {
|
|
|
680
672
|
items: inputSurface.items,
|
|
681
673
|
texts: inputSurface.texts,
|
|
682
674
|
onSelectItem: (item) => {
|
|
683
|
-
|
|
675
|
+
setActiveTrigger(null, null);
|
|
684
676
|
onSelectItem(item);
|
|
685
677
|
},
|
|
686
678
|
onOpenChange: handleInputSurfaceOpenChange,
|
|
687
|
-
onDetailsPointerDown: (
|
|
679
|
+
onDetailsPointerDown: () => {
|
|
680
|
+
isDetailsInteractionRef.current = true;
|
|
681
|
+
requestAnimationFrame(() => {
|
|
682
|
+
isDetailsInteractionRef.current = false;
|
|
683
|
+
});
|
|
684
|
+
}
|
|
688
685
|
}, activeTriggerIdentity) : null] });
|
|
689
686
|
}
|
|
690
687
|
//#endregion
|
|
@@ -727,12 +724,6 @@ const ChatButton = React.forwardRef(({ className, variant, size, ...props }, ref
|
|
|
727
724
|
ChatButton.displayName = "ChatButton";
|
|
728
725
|
//#endregion
|
|
729
726
|
//#region src/components/chat/ui/chat-input-bar/chat-input-bar-actions.tsx
|
|
730
|
-
const SEND_ERROR_PREVIEW_MAX_CHARS = 120;
|
|
731
|
-
function buildSendErrorPreview(value) {
|
|
732
|
-
const compact = value.replace(/\s+/g, " ").trim();
|
|
733
|
-
if (compact.length <= SEND_ERROR_PREVIEW_MAX_CHARS) return compact;
|
|
734
|
-
return `${compact.slice(0, SEND_ERROR_PREVIEW_MAX_CHARS - 1)}…`;
|
|
735
|
-
}
|
|
736
727
|
function StopIcon() {
|
|
737
728
|
return /* @__PURE__ */ jsx("span", {
|
|
738
729
|
"aria-hidden": "true",
|
|
@@ -782,75 +773,42 @@ function ContextWindowIndicator({ contextWindow }) {
|
|
|
782
773
|
})] })
|
|
783
774
|
});
|
|
784
775
|
}
|
|
785
|
-
function ChatInputBarActions({
|
|
786
|
-
const {
|
|
787
|
-
const normalizedSendError = sendError?.trim() ?? "";
|
|
788
|
-
const sendErrorPreview = normalizedSendError ? buildSendErrorPreview(normalizedSendError) : "";
|
|
789
|
-
const resolvedSendErrorDetailsLabel = sendErrorDetailsLabel?.trim() || "Details";
|
|
776
|
+
function ChatInputBarActions({ isSending, canStopGeneration, sendDisabled, stopDisabled, stopHint, sendButtonLabel, stopButtonLabel, contextWindow, onSend, onStop }) {
|
|
777
|
+
const { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } = ChatUiPrimitives;
|
|
790
778
|
return /* @__PURE__ */ jsxs("div", {
|
|
791
|
-
className: "flex shrink-0
|
|
792
|
-
children: [
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
className: "w-[min(32rem,calc(100vw-1.5rem))] border-red-100/80 p-0",
|
|
811
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
812
|
-
className: "border-b border-red-100 bg-red-50/80 px-4 py-2 text-xs font-semibold text-red-700",
|
|
813
|
-
children: resolvedSendErrorDetailsLabel
|
|
814
|
-
}), /* @__PURE__ */ jsx("pre", {
|
|
815
|
-
className: "max-h-80 overflow-auto whitespace-pre-wrap break-words px-4 py-3 text-xs leading-relaxed text-red-700",
|
|
816
|
-
children: normalizedSendError
|
|
817
|
-
})]
|
|
818
|
-
})] })]
|
|
819
|
-
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
820
|
-
className: "flex items-center gap-2",
|
|
821
|
-
children: [contextWindow ? /* @__PURE__ */ jsx(ContextWindowIndicator, { contextWindow }) : null, !isSending || !sendDisabled ? /* @__PURE__ */ jsx(ChatButton, {
|
|
822
|
-
size: "icon",
|
|
823
|
-
className: "h-8 w-8 rounded-full",
|
|
824
|
-
"aria-label": sendButtonLabel,
|
|
825
|
-
onClick: () => void onSend(),
|
|
826
|
-
disabled: sendDisabled,
|
|
827
|
-
children: /* @__PURE__ */ jsx(ArrowUp, { className: "h-5 w-5" })
|
|
828
|
-
}) : canStopGeneration ? /* @__PURE__ */ jsx(ChatButton, {
|
|
779
|
+
className: "flex shrink-0 items-center gap-2",
|
|
780
|
+
children: [contextWindow ? /* @__PURE__ */ jsx(ContextWindowIndicator, { contextWindow }) : null, !isSending || !sendDisabled ? /* @__PURE__ */ jsx(ChatButton, {
|
|
781
|
+
size: "icon",
|
|
782
|
+
className: "h-8 w-8 rounded-full",
|
|
783
|
+
"aria-label": sendButtonLabel,
|
|
784
|
+
onClick: () => void onSend(),
|
|
785
|
+
disabled: sendDisabled,
|
|
786
|
+
children: /* @__PURE__ */ jsx(ArrowUp, { className: "h-5 w-5" })
|
|
787
|
+
}) : canStopGeneration ? /* @__PURE__ */ jsx(ChatButton, {
|
|
788
|
+
size: "icon",
|
|
789
|
+
variant: "outline",
|
|
790
|
+
className: "h-8 w-8 rounded-full",
|
|
791
|
+
"aria-label": stopButtonLabel,
|
|
792
|
+
onClick: () => void onStop(),
|
|
793
|
+
disabled: stopDisabled,
|
|
794
|
+
children: /* @__PURE__ */ jsx(StopIcon, {})
|
|
795
|
+
}) : /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
796
|
+
asChild: true,
|
|
797
|
+
children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(ChatButton, {
|
|
829
798
|
size: "icon",
|
|
830
799
|
variant: "outline",
|
|
831
800
|
className: "h-8 w-8 rounded-full",
|
|
832
801
|
"aria-label": stopButtonLabel,
|
|
833
|
-
|
|
834
|
-
disabled: stopDisabled,
|
|
802
|
+
disabled: true,
|
|
835
803
|
children: /* @__PURE__ */ jsx(StopIcon, {})
|
|
836
|
-
})
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
children: /* @__PURE__ */ jsx(StopIcon, {})
|
|
845
|
-
}) })
|
|
846
|
-
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
847
|
-
side: "top",
|
|
848
|
-
children: /* @__PURE__ */ jsx("p", {
|
|
849
|
-
className: "text-xs",
|
|
850
|
-
children: stopHint
|
|
851
|
-
})
|
|
852
|
-
})] }) })]
|
|
853
|
-
})]
|
|
804
|
+
}) })
|
|
805
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
806
|
+
side: "top",
|
|
807
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
808
|
+
className: "text-xs",
|
|
809
|
+
children: stopHint
|
|
810
|
+
})
|
|
811
|
+
})] }) })]
|
|
854
812
|
});
|
|
855
813
|
}
|
|
856
814
|
//#endregion
|
|
@@ -1160,16 +1118,16 @@ function ToolbarSearchableSelect({ item }) {
|
|
|
1160
1118
|
children: [/* @__PURE__ */ jsx(ToolbarSelectTriggerContent, { item }), /* @__PURE__ */ jsx(ChevronDown, { className: "ml-1 h-3.5 w-3.5 shrink-0 text-muted-foreground/70 [@container_nextclaw-chat-input-bar_(max-width:440px)]:hidden" })]
|
|
1161
1119
|
})
|
|
1162
1120
|
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
1163
|
-
className: `flex flex-col overflow-hidden p-
|
|
1121
|
+
className: `flex flex-col overflow-hidden p-1.5 ${CONTENT_WIDTH_BY_KEY[item.key] ?? ""}`,
|
|
1164
1122
|
style: { maxHeight: TOOLBAR_POPOVER_MAX_HEIGHT },
|
|
1165
1123
|
children: [
|
|
1166
1124
|
/* @__PURE__ */ jsxs("div", {
|
|
1167
|
-
className: "relative mb-
|
|
1125
|
+
className: "relative mb-1.5 shrink-0",
|
|
1168
1126
|
children: [/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/70" }), /* @__PURE__ */ jsx(Input, {
|
|
1169
1127
|
value: query,
|
|
1170
1128
|
onChange: (event) => setQuery(event.currentTarget.value),
|
|
1171
1129
|
placeholder: item.search?.placeholder ?? item.placeholder,
|
|
1172
|
-
className: "h-
|
|
1130
|
+
className: "h-7 rounded-md pl-8 text-xs"
|
|
1173
1131
|
})]
|
|
1174
1132
|
}),
|
|
1175
1133
|
!hasOptions ? item.loading ? /* @__PURE__ */ jsxs("div", {
|
|
@@ -1190,9 +1148,9 @@ function ToolbarSearchableSelect({ item }) {
|
|
|
1190
1148
|
/* @__PURE__ */ jsx("div", {
|
|
1191
1149
|
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
|
|
1192
1150
|
children: filteredGroups.map((group, groupIndex) => /* @__PURE__ */ jsxs("div", {
|
|
1193
|
-
className: groupIndex > 0 ? "border-t border-border pt-
|
|
1151
|
+
className: groupIndex > 0 ? "border-t border-border pt-0.5" : void 0,
|
|
1194
1152
|
children: [group.label ? /* @__PURE__ */ jsx("div", {
|
|
1195
|
-
className: "px-2
|
|
1153
|
+
className: "px-2 pb-0.5 pt-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted-foreground",
|
|
1196
1154
|
children: group.label
|
|
1197
1155
|
}) : null, group.options.map((option) => {
|
|
1198
1156
|
const isSelected = item.value === option.value;
|
|
@@ -1202,7 +1160,7 @@ function ToolbarSearchableSelect({ item }) {
|
|
|
1202
1160
|
className: "group flex items-center gap-1 rounded-md hover:bg-accent",
|
|
1203
1161
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
1204
1162
|
type: "button",
|
|
1205
|
-
className: "flex min-w-0 flex-1 items-center gap-
|
|
1163
|
+
className: "flex min-w-0 flex-1 items-center gap-1.5 rounded-md px-2 py-1.5 text-left leading-4",
|
|
1206
1164
|
onClick: () => {
|
|
1207
1165
|
item.onValueChange(option.value);
|
|
1208
1166
|
setOpen(false);
|
|
@@ -1269,7 +1227,7 @@ function ToolbarSelect({ item }) {
|
|
|
1269
1227
|
children: item.emptyLabel
|
|
1270
1228
|
}) : null : null, groups.map((group, groupIndex) => /* @__PURE__ */ jsxs("div", { children: [groupIndex > 0 ? /* @__PURE__ */ jsx(SelectSeparator, {}) : null, /* @__PURE__ */ jsxs(SelectGroup, { children: [group.label ? /* @__PURE__ */ jsx(SelectLabel, { children: group.label }) : null, group.options.map((option) => /* @__PURE__ */ jsx(SelectItem, {
|
|
1271
1229
|
value: option.value,
|
|
1272
|
-
className: "py-
|
|
1230
|
+
className: "py-1.5",
|
|
1273
1231
|
children: /* @__PURE__ */ jsx(ToolbarSelectOptionContent, { option })
|
|
1274
1232
|
}, option.value))] })] }, group.key))]
|
|
1275
1233
|
})]
|
|
@@ -2294,6 +2252,11 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
2294
2252
|
ChatInputBarTokenizedComposer.displayName = "LexicalChatInputBarTokenizedComposer";
|
|
2295
2253
|
//#endregion
|
|
2296
2254
|
//#region src/components/chat/ui/chat-input-bar/chat-input-bar.tsx
|
|
2255
|
+
const SEND_ERROR_PREVIEW_MAX_CHARS = 120;
|
|
2256
|
+
function buildSendErrorPreview(value) {
|
|
2257
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
2258
|
+
return compact.length <= SEND_ERROR_PREVIEW_MAX_CHARS ? compact : `${compact.slice(0, SEND_ERROR_PREVIEW_MAX_CHARS - 1)}…`;
|
|
2259
|
+
}
|
|
2297
2260
|
function InputBarHint({ hint }) {
|
|
2298
2261
|
if (!hint) return null;
|
|
2299
2262
|
if (hint.loading) return /* @__PURE__ */ jsx("div", {
|
|
@@ -2316,7 +2279,44 @@ function InputBarHint({ hint }) {
|
|
|
2316
2279
|
})
|
|
2317
2280
|
});
|
|
2318
2281
|
}
|
|
2319
|
-
|
|
2282
|
+
function ChatInputBarSendError({ sendError, sendErrorDetailsLabel }) {
|
|
2283
|
+
const normalizedSendError = sendError?.trim() ?? "";
|
|
2284
|
+
if (!normalizedSendError) return null;
|
|
2285
|
+
const { Popover, PopoverContent, PopoverTrigger } = ChatUiPrimitives;
|
|
2286
|
+
const sendErrorPreview = buildSendErrorPreview(normalizedSendError);
|
|
2287
|
+
const resolvedSendErrorDetailsLabel = sendErrorDetailsLabel?.trim() || "Details";
|
|
2288
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2289
|
+
className: "px-3 pb-2 sm:px-4",
|
|
2290
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2291
|
+
"aria-live": "polite",
|
|
2292
|
+
className: "flex min-w-0 items-start justify-between gap-3 rounded-lg border border-red-200/80 bg-red-50/80 px-3 py-2 text-xs text-red-700",
|
|
2293
|
+
role: "status",
|
|
2294
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2295
|
+
className: "min-w-0 flex-1 truncate leading-5",
|
|
2296
|
+
title: normalizedSendError,
|
|
2297
|
+
children: sendErrorPreview
|
|
2298
|
+
}), /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
2299
|
+
asChild: true,
|
|
2300
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
2301
|
+
type: "button",
|
|
2302
|
+
className: "inline-flex h-7 shrink-0 items-center rounded-md border border-red-200/80 bg-background px-2 text-xs font-medium text-red-700 transition-colors hover:bg-red-100",
|
|
2303
|
+
children: resolvedSendErrorDetailsLabel
|
|
2304
|
+
})
|
|
2305
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
2306
|
+
align: "end",
|
|
2307
|
+
className: "w-[min(32rem,calc(100vw-1.5rem))] border-red-100/80 p-0",
|
|
2308
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2309
|
+
className: "border-b border-red-100 bg-red-50/80 px-4 py-2 text-xs font-semibold text-red-700",
|
|
2310
|
+
children: resolvedSendErrorDetailsLabel
|
|
2311
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
2312
|
+
className: "max-h-80 overflow-auto whitespace-pre-wrap break-words px-4 py-3 text-xs leading-relaxed text-red-700",
|
|
2313
|
+
children: normalizedSendError
|
|
2314
|
+
})]
|
|
2315
|
+
})] })]
|
|
2316
|
+
})
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSurface, sendError, sendErrorDetailsLabel, slashMenu, surface, toolbar: toolbarProps, topSlot }, ref) {
|
|
2320
2320
|
const composerRef = useRef(null);
|
|
2321
2321
|
const resolvedInputSurface = inputSurface ?? (slashMenu ? {
|
|
2322
2322
|
isLoading: slashMenu.isLoading,
|
|
@@ -2384,6 +2384,10 @@ const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSur
|
|
|
2384
2384
|
})
|
|
2385
2385
|
}),
|
|
2386
2386
|
/* @__PURE__ */ jsx(InputBarHint, { hint }),
|
|
2387
|
+
/* @__PURE__ */ jsx(ChatInputBarSendError, {
|
|
2388
|
+
sendError,
|
|
2389
|
+
sendErrorDetailsLabel
|
|
2390
|
+
}),
|
|
2387
2391
|
/* @__PURE__ */ jsx(ChatInputBarToolbar, { ...toolbar })
|
|
2388
2392
|
]
|
|
2389
2393
|
})
|
|
@@ -2393,22 +2397,25 @@ const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSur
|
|
|
2393
2397
|
ChatInputBar.displayName = "ChatInputBar";
|
|
2394
2398
|
//#endregion
|
|
2395
2399
|
//#region src/components/chat/ui/chat-message-list/chat-message-avatar.tsx
|
|
2396
|
-
function ChatMessageAvatar({ role }) {
|
|
2400
|
+
function ChatMessageAvatar({ role, size = "default" }) {
|
|
2401
|
+
const compact = size === "compact";
|
|
2402
|
+
const frameSize = compact ? "h-7 w-7" : "h-8 w-8";
|
|
2403
|
+
const iconSize = compact ? "h-3.5 w-3.5" : "h-4 w-4";
|
|
2397
2404
|
if (role === "user") return /* @__PURE__ */ jsx("div", {
|
|
2398
2405
|
"data-testid": "chat-message-avatar-user",
|
|
2399
|
-
className: "flex
|
|
2400
|
-
children: /* @__PURE__ */ jsx(User, { className:
|
|
2406
|
+
className: cn("flex shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm", frameSize),
|
|
2407
|
+
children: /* @__PURE__ */ jsx(User, { className: iconSize })
|
|
2401
2408
|
});
|
|
2402
2409
|
if (role === "tool") return /* @__PURE__ */ jsx("div", {
|
|
2403
2410
|
"data-testid": "chat-message-avatar-tool",
|
|
2404
|
-
className: "flex
|
|
2405
|
-
children: /* @__PURE__ */ jsx(Wrench, { className:
|
|
2411
|
+
className: cn("flex shrink-0 items-center justify-center rounded-full bg-accent text-accent-foreground shadow-sm ring-1 ring-border", frameSize),
|
|
2412
|
+
children: /* @__PURE__ */ jsx(Wrench, { className: iconSize })
|
|
2406
2413
|
});
|
|
2407
2414
|
return /* @__PURE__ */ jsx("div", {
|
|
2408
2415
|
"data-testid": "chat-message-avatar-assistant",
|
|
2409
|
-
className: "flex
|
|
2416
|
+
className: cn("flex shrink-0 items-center justify-center rounded-full bg-muted text-foreground shadow-sm ring-1 ring-border", frameSize),
|
|
2410
2417
|
children: /* @__PURE__ */ jsx(Bot, {
|
|
2411
|
-
className: "h-[18px] w-[18px] text-current",
|
|
2418
|
+
className: cn(compact ? "h-4 w-4" : "h-[18px] w-[18px]", "text-current"),
|
|
2412
2419
|
strokeWidth: 2.5
|
|
2413
2420
|
})
|
|
2414
2421
|
});
|
|
@@ -2929,26 +2936,126 @@ function ChatInlineDisplay({ display, renderInlineDisplay }) {
|
|
|
2929
2936
|
return /* @__PURE__ */ jsx(ChatInlineDisplayFallback, { display });
|
|
2930
2937
|
}
|
|
2931
2938
|
//#endregion
|
|
2932
|
-
//#region src/components/chat/ui/chat-message-list/chat-message-
|
|
2933
|
-
|
|
2939
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-file/chat-message-image-preview.tsx
|
|
2940
|
+
function ChatMessageImageLightbox({ alt, closeLabel, onClose, src }) {
|
|
2941
|
+
const titleId = useId();
|
|
2942
|
+
useEffect(() => {
|
|
2943
|
+
const previousOverflow = document.body.style.overflow;
|
|
2944
|
+
document.body.style.overflow = "hidden";
|
|
2945
|
+
const onKeyDown = (event) => {
|
|
2946
|
+
if (event.key === "Escape") {
|
|
2947
|
+
event.preventDefault();
|
|
2948
|
+
onClose();
|
|
2949
|
+
}
|
|
2950
|
+
};
|
|
2951
|
+
window.addEventListener("keydown", onKeyDown);
|
|
2952
|
+
return () => {
|
|
2953
|
+
document.body.style.overflow = previousOverflow;
|
|
2954
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
2955
|
+
};
|
|
2956
|
+
}, [onClose]);
|
|
2957
|
+
return createPortal(/* @__PURE__ */ jsxs("div", {
|
|
2958
|
+
role: "dialog",
|
|
2959
|
+
"aria-modal": "true",
|
|
2960
|
+
"aria-labelledby": titleId,
|
|
2961
|
+
className: "fixed inset-0 z-[var(--z-modal,10050)] flex items-center justify-center bg-black/80 p-4 backdrop-blur-[2px]",
|
|
2962
|
+
"data-testid": "chat-message-image-lightbox",
|
|
2963
|
+
onClick: onClose,
|
|
2964
|
+
children: [
|
|
2965
|
+
/* @__PURE__ */ jsx("span", {
|
|
2966
|
+
id: titleId,
|
|
2967
|
+
className: "sr-only",
|
|
2968
|
+
children: alt
|
|
2969
|
+
}),
|
|
2970
|
+
/* @__PURE__ */ jsx("button", {
|
|
2971
|
+
type: "button",
|
|
2972
|
+
"aria-label": closeLabel,
|
|
2973
|
+
className: "absolute right-4 top-4 inline-flex h-9 w-9 items-center justify-center rounded-full bg-black/55 text-white transition-colors hover:bg-black/70 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-white/70",
|
|
2974
|
+
onClick: (event) => {
|
|
2975
|
+
event.stopPropagation();
|
|
2976
|
+
onClose();
|
|
2977
|
+
},
|
|
2978
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
2979
|
+
className: "h-4 w-4",
|
|
2980
|
+
strokeWidth: 2
|
|
2981
|
+
})
|
|
2982
|
+
}),
|
|
2983
|
+
/* @__PURE__ */ jsx("img", {
|
|
2984
|
+
src,
|
|
2985
|
+
alt,
|
|
2986
|
+
className: "max-h-[min(92vh,100%)] max-w-[min(96vw,100%)] object-contain shadow-2xl",
|
|
2987
|
+
onClick: (event) => event.stopPropagation()
|
|
2988
|
+
})
|
|
2989
|
+
]
|
|
2990
|
+
}), document.body);
|
|
2991
|
+
}
|
|
2992
|
+
function ChatMessageImagePreview({ alt, expandLabel, closeLabel, sizeLabel, src }) {
|
|
2993
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
2994
|
+
const openLightbox = () => setIsExpanded(true);
|
|
2995
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
2996
|
+
"data-chat-message-image-preview": true,
|
|
2997
|
+
className: "group/image relative block w-fit max-w-[min(100%,32rem)] overflow-hidden rounded-lg",
|
|
2998
|
+
children: [
|
|
2999
|
+
/* @__PURE__ */ jsx("button", {
|
|
3000
|
+
type: "button",
|
|
3001
|
+
className: "block w-fit max-w-[min(100%,32rem)] text-left focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
|
|
3002
|
+
onClick: openLightbox,
|
|
3003
|
+
onDoubleClick: (event) => {
|
|
3004
|
+
event.preventDefault();
|
|
3005
|
+
openLightbox();
|
|
3006
|
+
},
|
|
3007
|
+
"aria-label": expandLabel,
|
|
3008
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
3009
|
+
src,
|
|
3010
|
+
alt,
|
|
3011
|
+
className: "block h-auto w-auto max-h-[26rem] max-w-full rounded-lg bg-transparent object-contain"
|
|
3012
|
+
})
|
|
3013
|
+
}),
|
|
3014
|
+
/* @__PURE__ */ jsx("button", {
|
|
3015
|
+
type: "button",
|
|
3016
|
+
"aria-label": expandLabel,
|
|
3017
|
+
className: "absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md bg-black/45 text-white opacity-0 transition-opacity duration-150 hover:bg-black/60 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-white/70 group-hover/image:opacity-100",
|
|
3018
|
+
onClick: (event) => {
|
|
3019
|
+
event.preventDefault();
|
|
3020
|
+
event.stopPropagation();
|
|
3021
|
+
openLightbox();
|
|
3022
|
+
},
|
|
3023
|
+
children: /* @__PURE__ */ jsx(Expand, {
|
|
3024
|
+
className: "h-3.5 w-3.5",
|
|
3025
|
+
strokeWidth: 2
|
|
3026
|
+
})
|
|
3027
|
+
}),
|
|
3028
|
+
sizeLabel ? /* @__PURE__ */ jsx("span", {
|
|
3029
|
+
className: "pointer-events-none absolute inset-x-0 bottom-0 flex items-end justify-start bg-gradient-to-t from-black/50 via-black/15 to-transparent px-2.5 pb-2 pt-8 opacity-0 transition-opacity duration-150 group-hover/image:opacity-100 group-focus-within/image:opacity-100",
|
|
3030
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
3031
|
+
className: "inline-flex items-center rounded-md bg-black/40 px-1.5 py-0.5 text-[10px] font-medium text-white/95 backdrop-blur-sm",
|
|
3032
|
+
children: sizeLabel
|
|
3033
|
+
})
|
|
3034
|
+
}) : null
|
|
3035
|
+
]
|
|
3036
|
+
}), isExpanded ? /* @__PURE__ */ jsx(ChatMessageImageLightbox, {
|
|
3037
|
+
alt,
|
|
3038
|
+
closeLabel,
|
|
3039
|
+
onClose: () => setIsExpanded(false),
|
|
3040
|
+
src
|
|
3041
|
+
}) : null] });
|
|
3042
|
+
}
|
|
3043
|
+
//#endregion
|
|
3044
|
+
//#region src/components/chat/ui/chat-message-list/utils/chat-local-resource.utils.ts
|
|
2934
3045
|
const SAFE_LINK_PROTOCOLS = new Set([
|
|
2935
3046
|
"http:",
|
|
2936
3047
|
"https:",
|
|
2937
3048
|
"mailto:",
|
|
2938
3049
|
"tel:"
|
|
2939
3050
|
]);
|
|
2940
|
-
const
|
|
2941
|
-
const
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
const PROJECT_RELATIVE_FILE_HREF_PATTERN = new RegExp(`^(?![a-zA-Z][a-zA-Z\\d+.-]*:)(?!//)(?:(?:[^/\\s?#]+/)+[^?#\\s]+\\.[A-Za-z0-9_-]+|[^/\\s?#]+\\.(?:cjs|css|cts|html?|js|json|jsx|mdx?|mjs|mts|tsx?|txt|ya?ml))(?::\\d+(?::\\d+)?)?(?:[?#].*)?$`, "i");
|
|
2945
|
-
function trimMarkdown(value) {
|
|
2946
|
-
if (value.length <= MARKDOWN_MAX_CHARS) return value;
|
|
2947
|
-
return `${value.slice(0, MARKDOWN_MAX_CHARS)}\n\n...`;
|
|
3051
|
+
const URI_SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z\d+.-]*:/;
|
|
3052
|
+
const WINDOWS_ABSOLUTE_PATH_PATTERN = /^[a-z]:[\\/]/i;
|
|
3053
|
+
function isSchemeLessResourceHref(href) {
|
|
3054
|
+
return !href.startsWith("//") && !URI_SCHEME_PATTERN.test(href);
|
|
2948
3055
|
}
|
|
2949
|
-
function
|
|
3056
|
+
function resolveSafeChatResourceHref(href) {
|
|
2950
3057
|
if (!href) return null;
|
|
2951
|
-
if (
|
|
3058
|
+
if (WINDOWS_ABSOLUTE_PATH_PATTERN.test(href) || isSchemeLessResourceHref(href)) return href;
|
|
2952
3059
|
try {
|
|
2953
3060
|
const url = new URL(href);
|
|
2954
3061
|
return SAFE_LINK_PROTOCOLS.has(url.protocol) ? href : null;
|
|
@@ -2956,30 +3063,57 @@ function resolveSafeHref(href) {
|
|
|
2956
3063
|
return null;
|
|
2957
3064
|
}
|
|
2958
3065
|
}
|
|
2959
|
-
function
|
|
3066
|
+
function isExternalChatResourceHref(href) {
|
|
2960
3067
|
return /^https?:\/\//i.test(href);
|
|
2961
3068
|
}
|
|
2962
|
-
function
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
3069
|
+
function parseChatLocalFileAction(href) {
|
|
3070
|
+
const normalizedHref = href.split("#")[0] ?? href;
|
|
3071
|
+
const [encodedPath, encodedQuery = ""] = normalizedHref.split("?", 2);
|
|
3072
|
+
let path;
|
|
3073
|
+
try {
|
|
3074
|
+
path = decodeURIComponent(encodedPath ?? normalizedHref);
|
|
3075
|
+
} catch {
|
|
3076
|
+
return null;
|
|
3077
|
+
}
|
|
3078
|
+
if (!path || path.startsWith("#") || path.startsWith("//") || !WINDOWS_ABSOLUTE_PATH_PATTERN.test(path) && URI_SCHEME_PATTERN.test(path)) return null;
|
|
3079
|
+
const viewer = new URLSearchParams(encodedQuery).get("viewer");
|
|
3080
|
+
const lineMatch = /^(.*?)(?::(\d+)(?::(\d+))?)$/.exec(path);
|
|
3081
|
+
const rawPath = lineMatch?.[1] ?? path;
|
|
2972
3082
|
const line = lineMatch?.[2] ? Number(lineMatch[2]) : void 0;
|
|
2973
3083
|
const column = lineMatch?.[3] ? Number(lineMatch[3]) : void 0;
|
|
2974
3084
|
return {
|
|
2975
3085
|
path: rawPath,
|
|
2976
|
-
label: rawPath.split(
|
|
3086
|
+
label: rawPath.split(/[\\/]/).filter(Boolean).pop() ?? rawPath,
|
|
2977
3087
|
viewMode: "preview",
|
|
2978
3088
|
...viewer === "source" || viewer === "rendered" ? { previewViewer: viewer } : {},
|
|
2979
3089
|
...typeof line === "number" ? { line } : {},
|
|
2980
3090
|
...typeof column === "number" ? { column } : {}
|
|
2981
3091
|
};
|
|
2982
3092
|
}
|
|
3093
|
+
//#endregion
|
|
3094
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-markdown.tsx
|
|
3095
|
+
const MARKDOWN_MAX_CHARS = 14e4;
|
|
3096
|
+
const INLINE_TOKEN_KIND_ATTR = "data-chat-inline-token-kind";
|
|
3097
|
+
const INLINE_TOKEN_KEY_ATTR = "data-chat-inline-token-key";
|
|
3098
|
+
const INLINE_TOKEN_LABEL_ATTR = "data-chat-inline-token-label";
|
|
3099
|
+
const INLINE_TOKEN_RAW_TEXT_ATTR = "data-chat-inline-token-raw-text";
|
|
3100
|
+
function trimMarkdown(value) {
|
|
3101
|
+
if (value.length <= MARKDOWN_MAX_CHARS) return value;
|
|
3102
|
+
return `${value.slice(0, MARKDOWN_MAX_CHARS)}\n\n...`;
|
|
3103
|
+
}
|
|
3104
|
+
function isSingleLineImageParagraph(node) {
|
|
3105
|
+
let imageCount = 0;
|
|
3106
|
+
for (const child of node?.children ?? []) {
|
|
3107
|
+
if (child.type === "text") {
|
|
3108
|
+
const text = child.value ?? "";
|
|
3109
|
+
if (text.trim().length > 0 || text.includes("\n") || text.includes("\r")) return false;
|
|
3110
|
+
continue;
|
|
3111
|
+
}
|
|
3112
|
+
if (child.tagName !== "img") return false;
|
|
3113
|
+
imageCount += 1;
|
|
3114
|
+
}
|
|
3115
|
+
return imageCount >= 3;
|
|
3116
|
+
}
|
|
2983
3117
|
function prepareInlineTokens(inlineTokens) {
|
|
2984
3118
|
if (!inlineTokens || inlineTokens.length === 0) return [];
|
|
2985
3119
|
const seenRawTexts = /* @__PURE__ */ new Set();
|
|
@@ -3069,11 +3203,14 @@ function readStringProp(props, key) {
|
|
|
3069
3203
|
const value = props[key];
|
|
3070
3204
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
3071
3205
|
}
|
|
3072
|
-
function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens, onFileOpen, onInlineTokenClick, renderInlineDisplay }) {
|
|
3206
|
+
function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens, onFileOpen, onInlineTokenClick, resolveFileContentUrl, renderInlineDisplay }) {
|
|
3073
3207
|
const isUser = role === "user";
|
|
3074
3208
|
const remarkPlugins = useMemo(() => inlineTokens && inlineTokens.length > 0 ? [remarkGfm, createRemarkInlineTokenPlugin(inlineTokens)] : [remarkGfm], [inlineTokens]);
|
|
3075
3209
|
const markdownComponents = useMemo(() => ({
|
|
3076
|
-
p: ({ children }) => inline ? /* @__PURE__ */ jsx(Fragment$1, { children }) : /* @__PURE__ */ jsx("p", {
|
|
3210
|
+
p: ({ node, children }) => inline ? /* @__PURE__ */ jsx(Fragment$1, { children }) : /* @__PURE__ */ jsx("p", {
|
|
3211
|
+
"data-chat-image-row": isSingleLineImageParagraph(node) ? "three-column" : void 0,
|
|
3212
|
+
children
|
|
3213
|
+
}),
|
|
3077
3214
|
span: ({ node: _node, children, ...rest }) => {
|
|
3078
3215
|
const restProps = rest;
|
|
3079
3216
|
const kind = readStringProp(restProps, INLINE_TOKEN_KIND_ATTR);
|
|
@@ -3097,14 +3234,14 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens,
|
|
|
3097
3234
|
});
|
|
3098
3235
|
},
|
|
3099
3236
|
a: ({ node: _node, href, children, ...rest }) => {
|
|
3100
|
-
const safeHref =
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
children
|
|
3104
|
-
});
|
|
3105
|
-
const external = isExternalHref(safeHref);
|
|
3106
|
-
const localFileAction = external ? null : parseLocalFileAction(safeHref);
|
|
3237
|
+
const safeHref = resolveSafeChatResourceHref(href);
|
|
3238
|
+
const external = safeHref ? isExternalChatResourceHref(safeHref) : false;
|
|
3239
|
+
const localFileAction = external ? null : safeHref ? parseChatLocalFileAction(safeHref) : null;
|
|
3107
3240
|
const handleClick = (event) => {
|
|
3241
|
+
if (!safeHref) {
|
|
3242
|
+
event.preventDefault();
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3108
3245
|
if (!onFileOpen || !localFileAction) return;
|
|
3109
3246
|
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
3110
3247
|
event.preventDefault();
|
|
@@ -3112,7 +3249,9 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens,
|
|
|
3112
3249
|
};
|
|
3113
3250
|
return /* @__PURE__ */ jsx("a", {
|
|
3114
3251
|
...rest,
|
|
3115
|
-
|
|
3252
|
+
className: cn(rest.className, !safeHref && "chat-link-invalid"),
|
|
3253
|
+
href: safeHref ?? "#",
|
|
3254
|
+
"aria-disabled": !safeHref || void 0,
|
|
3116
3255
|
onClick: handleClick,
|
|
3117
3256
|
target: external ? "_blank" : void 0,
|
|
3118
3257
|
rel: external ? "noreferrer noopener" : void 0,
|
|
@@ -3140,15 +3279,18 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens,
|
|
|
3140
3279
|
className: "chat-task-checkbox"
|
|
3141
3280
|
});
|
|
3142
3281
|
},
|
|
3143
|
-
img: ({ node: _node, src, alt
|
|
3144
|
-
const safeSrc =
|
|
3282
|
+
img: ({ node: _node, src, alt }) => {
|
|
3283
|
+
const safeSrc = resolveSafeChatResourceHref(src);
|
|
3145
3284
|
if (!safeSrc) return null;
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3285
|
+
const localFileAction = parseChatLocalFileAction(safeSrc);
|
|
3286
|
+
const resolvedSrc = localFileAction && resolveFileContentUrl ? resolveFileContentUrl(localFileAction) : safeSrc;
|
|
3287
|
+
if (!resolvedSrc) return null;
|
|
3288
|
+
return /* @__PURE__ */ jsx(ChatMessageImagePreview, {
|
|
3149
3289
|
alt: alt || "",
|
|
3150
|
-
|
|
3151
|
-
|
|
3290
|
+
closeLabel: texts.attachmentCloseLabel ?? "Close preview",
|
|
3291
|
+
expandLabel: texts.attachmentExpandLabel ?? "Expand image",
|
|
3292
|
+
sizeLabel: null,
|
|
3293
|
+
src: resolvedSrc
|
|
3152
3294
|
});
|
|
3153
3295
|
},
|
|
3154
3296
|
code: ({ node: _node, className, children, ...rest }) => {
|
|
@@ -3175,6 +3317,7 @@ function ChatMessageMarkdown({ text, role, texts, inline = false, inlineTokens,
|
|
|
3175
3317
|
onFileOpen,
|
|
3176
3318
|
onInlineTokenClick,
|
|
3177
3319
|
renderInlineDisplay,
|
|
3320
|
+
resolveFileContentUrl,
|
|
3178
3321
|
texts
|
|
3179
3322
|
]);
|
|
3180
3323
|
return /* @__PURE__ */ jsx(inline ? "span" : "div", {
|
|
@@ -3451,109 +3594,6 @@ function FileCategoryGlyph({ category, isUser }) {
|
|
|
3451
3594
|
})
|
|
3452
3595
|
});
|
|
3453
3596
|
}
|
|
3454
|
-
function ChatMessageImageLightbox({ alt, closeLabel, onClose, src }) {
|
|
3455
|
-
const titleId = useId();
|
|
3456
|
-
useEffect(() => {
|
|
3457
|
-
const previousOverflow = document.body.style.overflow;
|
|
3458
|
-
document.body.style.overflow = "hidden";
|
|
3459
|
-
const onKeyDown = (event) => {
|
|
3460
|
-
if (event.key === "Escape") {
|
|
3461
|
-
event.preventDefault();
|
|
3462
|
-
onClose();
|
|
3463
|
-
}
|
|
3464
|
-
};
|
|
3465
|
-
window.addEventListener("keydown", onKeyDown);
|
|
3466
|
-
return () => {
|
|
3467
|
-
document.body.style.overflow = previousOverflow;
|
|
3468
|
-
window.removeEventListener("keydown", onKeyDown);
|
|
3469
|
-
};
|
|
3470
|
-
}, [onClose]);
|
|
3471
|
-
return createPortal(/* @__PURE__ */ jsxs("div", {
|
|
3472
|
-
role: "dialog",
|
|
3473
|
-
"aria-modal": "true",
|
|
3474
|
-
"aria-labelledby": titleId,
|
|
3475
|
-
className: "fixed inset-0 z-[var(--z-modal,10050)] flex items-center justify-center bg-black/80 p-4 backdrop-blur-[2px]",
|
|
3476
|
-
"data-testid": "chat-message-image-lightbox",
|
|
3477
|
-
onClick: onClose,
|
|
3478
|
-
children: [
|
|
3479
|
-
/* @__PURE__ */ jsx("span", {
|
|
3480
|
-
id: titleId,
|
|
3481
|
-
className: "sr-only",
|
|
3482
|
-
children: alt
|
|
3483
|
-
}),
|
|
3484
|
-
/* @__PURE__ */ jsx("button", {
|
|
3485
|
-
type: "button",
|
|
3486
|
-
"aria-label": closeLabel,
|
|
3487
|
-
className: "absolute right-4 top-4 inline-flex h-9 w-9 items-center justify-center rounded-full bg-black/55 text-white transition-colors hover:bg-black/70 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-white/70",
|
|
3488
|
-
onClick: (event) => {
|
|
3489
|
-
event.stopPropagation();
|
|
3490
|
-
onClose();
|
|
3491
|
-
},
|
|
3492
|
-
children: /* @__PURE__ */ jsx(X, {
|
|
3493
|
-
className: "h-4 w-4",
|
|
3494
|
-
strokeWidth: 2
|
|
3495
|
-
})
|
|
3496
|
-
}),
|
|
3497
|
-
/* @__PURE__ */ jsx("img", {
|
|
3498
|
-
src,
|
|
3499
|
-
alt,
|
|
3500
|
-
className: "max-h-[min(92vh,100%)] max-w-[min(96vw,100%)] object-contain shadow-2xl",
|
|
3501
|
-
onClick: (event) => event.stopPropagation()
|
|
3502
|
-
})
|
|
3503
|
-
]
|
|
3504
|
-
}), document.body);
|
|
3505
|
-
}
|
|
3506
|
-
function ChatMessageImagePreview({ expandLabel, closeLabel, file, sizeLabel }) {
|
|
3507
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
3508
|
-
if (!file.dataUrl) return null;
|
|
3509
|
-
const openLightbox = () => setIsExpanded(true);
|
|
3510
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
3511
|
-
className: "group/image relative w-fit max-w-full overflow-hidden rounded-xl",
|
|
3512
|
-
children: [
|
|
3513
|
-
/* @__PURE__ */ jsx("button", {
|
|
3514
|
-
type: "button",
|
|
3515
|
-
className: "block w-fit max-w-full text-left focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
|
|
3516
|
-
onClick: openLightbox,
|
|
3517
|
-
onDoubleClick: (event) => {
|
|
3518
|
-
event.preventDefault();
|
|
3519
|
-
openLightbox();
|
|
3520
|
-
},
|
|
3521
|
-
"aria-label": expandLabel,
|
|
3522
|
-
children: /* @__PURE__ */ jsx("img", {
|
|
3523
|
-
src: file.dataUrl,
|
|
3524
|
-
alt: file.label,
|
|
3525
|
-
className: "block h-auto max-h-[26rem] max-w-full rounded-xl bg-transparent object-contain"
|
|
3526
|
-
})
|
|
3527
|
-
}),
|
|
3528
|
-
/* @__PURE__ */ jsx("button", {
|
|
3529
|
-
type: "button",
|
|
3530
|
-
"aria-label": expandLabel,
|
|
3531
|
-
className: "absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md bg-black/45 text-white opacity-0 transition-opacity duration-150 hover:bg-black/60 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-white/70 group-hover/image:opacity-100",
|
|
3532
|
-
onClick: (event) => {
|
|
3533
|
-
event.preventDefault();
|
|
3534
|
-
event.stopPropagation();
|
|
3535
|
-
openLightbox();
|
|
3536
|
-
},
|
|
3537
|
-
children: /* @__PURE__ */ jsx(Expand, {
|
|
3538
|
-
className: "h-3.5 w-3.5",
|
|
3539
|
-
strokeWidth: 2
|
|
3540
|
-
})
|
|
3541
|
-
}),
|
|
3542
|
-
sizeLabel ? /* @__PURE__ */ jsx("div", {
|
|
3543
|
-
className: "pointer-events-none absolute inset-x-0 bottom-0 flex items-end justify-start bg-gradient-to-t from-black/50 via-black/15 to-transparent px-2.5 pb-2 pt-8 opacity-0 transition-opacity duration-150 group-hover/image:opacity-100 group-focus-within/image:opacity-100",
|
|
3544
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
3545
|
-
className: "inline-flex items-center rounded-md bg-black/40 px-1.5 py-0.5 text-[10px] font-medium text-white/95 backdrop-blur-sm",
|
|
3546
|
-
children: sizeLabel
|
|
3547
|
-
})
|
|
3548
|
-
}) : null
|
|
3549
|
-
]
|
|
3550
|
-
}), isExpanded ? /* @__PURE__ */ jsx(ChatMessageImageLightbox, {
|
|
3551
|
-
alt: file.label,
|
|
3552
|
-
closeLabel,
|
|
3553
|
-
onClose: () => setIsExpanded(false),
|
|
3554
|
-
src: file.dataUrl
|
|
3555
|
-
}) : null] });
|
|
3556
|
-
}
|
|
3557
3597
|
function renderFileCardHeader(params) {
|
|
3558
3598
|
const { category, file, categoryLabel, sizeLabel, isUser, action } = params;
|
|
3559
3599
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -3633,11 +3673,12 @@ function ChatMessageFile({ file, isUser = false, texts, onOpen }) {
|
|
|
3633
3673
|
const closeLabel = texts?.attachmentCloseLabel ?? "Close preview";
|
|
3634
3674
|
const categoryLabel = readFileCategoryLabel(category, texts);
|
|
3635
3675
|
const shellClasses = cn("block overflow-hidden rounded-xl border transition-colors", isUser ? "border-black/8 bg-black/[0.03] text-foreground" : "border-border/70 bg-card text-card-foreground", isInteractive && (isUser ? "hover:bg-black/[0.05]" : "hover:bg-muted/50"));
|
|
3636
|
-
if (renderAsImage) return /* @__PURE__ */ jsx(ChatMessageImagePreview, {
|
|
3676
|
+
if (renderAsImage && file.dataUrl) return /* @__PURE__ */ jsx(ChatMessageImagePreview, {
|
|
3677
|
+
alt: file.label,
|
|
3637
3678
|
expandLabel,
|
|
3638
3679
|
closeLabel,
|
|
3639
|
-
|
|
3640
|
-
|
|
3680
|
+
sizeLabel,
|
|
3681
|
+
src: file.dataUrl
|
|
3641
3682
|
});
|
|
3642
3683
|
if (renderAsAudio || renderAsVideo) return renderInlineMediaCard({
|
|
3643
3684
|
category,
|
|
@@ -3676,6 +3717,54 @@ function ChatMessageFile({ file, isUser = false, texts, onOpen }) {
|
|
|
3676
3717
|
});
|
|
3677
3718
|
}
|
|
3678
3719
|
//#endregion
|
|
3720
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-file/chat-message-image-row.tsx
|
|
3721
|
+
function ChatMessageImageRow({ group, indexOffset, isUser, texts, onOpen }) {
|
|
3722
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3723
|
+
"data-chat-message-image-row": "three-column",
|
|
3724
|
+
"data-chat-message-wide-content": "true",
|
|
3725
|
+
className: "grid w-full grid-cols-3 items-start gap-3",
|
|
3726
|
+
children: group.items.map(({ index, part }) => /* @__PURE__ */ jsx(ChatMessageFile, {
|
|
3727
|
+
file: part.file,
|
|
3728
|
+
isUser,
|
|
3729
|
+
texts,
|
|
3730
|
+
onOpen
|
|
3731
|
+
}, `file-${indexOffset + index}`))
|
|
3732
|
+
});
|
|
3733
|
+
}
|
|
3734
|
+
//#endregion
|
|
3735
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-file/chat-message-image-group.utils.ts
|
|
3736
|
+
function isPreviewableImageBlock(block) {
|
|
3737
|
+
return Boolean(block?.kind === "part" && block.part.type === "file" && block.part.file.isImage && block.part.file.dataUrl);
|
|
3738
|
+
}
|
|
3739
|
+
function groupConsecutiveImageFileBlocks(blocks) {
|
|
3740
|
+
const grouped = [];
|
|
3741
|
+
let index = 0;
|
|
3742
|
+
while (index < blocks.length) {
|
|
3743
|
+
const block = blocks[index];
|
|
3744
|
+
if (!isPreviewableImageBlock(block)) {
|
|
3745
|
+
if (block) grouped.push(block);
|
|
3746
|
+
index += 1;
|
|
3747
|
+
continue;
|
|
3748
|
+
}
|
|
3749
|
+
const startIndex = index;
|
|
3750
|
+
const items = [];
|
|
3751
|
+
while (isPreviewableImageBlock(blocks[index])) {
|
|
3752
|
+
items.push(blocks[index]);
|
|
3753
|
+
index += 1;
|
|
3754
|
+
}
|
|
3755
|
+
if (items.length < 3) {
|
|
3756
|
+
grouped.push(...items);
|
|
3757
|
+
continue;
|
|
3758
|
+
}
|
|
3759
|
+
grouped.push({
|
|
3760
|
+
kind: "image-group",
|
|
3761
|
+
key: `image-group-${startIndex}`,
|
|
3762
|
+
items
|
|
3763
|
+
});
|
|
3764
|
+
}
|
|
3765
|
+
return grouped;
|
|
3766
|
+
}
|
|
3767
|
+
//#endregion
|
|
3679
3768
|
//#region src/components/chat/hooks/use-reasoning-block-open-state.ts
|
|
3680
3769
|
function useReasoningBlockOpenState(params) {
|
|
3681
3770
|
const { isInProgress } = params;
|
|
@@ -3711,7 +3800,6 @@ const DEFAULT_STICKY_THRESHOLD_PX = 10;
|
|
|
3711
3800
|
function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey, scrollRef, stickyThresholdPx }) {
|
|
3712
3801
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
3713
3802
|
const isStickyRef = useRef(true);
|
|
3714
|
-
const isProgrammaticScrollRef = useRef(false);
|
|
3715
3803
|
const previousResetKeyRef = useRef(null);
|
|
3716
3804
|
const pendingInitialScrollRef = useRef(false);
|
|
3717
3805
|
const scheduledScrollFrameRef = useRef(null);
|
|
@@ -3729,7 +3817,6 @@ function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey
|
|
|
3729
3817
|
scheduledScrollFrameRef.current = null;
|
|
3730
3818
|
const currentElement = scrollRef.current;
|
|
3731
3819
|
if (!currentElement) return;
|
|
3732
|
-
isProgrammaticScrollRef.current = true;
|
|
3733
3820
|
if (typeof currentElement.scrollTo === "function") currentElement.scrollTo({
|
|
3734
3821
|
top: currentElement.scrollHeight,
|
|
3735
3822
|
behavior
|
|
@@ -3742,10 +3829,6 @@ function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey
|
|
|
3742
3829
|
queueScrollToBottom();
|
|
3743
3830
|
}, [queueScrollToBottom, updateStickyState]);
|
|
3744
3831
|
const onScroll = useCallback(() => {
|
|
3745
|
-
if (isProgrammaticScrollRef.current) {
|
|
3746
|
-
isProgrammaticScrollRef.current = false;
|
|
3747
|
-
return;
|
|
3748
|
-
}
|
|
3749
3832
|
const element = scrollRef.current;
|
|
3750
3833
|
if (!element) return;
|
|
3751
3834
|
updateStickyState(resolveIsAtBottom(element));
|
|
@@ -3803,15 +3886,23 @@ function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey
|
|
|
3803
3886
|
* Collapsed process/tool rows should feel like ordinary body text lines.
|
|
3804
3887
|
*/
|
|
3805
3888
|
const CHAT_PROCESS_LEADING_COL_CLASS = "inline-flex h-[1.15em] w-[1.15em] shrink-0 items-center justify-center text-current";
|
|
3806
|
-
const CHAT_PROCESS_META_ROW_CLASS = "group/process-row flex w-full min-w-0 items-center gap-1.5 py-0 text-[0.925rem] font-normal leading-[1.72] text-muted-foreground";
|
|
3889
|
+
const CHAT_PROCESS_META_ROW_CLASS = "group/process-row flex w-full min-w-0 items-center gap-1.5 py-0 text-[0.925rem] font-normal leading-[1.72] text-muted-foreground/80";
|
|
3807
3890
|
function ChatProcessLeadingIcon({ children, className }) {
|
|
3808
3891
|
return /* @__PURE__ */ jsx("span", {
|
|
3809
3892
|
className: cn(CHAT_PROCESS_LEADING_COL_CLASS, className),
|
|
3810
3893
|
children
|
|
3811
3894
|
});
|
|
3812
3895
|
}
|
|
3896
|
+
function ChatProcessWorkflowRail({ position }) {
|
|
3897
|
+
return /* @__PURE__ */ jsx("span", {
|
|
3898
|
+
"aria-hidden": "true",
|
|
3899
|
+
"data-tool-workflow-rail": "true",
|
|
3900
|
+
className: cn("pointer-events-none absolute left-[0.575em] w-px -translate-x-1/2 bg-border/70", position === "first" ? "bottom-0 top-[0.86em]" : position === "last" ? "top-0 h-[0.86em]" : "inset-y-0")
|
|
3901
|
+
});
|
|
3902
|
+
}
|
|
3813
3903
|
function ChatProcessMetaRow({ children, className, interactive = false, onClick, role, tabIndex, onKeyDown }) {
|
|
3814
3904
|
return /* @__PURE__ */ jsx("div", {
|
|
3905
|
+
"data-chat-process-meta-row": "true",
|
|
3815
3906
|
className: cn(CHAT_PROCESS_META_ROW_CLASS, interactive ? "cursor-pointer transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35" : null, className),
|
|
3816
3907
|
onClick,
|
|
3817
3908
|
role,
|
|
@@ -3827,22 +3918,33 @@ function ChatProcessMetaRow({ children, className, interactive = false, onClick,
|
|
|
3827
3918
|
* Rendered as a plain button-like row (not <summary>) so browsers never inject
|
|
3828
3919
|
* a default disclosure label such as "详情" / "Details".
|
|
3829
3920
|
*/
|
|
3830
|
-
function ChatCollapsibleMetaSummary({ label, openGroup, open = false, className, labelClassName, onClick }) {
|
|
3921
|
+
function ChatCollapsibleMetaSummary({ label, openGroup, open = false, icon: Icon, className, leadingIconClassName, labelClassName, onClick }) {
|
|
3831
3922
|
return /* @__PURE__ */ jsxs("button", {
|
|
3832
3923
|
type: "button",
|
|
3924
|
+
"data-chat-process-meta-row": "true",
|
|
3833
3925
|
className: cn(CHAT_PROCESS_META_ROW_CLASS, "m-0 w-full border-0 bg-transparent p-0 text-left shadow-none transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35", className),
|
|
3834
3926
|
"aria-expanded": open,
|
|
3835
3927
|
onClick,
|
|
3836
|
-
children: [
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3928
|
+
children: [
|
|
3929
|
+
Icon ? /* @__PURE__ */ jsx(ChatProcessLeadingIcon, {
|
|
3930
|
+
className: cn("relative z-[1] rounded-sm", leadingIconClassName),
|
|
3931
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
3932
|
+
className: "h-[1.05em] w-[1.05em]",
|
|
3933
|
+
strokeWidth: 2.25
|
|
3934
|
+
})
|
|
3935
|
+
}) : null,
|
|
3936
|
+
/* @__PURE__ */ jsx("span", {
|
|
3937
|
+
className: cn("min-w-0 shrink truncate", labelClassName),
|
|
3938
|
+
children: label
|
|
3939
|
+
}),
|
|
3940
|
+
/* @__PURE__ */ jsx("span", {
|
|
3941
|
+
className: cn("inline-flex h-[1.15em] w-[1.15em] shrink-0 items-center justify-center text-muted-foreground/80 transition-opacity", open ? "opacity-100" : "opacity-0 group-hover/process-row:opacity-100 focus-visible:opacity-100"),
|
|
3942
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {
|
|
3943
|
+
className: cn("h-[1.05em] w-[1.05em] transition-transform", open && "rotate-90"),
|
|
3944
|
+
strokeWidth: 2.25
|
|
3945
|
+
})
|
|
3844
3946
|
})
|
|
3845
|
-
|
|
3947
|
+
]
|
|
3846
3948
|
});
|
|
3847
3949
|
}
|
|
3848
3950
|
//#endregion
|
|
@@ -3857,7 +3959,7 @@ function ChatReasoningBlock({ label, text, characterCountTemplates, isUser, isIn
|
|
|
3857
3959
|
const displayLabel = formatReasoningLabel(label, text, isInProgress ? characterCountTemplates?.inProgress : characterCountTemplates?.completed);
|
|
3858
3960
|
const { onScroll } = useStickyBottomScroll({
|
|
3859
3961
|
scrollRef,
|
|
3860
|
-
resetKey:
|
|
3962
|
+
resetKey: "reasoning",
|
|
3861
3963
|
isLoading: false,
|
|
3862
3964
|
hasContent: text.length > 0,
|
|
3863
3965
|
contentVersion: text,
|
|
@@ -3868,6 +3970,8 @@ function ChatReasoningBlock({ label, text, characterCountTemplates, isUser, isIn
|
|
|
3868
3970
|
children: [/* @__PURE__ */ jsx(ChatCollapsibleMetaSummary, {
|
|
3869
3971
|
openGroup: "reasoning",
|
|
3870
3972
|
open: isOpen,
|
|
3973
|
+
icon: Brain,
|
|
3974
|
+
leadingIconClassName: isUser ? "bg-primary" : "bg-card",
|
|
3871
3975
|
label: displayLabel,
|
|
3872
3976
|
labelClassName: isUser ? "text-primary-100" : void 0,
|
|
3873
3977
|
onClick: onSummaryClick
|
|
@@ -3902,6 +4006,38 @@ function ToolCardContent({ children, className }) {
|
|
|
3902
4006
|
children
|
|
3903
4007
|
});
|
|
3904
4008
|
}
|
|
4009
|
+
function ToolCardDetailSection({ label, tone, children }) {
|
|
4010
|
+
const style = {
|
|
4011
|
+
input: {
|
|
4012
|
+
dot: "bg-muted-foreground/60",
|
|
4013
|
+
body: "text-foreground"
|
|
4014
|
+
},
|
|
4015
|
+
output: {
|
|
4016
|
+
dot: "bg-primary/70",
|
|
4017
|
+
body: "text-foreground"
|
|
4018
|
+
},
|
|
4019
|
+
error: {
|
|
4020
|
+
dot: "bg-rose-500/80",
|
|
4021
|
+
body: "text-rose-950/85"
|
|
4022
|
+
}
|
|
4023
|
+
}[tone];
|
|
4024
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
4025
|
+
className: "overflow-hidden rounded-md border border-border/70 bg-muted/20",
|
|
4026
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
4027
|
+
className: "flex items-center gap-2 border-b border-border/60 px-2.5 py-1.5 text-[10px] font-medium tracking-wide text-muted-foreground",
|
|
4028
|
+
children: [/* @__PURE__ */ jsx("span", { className: cn("h-1.5 w-1.5 rounded-full", style.dot) }), /* @__PURE__ */ jsx("span", {
|
|
4029
|
+
className: "normal-case tracking-normal",
|
|
4030
|
+
children: label
|
|
4031
|
+
})]
|
|
4032
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
4033
|
+
className: "w-full overflow-hidden",
|
|
4034
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
4035
|
+
className: cn("w-full max-w-full min-w-0 max-h-64 overflow-x-auto overflow-y-auto px-2.5 py-2 font-mono text-[12px] leading-relaxed whitespace-pre custom-scrollbar", style.body),
|
|
4036
|
+
children
|
|
4037
|
+
})
|
|
4038
|
+
})]
|
|
4039
|
+
});
|
|
4040
|
+
}
|
|
3905
4041
|
//#endregion
|
|
3906
4042
|
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-status.tsx
|
|
3907
4043
|
const STATUS_STYLES = {
|
|
@@ -4803,9 +4939,12 @@ function TerminalExecutionView({ card, toolLabel }) {
|
|
|
4803
4939
|
})] });
|
|
4804
4940
|
}
|
|
4805
4941
|
function FileOperationView({ card, toolLabel, onFileOpen }) {
|
|
4942
|
+
const input = card.input?.trim() ?? "";
|
|
4806
4943
|
const output = card.output?.trim() ?? "";
|
|
4807
4944
|
const isRunning = card.statusTone === "running";
|
|
4808
|
-
const
|
|
4945
|
+
const hasStructuredPreview = Boolean(card.fileOperation?.blocks.length);
|
|
4946
|
+
const showRawInput = Boolean(input) && !hasStructuredPreview;
|
|
4947
|
+
const hasContent = hasStructuredPreview || Boolean(input) || Boolean(output);
|
|
4809
4948
|
const previewBlocks = card.fileOperation?.blocks ?? [];
|
|
4810
4949
|
const previewLineCount = previewBlocks.reduce((count, block) => count + block.lines.length, 0);
|
|
4811
4950
|
const previewCharCount = previewBlocks.reduce((count, block) => {
|
|
@@ -4830,13 +4969,21 @@ function FileOperationView({ card, toolLabel, onFileOpen }) {
|
|
|
4830
4969
|
canExpand: hasContent || isRunning,
|
|
4831
4970
|
hideSummary: false,
|
|
4832
4971
|
onToggle
|
|
4833
|
-
}), expanded && hasContent ? /* @__PURE__ */
|
|
4972
|
+
}), expanded && hasContent ? /* @__PURE__ */ jsxs(ToolCardContent, {
|
|
4834
4973
|
className: "bg-transparent py-0",
|
|
4835
|
-
children:
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4974
|
+
children: [
|
|
4975
|
+
showRawInput ? /* @__PURE__ */ jsx(ToolCardDetailSection, {
|
|
4976
|
+
label: card.inputLabel?.trim() || "Input",
|
|
4977
|
+
tone: "input",
|
|
4978
|
+
children: input
|
|
4979
|
+
}) : null,
|
|
4980
|
+
showRawInput && output ? /* @__PURE__ */ jsx("div", { className: "h-2" }) : null,
|
|
4981
|
+
hasStructuredPreview || output ? /* @__PURE__ */ jsx(ToolCardFileOperationContent, {
|
|
4982
|
+
card,
|
|
4983
|
+
onFileOpen,
|
|
4984
|
+
showPathRow: true
|
|
4985
|
+
}) : null
|
|
4986
|
+
]
|
|
4840
4987
|
}) : null] });
|
|
4841
4988
|
}
|
|
4842
4989
|
function SearchSnippetView({ card, toolLabel, icon: Icon = Search }) {
|
|
@@ -4865,38 +5012,6 @@ function SearchSnippetView({ card, toolLabel, icon: Icon = Search }) {
|
|
|
4865
5012
|
}
|
|
4866
5013
|
//#endregion
|
|
4867
5014
|
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-generic.tsx
|
|
4868
|
-
function GenericToolSection({ label, tone, children }) {
|
|
4869
|
-
const style = {
|
|
4870
|
-
input: {
|
|
4871
|
-
dot: "bg-muted-foreground/60",
|
|
4872
|
-
body: "text-foreground"
|
|
4873
|
-
},
|
|
4874
|
-
output: {
|
|
4875
|
-
dot: "bg-primary/70",
|
|
4876
|
-
body: "text-foreground"
|
|
4877
|
-
},
|
|
4878
|
-
error: {
|
|
4879
|
-
dot: "bg-rose-500/80",
|
|
4880
|
-
body: "text-rose-950/85"
|
|
4881
|
-
}
|
|
4882
|
-
}[tone];
|
|
4883
|
-
return /* @__PURE__ */ jsxs("section", {
|
|
4884
|
-
className: "overflow-hidden rounded-md border border-border/70 bg-muted/20",
|
|
4885
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
4886
|
-
className: "flex items-center gap-2 border-b border-border/60 px-2.5 py-1.5 text-[10px] font-medium tracking-wide text-muted-foreground",
|
|
4887
|
-
children: [/* @__PURE__ */ jsx("span", { className: cn("h-1.5 w-1.5 rounded-full", style.dot) }), /* @__PURE__ */ jsx("span", {
|
|
4888
|
-
className: "normal-case tracking-normal",
|
|
4889
|
-
children: label
|
|
4890
|
-
})]
|
|
4891
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
4892
|
-
className: "w-full overflow-hidden",
|
|
4893
|
-
children: /* @__PURE__ */ jsx("pre", {
|
|
4894
|
-
className: cn("w-full max-w-full min-w-0 max-h-64 overflow-x-auto overflow-y-auto px-2.5 py-2 font-mono text-[12px] leading-relaxed whitespace-pre custom-scrollbar", style.body),
|
|
4895
|
-
children
|
|
4896
|
-
})
|
|
4897
|
-
})]
|
|
4898
|
-
});
|
|
4899
|
-
}
|
|
4900
5015
|
function buildToolActionSlot(card, onToolAction, renderToolAgent) {
|
|
4901
5016
|
const agentAction = card.agentId && renderToolAgent ? renderToolAgent(card.agentId) : null;
|
|
4902
5017
|
const toolAction = card.action && onToolAction ? /* @__PURE__ */ jsx(ToolCardHeaderAction, {
|
|
@@ -4930,13 +5045,13 @@ function GenericToolCard({ card, toolLabel, icon: Icon = Wrench, onToolAction, r
|
|
|
4930
5045
|
}), expanded && hasContent ? /* @__PURE__ */ jsxs(ToolCardContent, {
|
|
4931
5046
|
className: "bg-transparent py-0",
|
|
4932
5047
|
children: [
|
|
4933
|
-
hasInputSection ? /* @__PURE__ */ jsx(
|
|
5048
|
+
hasInputSection ? /* @__PURE__ */ jsx(ToolCardDetailSection, {
|
|
4934
5049
|
label: card.inputLabel?.trim() || "Input",
|
|
4935
5050
|
tone: "input",
|
|
4936
5051
|
children: input
|
|
4937
5052
|
}) : null,
|
|
4938
5053
|
hasInputSection && hasOutputSection ? /* @__PURE__ */ jsx("div", { className: "h-2" }) : null,
|
|
4939
|
-
hasOutputSection ? /* @__PURE__ */ jsx(
|
|
5054
|
+
hasOutputSection ? /* @__PURE__ */ jsx(ToolCardDetailSection, {
|
|
4940
5055
|
label: card.outputLabel?.trim() || "Output",
|
|
4941
5056
|
tone: card.statusTone === "error" ? "error" : "output",
|
|
4942
5057
|
children: output
|
|
@@ -5085,8 +5200,7 @@ function ChatToolCard({ card, toolStatusLabels, onToolAction, onFileOpen, render
|
|
|
5085
5200
|
}
|
|
5086
5201
|
//#endregion
|
|
5087
5202
|
//#region src/components/chat/ui/chat-message-list/chat-tool-activity-group.tsx
|
|
5088
|
-
function ChatToolActivityGroup({ group, isUser, reasoningCharacterCountTemplates, toolStatusLabels, onToolAction, onFileOpen, renderToolAgent, renderPanelAppCard }) {
|
|
5089
|
-
const [open, setOpen] = useState(false);
|
|
5203
|
+
function ChatToolActivityGroup({ group, open, isUser, reasoningCharacterCountTemplates, toolStatusLabels, onToolAction, onFileOpen, renderToolAgent, renderPanelAppCard, onOpenChange }) {
|
|
5090
5204
|
const toolCount = group.parts.filter((part) => part.type === "tool-card").length;
|
|
5091
5205
|
const showWorkflowRail = open && toolCount > 1;
|
|
5092
5206
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -5094,34 +5208,29 @@ function ChatToolActivityGroup({ group, isUser, reasoningCharacterCountTemplates
|
|
|
5094
5208
|
children: [/* @__PURE__ */ jsx(ChatCollapsibleMetaSummary, {
|
|
5095
5209
|
openGroup: "tool-activity",
|
|
5096
5210
|
open,
|
|
5211
|
+
icon: Workflow,
|
|
5212
|
+
leadingIconClassName: "bg-card",
|
|
5097
5213
|
label: group.label,
|
|
5098
|
-
onClick: () =>
|
|
5214
|
+
onClick: () => onOpenChange(!open)
|
|
5099
5215
|
}), open ? /* @__PURE__ */ jsx("div", {
|
|
5100
5216
|
className: "text-[0.925rem] leading-[1.72]",
|
|
5101
5217
|
children: group.parts.map((part, index) => {
|
|
5102
5218
|
const isLast = index === group.parts.length - 1;
|
|
5103
5219
|
return /* @__PURE__ */ jsxs("div", {
|
|
5104
5220
|
className: "relative min-w-0",
|
|
5105
|
-
children: [showWorkflowRail ? /* @__PURE__ */ jsx("
|
|
5106
|
-
"aria-hidden": "true",
|
|
5107
|
-
"data-tool-workflow-rail": "true",
|
|
5108
|
-
className: cn("pointer-events-none absolute left-[0.575em] w-px -translate-x-1/2 bg-border/70", index === 0 ? "bottom-0 top-[0.86em]" : isLast ? "top-0 h-[0.86em]" : "inset-y-0")
|
|
5109
|
-
}) : null, part.type === "tool-card" ? /* @__PURE__ */ jsx(ChatToolCard, {
|
|
5221
|
+
children: [showWorkflowRail ? /* @__PURE__ */ jsx(ChatProcessWorkflowRail, { position: index === 0 ? "first" : isLast ? "last" : "middle" }) : null, part.type === "tool-card" ? /* @__PURE__ */ jsx(ChatToolCard, {
|
|
5110
5222
|
card: part.card,
|
|
5111
5223
|
toolStatusLabels,
|
|
5112
5224
|
onToolAction,
|
|
5113
5225
|
onFileOpen,
|
|
5114
5226
|
renderToolAgent,
|
|
5115
5227
|
renderPanelAppCard
|
|
5116
|
-
}) : /* @__PURE__ */ jsx(
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
isUser,
|
|
5123
|
-
isInProgress: false
|
|
5124
|
-
})
|
|
5228
|
+
}) : /* @__PURE__ */ jsx(ChatReasoningBlock, {
|
|
5229
|
+
label: part.label,
|
|
5230
|
+
text: part.text,
|
|
5231
|
+
characterCountTemplates: reasoningCharacterCountTemplates,
|
|
5232
|
+
isUser,
|
|
5233
|
+
isInProgress: false
|
|
5125
5234
|
})]
|
|
5126
5235
|
}, `tool-group-item-${group.startIndex + index}`);
|
|
5127
5236
|
})
|
|
@@ -5261,9 +5370,9 @@ function groupConsecutiveToolParts(parts, labels) {
|
|
|
5261
5370
|
});
|
|
5262
5371
|
blocks.push({
|
|
5263
5372
|
kind: "tool-group",
|
|
5264
|
-
key: `tool-group-${startIndex}
|
|
5373
|
+
key: `tool-group-${startIndex}`,
|
|
5265
5374
|
group: {
|
|
5266
|
-
key: `tool-group-${startIndex}
|
|
5375
|
+
key: `tool-group-${startIndex}`,
|
|
5267
5376
|
startIndex,
|
|
5268
5377
|
endIndex: index - 1,
|
|
5269
5378
|
parts: groupedParts,
|
|
@@ -5347,7 +5456,7 @@ function splitAssistantProcess(message) {
|
|
|
5347
5456
|
finalParts: message.parts.slice(lastProcessPartIndex + 1)
|
|
5348
5457
|
};
|
|
5349
5458
|
}
|
|
5350
|
-
function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, part, renderInlineDisplay, renderPanelAppCard, renderToolAgent, role, texts }) {
|
|
5459
|
+
function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, part, renderInlineDisplay, renderPanelAppCard, renderToolAgent, resolveFileContentUrl, role, texts }) {
|
|
5351
5460
|
const { type } = part;
|
|
5352
5461
|
if (type === "markdown") {
|
|
5353
5462
|
const { inlineTokens, text } = part;
|
|
@@ -5358,6 +5467,7 @@ function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAtta
|
|
|
5358
5467
|
inlineTokens,
|
|
5359
5468
|
onFileOpen,
|
|
5360
5469
|
onInlineTokenClick,
|
|
5470
|
+
resolveFileContentUrl,
|
|
5361
5471
|
renderInlineDisplay
|
|
5362
5472
|
}, `markdown-${index}`);
|
|
5363
5473
|
}
|
|
@@ -5373,14 +5483,17 @@ function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAtta
|
|
|
5373
5483
|
}
|
|
5374
5484
|
if (type === "tool-card") {
|
|
5375
5485
|
const { card } = part;
|
|
5376
|
-
return /* @__PURE__ */
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5486
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5487
|
+
className: "relative min-w-0",
|
|
5488
|
+
children: [/* @__PURE__ */ jsx(ChatProcessWorkflowRail, { position: "single" }), /* @__PURE__ */ jsx(ChatToolCard, {
|
|
5489
|
+
card,
|
|
5490
|
+
toolStatusLabels: texts.toolStatusLabels,
|
|
5491
|
+
onToolAction,
|
|
5492
|
+
onFileOpen,
|
|
5493
|
+
renderToolAgent,
|
|
5494
|
+
renderPanelAppCard
|
|
5495
|
+
})]
|
|
5496
|
+
}, `tool-${index}`);
|
|
5384
5497
|
}
|
|
5385
5498
|
if (type === "file") {
|
|
5386
5499
|
const { file } = part;
|
|
@@ -5411,17 +5524,26 @@ function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAtta
|
|
|
5411
5524
|
return null;
|
|
5412
5525
|
}
|
|
5413
5526
|
function renderMessageParts(params) {
|
|
5414
|
-
const { isInProgress, isUser, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, parts, renderInlineDisplay, renderPanelAppCard, renderToolAgent, role, texts, indexOffset = 0 } = params;
|
|
5415
|
-
return groupConsecutiveToolParts(parts, resolveToolActivityLabels(texts)).map((block) => {
|
|
5527
|
+
const { isInProgress, isUser, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolActivityOpenChange, onToolAction, openToolGroupKeys, parts, renderInlineDisplay, renderPanelAppCard, renderToolAgent, resolveFileContentUrl, role, texts, indexOffset = 0 } = params;
|
|
5528
|
+
return groupConsecutiveImageFileBlocks(groupConsecutiveToolParts(parts, resolveToolActivityLabels(texts))).map((block) => {
|
|
5529
|
+
if (block.kind === "image-group") return /* @__PURE__ */ jsx(ChatMessageImageRow, {
|
|
5530
|
+
group: block,
|
|
5531
|
+
indexOffset,
|
|
5532
|
+
isUser,
|
|
5533
|
+
texts,
|
|
5534
|
+
onOpen: onAttachmentOpen
|
|
5535
|
+
}, `${block.key}-${indexOffset}`);
|
|
5416
5536
|
if (block.kind === "tool-group") return /* @__PURE__ */ jsx(ChatToolActivityGroup, {
|
|
5417
5537
|
group: block.group,
|
|
5538
|
+
open: openToolGroupKeys.has(block.group.key),
|
|
5418
5539
|
isUser,
|
|
5419
5540
|
reasoningCharacterCountTemplates: texts.reasoningCharacterCountTemplates,
|
|
5420
5541
|
toolStatusLabels: texts.toolStatusLabels,
|
|
5421
5542
|
onToolAction,
|
|
5422
5543
|
onFileOpen,
|
|
5423
5544
|
renderToolAgent,
|
|
5424
|
-
renderPanelAppCard
|
|
5545
|
+
renderPanelAppCard,
|
|
5546
|
+
onOpenChange: (open) => onToolActivityOpenChange(block.group.key, open)
|
|
5425
5547
|
}, block.key);
|
|
5426
5548
|
return renderChatMessagePart({
|
|
5427
5549
|
part: block.part,
|
|
@@ -5435,20 +5557,33 @@ function renderMessageParts(params) {
|
|
|
5435
5557
|
onFileOpen,
|
|
5436
5558
|
onAttachmentOpen,
|
|
5437
5559
|
onInlineTokenClick,
|
|
5560
|
+
resolveFileContentUrl,
|
|
5438
5561
|
renderInlineDisplay,
|
|
5439
5562
|
renderToolAgent,
|
|
5440
5563
|
renderPanelAppCard
|
|
5441
5564
|
});
|
|
5442
5565
|
});
|
|
5443
5566
|
}
|
|
5444
|
-
const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, onFileOpen, onAttachmentOpen, onInlineTokenClick, renderInlineDisplay, renderToolAgent, renderPanelAppCard }) {
|
|
5567
|
+
const ChatMessage = memo(function ChatMessage({ layout, message, texts, onToolAction, onFileOpen, onAttachmentOpen, onInlineTokenClick, renderInlineDisplay, renderToolAgent, renderPanelAppCard, resolveFileContentUrl }) {
|
|
5445
5568
|
const { role } = message;
|
|
5446
5569
|
const isUser = role === "user";
|
|
5570
|
+
const isFlat = layout === "flat" && !isUser;
|
|
5447
5571
|
const isInProgress = isMessageInProgress(message.status);
|
|
5448
5572
|
const processSplit = splitAssistantProcess(message);
|
|
5449
5573
|
const [processOpen, setProcessOpen] = useState(false);
|
|
5574
|
+
const [openToolGroupKeys, setOpenToolGroupKeys] = useState(() => /* @__PURE__ */ new Set());
|
|
5575
|
+
const handleToolActivityOpenChange = (groupKey, open) => {
|
|
5576
|
+
setOpenToolGroupKeys((current) => {
|
|
5577
|
+
const next = new Set(current);
|
|
5578
|
+
if (open) next.add(groupKey);
|
|
5579
|
+
else next.delete(groupKey);
|
|
5580
|
+
return next;
|
|
5581
|
+
});
|
|
5582
|
+
if (open) setProcessOpen(true);
|
|
5583
|
+
};
|
|
5450
5584
|
return /* @__PURE__ */ jsx("div", {
|
|
5451
|
-
|
|
5585
|
+
"data-chat-message-surface": isFlat ? "flat" : "card",
|
|
5586
|
+
className: cn("max-w-full has-[[data-chat-message-wide-content=true]]:w-full", isFlat ? "block w-full text-foreground" : "inline-block w-fit rounded-2xl border px-4 shadow-sm", !isFlat && (isUser ? "nextclaw-chat-message-user rounded-[1.45rem] border-primary bg-primary py-2.5 text-primary-foreground shadow-none" : role === "assistant" ? "border-border bg-card pb-3 pt-4 text-card-foreground" : "border-border bg-muted/45 py-3 text-foreground")),
|
|
5452
5587
|
children: /* @__PURE__ */ jsx("div", {
|
|
5453
5588
|
className: "space-y-0",
|
|
5454
5589
|
children: processSplit ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
@@ -5458,6 +5593,8 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5458
5593
|
children: /* @__PURE__ */ jsx(ChatCollapsibleMetaSummary, {
|
|
5459
5594
|
openGroup: "process",
|
|
5460
5595
|
open: processOpen,
|
|
5596
|
+
icon: ListChecks,
|
|
5597
|
+
leadingIconClassName: "bg-card",
|
|
5461
5598
|
label: message.processSummary?.label,
|
|
5462
5599
|
onClick: () => setProcessOpen((current) => !current)
|
|
5463
5600
|
})
|
|
@@ -5473,6 +5610,9 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5473
5610
|
onFileOpen,
|
|
5474
5611
|
onAttachmentOpen,
|
|
5475
5612
|
onInlineTokenClick,
|
|
5613
|
+
resolveFileContentUrl,
|
|
5614
|
+
openToolGroupKeys,
|
|
5615
|
+
onToolActivityOpenChange: handleToolActivityOpenChange,
|
|
5476
5616
|
renderInlineDisplay,
|
|
5477
5617
|
renderToolAgent,
|
|
5478
5618
|
renderPanelAppCard
|
|
@@ -5488,6 +5628,9 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5488
5628
|
onFileOpen,
|
|
5489
5629
|
onAttachmentOpen,
|
|
5490
5630
|
onInlineTokenClick,
|
|
5631
|
+
resolveFileContentUrl,
|
|
5632
|
+
openToolGroupKeys,
|
|
5633
|
+
onToolActivityOpenChange: handleToolActivityOpenChange,
|
|
5491
5634
|
renderInlineDisplay,
|
|
5492
5635
|
renderToolAgent,
|
|
5493
5636
|
renderPanelAppCard,
|
|
@@ -5502,6 +5645,9 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5502
5645
|
onFileOpen,
|
|
5503
5646
|
onAttachmentOpen,
|
|
5504
5647
|
onInlineTokenClick,
|
|
5648
|
+
resolveFileContentUrl,
|
|
5649
|
+
openToolGroupKeys,
|
|
5650
|
+
onToolActivityOpenChange: handleToolActivityOpenChange,
|
|
5505
5651
|
renderInlineDisplay,
|
|
5506
5652
|
renderToolAgent,
|
|
5507
5653
|
renderPanelAppCard
|
|
@@ -5542,7 +5688,6 @@ const TYPING_TEXT_SHEEN_CSS = `
|
|
|
5542
5688
|
background-position: -60% 0;
|
|
5543
5689
|
}
|
|
5544
5690
|
}
|
|
5545
|
-
|
|
5546
5691
|
.nextclaw-chat-typing-indicator__text {
|
|
5547
5692
|
background-image: linear-gradient(
|
|
5548
5693
|
100deg,
|
|
@@ -5596,16 +5741,17 @@ function ChatMessageTypingFooter() {
|
|
|
5596
5741
|
})
|
|
5597
5742
|
});
|
|
5598
5743
|
}
|
|
5599
|
-
function ChatTypingIndicator({ label }) {
|
|
5744
|
+
function ChatTypingIndicator({ label, layout }) {
|
|
5600
5745
|
return /* @__PURE__ */ jsxs("div", {
|
|
5601
|
-
|
|
5746
|
+
"data-chat-message-surface": layout === "flat" ? "flat" : "card",
|
|
5747
|
+
className: cn("text-sm text-muted-foreground", layout === "flat" ? "py-1" : "rounded-2xl border border-border bg-card px-4 py-3 shadow-sm"),
|
|
5602
5748
|
children: [/* @__PURE__ */ jsx("style", { children: TYPING_TEXT_SHEEN_CSS }), /* @__PURE__ */ jsx("span", {
|
|
5603
5749
|
className: "nextclaw-chat-typing-indicator__text",
|
|
5604
5750
|
children: label
|
|
5605
5751
|
})]
|
|
5606
5752
|
});
|
|
5607
5753
|
}
|
|
5608
|
-
function ChatMessageList({ className, isSending, messages, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, renderInlineDisplay, renderPanelAppCard, renderToolAgent, texts }) {
|
|
5754
|
+
function ChatMessageList({ className, isSending, layout = "card", messages, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, renderInlineDisplay, renderPanelAppCard, renderToolAgent, resolveFileContentUrl, texts }) {
|
|
5609
5755
|
const visibleMessages = messages.filter(hasRenderableMessageContent);
|
|
5610
5756
|
const hasRenderableAssistantDraft = visibleMessages.some((message) => message.role === "assistant" && (message.status === "streaming" || message.status === "pending"));
|
|
5611
5757
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -5613,23 +5759,63 @@ function ChatMessageList({ className, isSending, messages, onAttachmentOpen, onF
|
|
|
5613
5759
|
children: [visibleMessages.map((message) => {
|
|
5614
5760
|
const isUser = message.role === "user";
|
|
5615
5761
|
const isGenerating = !isUser && (message.status === "streaming" || message.status === "pending");
|
|
5762
|
+
const content = /* @__PURE__ */ jsx(ChatMessage, {
|
|
5763
|
+
layout,
|
|
5764
|
+
message,
|
|
5765
|
+
texts,
|
|
5766
|
+
onToolAction,
|
|
5767
|
+
onFileOpen,
|
|
5768
|
+
onAttachmentOpen,
|
|
5769
|
+
onInlineTokenClick,
|
|
5770
|
+
resolveFileContentUrl,
|
|
5771
|
+
renderInlineDisplay,
|
|
5772
|
+
renderToolAgent,
|
|
5773
|
+
renderPanelAppCard
|
|
5774
|
+
});
|
|
5775
|
+
if (layout === "flat" && !isUser) return /* @__PURE__ */ jsxs("article", {
|
|
5776
|
+
"data-chat-message-layout": "flat",
|
|
5777
|
+
className: "w-full min-w-0 space-y-2",
|
|
5778
|
+
children: [
|
|
5779
|
+
/* @__PURE__ */ jsxs("div", {
|
|
5780
|
+
"data-chat-message-header": "flat",
|
|
5781
|
+
className: "flex min-w-0 items-center gap-2.5",
|
|
5782
|
+
children: [/* @__PURE__ */ jsx(ChatMessageAvatar, {
|
|
5783
|
+
role: message.role,
|
|
5784
|
+
size: "compact"
|
|
5785
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
5786
|
+
className: "truncate text-sm font-semibold text-foreground",
|
|
5787
|
+
children: message.roleLabel
|
|
5788
|
+
})]
|
|
5789
|
+
}),
|
|
5790
|
+
/* @__PURE__ */ jsx("div", {
|
|
5791
|
+
"data-chat-message-body": "flat",
|
|
5792
|
+
className: "min-w-0 w-full",
|
|
5793
|
+
children: content
|
|
5794
|
+
}),
|
|
5795
|
+
isGenerating ? /* @__PURE__ */ jsx("div", {
|
|
5796
|
+
className: "w-full",
|
|
5797
|
+
children: /* @__PURE__ */ jsx(ChatMessageTypingFooter, {})
|
|
5798
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
5799
|
+
"data-chat-message-footer": "flat",
|
|
5800
|
+
className: "flex items-center gap-2",
|
|
5801
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5802
|
+
className: "px-1 text-[11px] leading-4 text-muted-foreground",
|
|
5803
|
+
children: message.timestampLabel
|
|
5804
|
+
}), /* @__PURE__ */ jsx(ChatMessageActionCopy, {
|
|
5805
|
+
message,
|
|
5806
|
+
texts
|
|
5807
|
+
})]
|
|
5808
|
+
})
|
|
5809
|
+
]
|
|
5810
|
+
}, message.id);
|
|
5616
5811
|
return /* @__PURE__ */ jsxs("div", {
|
|
5812
|
+
"data-chat-message-layout": "card",
|
|
5617
5813
|
className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"),
|
|
5618
5814
|
children: [
|
|
5619
5815
|
!isUser ? /* @__PURE__ */ jsx(ChatMessageAvatar, { role: message.role }) : null,
|
|
5620
5816
|
/* @__PURE__ */ jsxs("div", {
|
|
5621
5817
|
className: cn("w-fit max-w-[92%] space-y-2 has-[[data-chat-message-wide-content=true]]:w-full", isUser && "flex flex-col items-end"),
|
|
5622
|
-
children: [/* @__PURE__ */ jsx(
|
|
5623
|
-
message,
|
|
5624
|
-
texts,
|
|
5625
|
-
onToolAction,
|
|
5626
|
-
onFileOpen,
|
|
5627
|
-
onAttachmentOpen,
|
|
5628
|
-
onInlineTokenClick,
|
|
5629
|
-
renderInlineDisplay,
|
|
5630
|
-
renderToolAgent,
|
|
5631
|
-
renderPanelAppCard
|
|
5632
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
5818
|
+
children: [content, /* @__PURE__ */ jsx("div", {
|
|
5633
5819
|
className: cn("flex items-center gap-2", isUser && "justify-end"),
|
|
5634
5820
|
children: isGenerating ? /* @__PURE__ */ jsx(ChatMessageTypingFooter, {}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
5635
5821
|
className: cn("px-1 text-[11px] leading-4 text-muted-foreground", isUser ? "text-right" : "text-left"),
|
|
@@ -5648,8 +5834,15 @@ function ChatMessageList({ className, isSending, messages, onAttachmentOpen, onF
|
|
|
5648
5834
|
]
|
|
5649
5835
|
}, message.id);
|
|
5650
5836
|
}), isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs("div", {
|
|
5837
|
+
"data-chat-message-layout": layout,
|
|
5651
5838
|
className: "flex justify-start gap-3",
|
|
5652
|
-
children: [/* @__PURE__ */ jsx(ChatMessageAvatar, {
|
|
5839
|
+
children: [/* @__PURE__ */ jsx(ChatMessageAvatar, {
|
|
5840
|
+
role: "assistant",
|
|
5841
|
+
size: layout === "flat" ? "compact" : "default"
|
|
5842
|
+
}), /* @__PURE__ */ jsx(ChatTypingIndicator, {
|
|
5843
|
+
label: texts.typingLabel,
|
|
5844
|
+
layout
|
|
5845
|
+
})]
|
|
5653
5846
|
}) : null]
|
|
5654
5847
|
});
|
|
5655
5848
|
}
|