@nextclaw/agent-chat-ui 0.6.3 → 0.6.5
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 +20 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +983 -555
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { Fragment, forwardRef, memo, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Fragment, createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { clsx } from "clsx";
|
|
4
4
|
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";
|
|
12
12
|
import { LexicalComposer } from "@lexical/react/LexicalComposer";
|
|
13
13
|
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
14
14
|
import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";
|
|
15
|
-
import { $applyNodeReplacement, $createLineBreakNode, $createParagraphNode, $createRangeSelection, $createTextNode, $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode, $setSelection, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, DecoratorNode, KEY_DOWN_COMMAND, SELECTION_CHANGE_COMMAND, mergeRegister } from "lexical";
|
|
15
|
+
import { $applyNodeReplacement, $createLineBreakNode, $createParagraphNode, $createRangeSelection, $createTextNode, $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode, $setSelection, BLUR_COMMAND, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, DecoratorNode, KEY_DOWN_COMMAND, SELECTION_CHANGE_COMMAND, SKIP_DOM_SELECTION_TAG, mergeRegister } from "lexical";
|
|
16
16
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
17
17
|
import ReactMarkdown from "react-markdown";
|
|
18
18
|
import remarkGfm from "remark-gfm";
|
|
@@ -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
|
})]
|
|
@@ -1667,12 +1625,12 @@ function writeChatComposerStateToLexicalRoot(nodes, selection) {
|
|
|
1667
1625
|
nextSelection.focus.set(focus.key, focus.offset, focus.type);
|
|
1668
1626
|
$setSelection(nextSelection);
|
|
1669
1627
|
}
|
|
1670
|
-
function syncLexicalEditorFromChatComposerState(editor, nodes, selection) {
|
|
1628
|
+
function syncLexicalEditorFromChatComposerState(editor, nodes, selection, preserveDomSelection = false) {
|
|
1671
1629
|
editor.update(() => {
|
|
1672
1630
|
writeChatComposerStateToLexicalRoot(nodes, selection);
|
|
1673
|
-
});
|
|
1631
|
+
}, preserveDomSelection ? { tag: SKIP_DOM_SELECTION_TAG } : void 0);
|
|
1674
1632
|
}
|
|
1675
|
-
function syncLexicalSelectionFromChatComposerSelection(editor, selection) {
|
|
1633
|
+
function syncLexicalSelectionFromChatComposerSelection(editor, selection, preserveDomSelection = false) {
|
|
1676
1634
|
editor.update(() => {
|
|
1677
1635
|
const nextSelection = $createRangeSelection();
|
|
1678
1636
|
const anchor = buildSelectionPointFromOffset(selection.start);
|
|
@@ -1680,7 +1638,7 @@ function syncLexicalSelectionFromChatComposerSelection(editor, selection) {
|
|
|
1680
1638
|
nextSelection.anchor.set(anchor.key, anchor.offset, anchor.type);
|
|
1681
1639
|
nextSelection.focus.set(focus.key, focus.offset, focus.type);
|
|
1682
1640
|
$setSelection(nextSelection);
|
|
1683
|
-
});
|
|
1641
|
+
}, preserveDomSelection ? { tag: SKIP_DOM_SELECTION_TAG } : void 0);
|
|
1684
1642
|
}
|
|
1685
1643
|
//#endregion
|
|
1686
1644
|
//#region src/components/chat/ui/chat-input-bar/lexical/chat-composer-lexical-operations.ts
|
|
@@ -1991,11 +1949,12 @@ var ChatComposerLexicalOwner = class {
|
|
|
1991
1949
|
const shouldSyncDocument = nextSignature !== this.editorSignatureRef.current;
|
|
1992
1950
|
if (!shouldSyncDocument && !pendingSelection) return;
|
|
1993
1951
|
this.startApplyingExternalUpdate();
|
|
1952
|
+
const preserveDomSelection = editor.getRootElement() !== document.activeElement;
|
|
1994
1953
|
if (shouldSyncDocument) {
|
|
1995
|
-
syncLexicalEditorFromChatComposerState(editor, nodes, pendingSelection);
|
|
1954
|
+
syncLexicalEditorFromChatComposerState(editor, nodes, pendingSelection, preserveDomSelection);
|
|
1996
1955
|
this.editorSignatureRef.current = nextSignature;
|
|
1997
1956
|
this.lastPublishedSignatureRef.current = nextSignature;
|
|
1998
|
-
} else if (pendingSelection) syncLexicalSelectionFromChatComposerSelection(editor, pendingSelection);
|
|
1957
|
+
} else if (pendingSelection) syncLexicalSelectionFromChatComposerSelection(editor, pendingSelection, preserveDomSelection);
|
|
1999
1958
|
if (pendingSelection) {
|
|
2000
1959
|
this.selectionRef.current = pendingSelection;
|
|
2001
1960
|
this.pendingSelectionRef.current = null;
|
|
@@ -2294,6 +2253,11 @@ const ChatInputBarTokenizedComposer = forwardRef(function ChatInputBarTokenizedC
|
|
|
2294
2253
|
ChatInputBarTokenizedComposer.displayName = "LexicalChatInputBarTokenizedComposer";
|
|
2295
2254
|
//#endregion
|
|
2296
2255
|
//#region src/components/chat/ui/chat-input-bar/chat-input-bar.tsx
|
|
2256
|
+
const SEND_ERROR_PREVIEW_MAX_CHARS = 120;
|
|
2257
|
+
function buildSendErrorPreview(value) {
|
|
2258
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
2259
|
+
return compact.length <= SEND_ERROR_PREVIEW_MAX_CHARS ? compact : `${compact.slice(0, SEND_ERROR_PREVIEW_MAX_CHARS - 1)}…`;
|
|
2260
|
+
}
|
|
2297
2261
|
function InputBarHint({ hint }) {
|
|
2298
2262
|
if (!hint) return null;
|
|
2299
2263
|
if (hint.loading) return /* @__PURE__ */ jsx("div", {
|
|
@@ -2316,7 +2280,44 @@ function InputBarHint({ hint }) {
|
|
|
2316
2280
|
})
|
|
2317
2281
|
});
|
|
2318
2282
|
}
|
|
2319
|
-
|
|
2283
|
+
function ChatInputBarSendError({ sendError, sendErrorDetailsLabel }) {
|
|
2284
|
+
const normalizedSendError = sendError?.trim() ?? "";
|
|
2285
|
+
if (!normalizedSendError) return null;
|
|
2286
|
+
const { Popover, PopoverContent, PopoverTrigger } = ChatUiPrimitives;
|
|
2287
|
+
const sendErrorPreview = buildSendErrorPreview(normalizedSendError);
|
|
2288
|
+
const resolvedSendErrorDetailsLabel = sendErrorDetailsLabel?.trim() || "Details";
|
|
2289
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2290
|
+
className: "px-3 pb-2 sm:px-4",
|
|
2291
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2292
|
+
"aria-live": "polite",
|
|
2293
|
+
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",
|
|
2294
|
+
role: "status",
|
|
2295
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
2296
|
+
className: "min-w-0 flex-1 truncate leading-5",
|
|
2297
|
+
title: normalizedSendError,
|
|
2298
|
+
children: sendErrorPreview
|
|
2299
|
+
}), /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
2300
|
+
asChild: true,
|
|
2301
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
2302
|
+
type: "button",
|
|
2303
|
+
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",
|
|
2304
|
+
children: resolvedSendErrorDetailsLabel
|
|
2305
|
+
})
|
|
2306
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
2307
|
+
align: "end",
|
|
2308
|
+
className: "w-[min(32rem,calc(100vw-1.5rem))] border-red-100/80 p-0",
|
|
2309
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2310
|
+
className: "border-b border-red-100 bg-red-50/80 px-4 py-2 text-xs font-semibold text-red-700",
|
|
2311
|
+
children: resolvedSendErrorDetailsLabel
|
|
2312
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
2313
|
+
className: "max-h-80 overflow-auto whitespace-pre-wrap break-words px-4 py-3 text-xs leading-relaxed text-red-700",
|
|
2314
|
+
children: normalizedSendError
|
|
2315
|
+
})]
|
|
2316
|
+
})] })]
|
|
2317
|
+
})
|
|
2318
|
+
});
|
|
2319
|
+
}
|
|
2320
|
+
const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSurface, sendError, sendErrorDetailsLabel, slashMenu, surface, toolbar: toolbarProps, topSlot }, ref) {
|
|
2320
2321
|
const composerRef = useRef(null);
|
|
2321
2322
|
const resolvedInputSurface = inputSurface ?? (slashMenu ? {
|
|
2322
2323
|
isLoading: slashMenu.isLoading,
|
|
@@ -2384,6 +2385,10 @@ const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSur
|
|
|
2384
2385
|
})
|
|
2385
2386
|
}),
|
|
2386
2387
|
/* @__PURE__ */ jsx(InputBarHint, { hint }),
|
|
2388
|
+
/* @__PURE__ */ jsx(ChatInputBarSendError, {
|
|
2389
|
+
sendError,
|
|
2390
|
+
sendErrorDetailsLabel
|
|
2391
|
+
}),
|
|
2387
2392
|
/* @__PURE__ */ jsx(ChatInputBarToolbar, { ...toolbar })
|
|
2388
2393
|
]
|
|
2389
2394
|
})
|
|
@@ -2393,22 +2398,25 @@ const ChatInputBar = forwardRef(function ChatInputBar({ composer, hint, inputSur
|
|
|
2393
2398
|
ChatInputBar.displayName = "ChatInputBar";
|
|
2394
2399
|
//#endregion
|
|
2395
2400
|
//#region src/components/chat/ui/chat-message-list/chat-message-avatar.tsx
|
|
2396
|
-
function ChatMessageAvatar({ role }) {
|
|
2401
|
+
function ChatMessageAvatar({ role, size = "default" }) {
|
|
2402
|
+
const compact = size === "compact";
|
|
2403
|
+
const frameSize = compact ? "h-7 w-7" : "h-8 w-8";
|
|
2404
|
+
const iconSize = compact ? "h-3.5 w-3.5" : "h-4 w-4";
|
|
2397
2405
|
if (role === "user") return /* @__PURE__ */ jsx("div", {
|
|
2398
2406
|
"data-testid": "chat-message-avatar-user",
|
|
2399
|
-
className: "flex
|
|
2400
|
-
children: /* @__PURE__ */ jsx(User, { className:
|
|
2407
|
+
className: cn("flex shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-sm", frameSize),
|
|
2408
|
+
children: /* @__PURE__ */ jsx(User, { className: iconSize })
|
|
2401
2409
|
});
|
|
2402
2410
|
if (role === "tool") return /* @__PURE__ */ jsx("div", {
|
|
2403
2411
|
"data-testid": "chat-message-avatar-tool",
|
|
2404
|
-
className: "flex
|
|
2405
|
-
children: /* @__PURE__ */ jsx(Wrench, { className:
|
|
2412
|
+
className: cn("flex shrink-0 items-center justify-center rounded-full bg-accent text-accent-foreground shadow-sm ring-1 ring-border", frameSize),
|
|
2413
|
+
children: /* @__PURE__ */ jsx(Wrench, { className: iconSize })
|
|
2406
2414
|
});
|
|
2407
2415
|
return /* @__PURE__ */ jsx("div", {
|
|
2408
2416
|
"data-testid": "chat-message-avatar-assistant",
|
|
2409
|
-
className: "flex
|
|
2417
|
+
className: cn("flex shrink-0 items-center justify-center rounded-full bg-muted text-foreground shadow-sm ring-1 ring-border", frameSize),
|
|
2410
2418
|
children: /* @__PURE__ */ jsx(Bot, {
|
|
2411
|
-
className: "h-[18px] w-[18px] text-current",
|
|
2419
|
+
className: cn(compact ? "h-4 w-4" : "h-[18px] w-[18px]", "text-current"),
|
|
2412
2420
|
strokeWidth: 2.5
|
|
2413
2421
|
})
|
|
2414
2422
|
});
|
|
@@ -2773,7 +2781,7 @@ function isRecord$1(value) {
|
|
|
2773
2781
|
function readString(value) {
|
|
2774
2782
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
2775
2783
|
}
|
|
2776
|
-
function readPositiveInteger(value) {
|
|
2784
|
+
function readPositiveInteger$1(value) {
|
|
2777
2785
|
return Number.isInteger(value) && Number(value) > 0 ? Number(value) : void 0;
|
|
2778
2786
|
}
|
|
2779
2787
|
function readFilePreviewViewer(value) {
|
|
@@ -2793,8 +2801,8 @@ function readFileTarget(record) {
|
|
|
2793
2801
|
type: "file",
|
|
2794
2802
|
payload: {
|
|
2795
2803
|
path,
|
|
2796
|
-
line: readPositiveInteger(payload.line),
|
|
2797
|
-
column: readPositiveInteger(payload.column),
|
|
2804
|
+
line: readPositiveInteger$1(payload.line),
|
|
2805
|
+
column: readPositiveInteger$1(payload.column),
|
|
2798
2806
|
viewer: readFilePreviewViewer(payload.viewer)
|
|
2799
2807
|
}
|
|
2800
2808
|
};
|
|
@@ -2929,57 +2937,345 @@ function ChatInlineDisplay({ display, renderInlineDisplay }) {
|
|
|
2929
2937
|
return /* @__PURE__ */ jsx(ChatInlineDisplayFallback, { display });
|
|
2930
2938
|
}
|
|
2931
2939
|
//#endregion
|
|
2932
|
-
//#region src/components/chat/ui/chat-message-list/chat-
|
|
2933
|
-
const
|
|
2934
|
-
|
|
2940
|
+
//#region src/components/chat/ui/chat-message-list/mermaid/chat-mermaid-diagram.tsx
|
|
2941
|
+
const STREAMING_RENDER_DELAY_MS = 300;
|
|
2942
|
+
let mermaidModulePromise = null;
|
|
2943
|
+
function loadMermaid() {
|
|
2944
|
+
mermaidModulePromise ??= import("mermaid").then((module) => module.default);
|
|
2945
|
+
return mermaidModulePromise;
|
|
2946
|
+
}
|
|
2947
|
+
function readMermaidTheme() {
|
|
2948
|
+
if (typeof document === "undefined") return "default";
|
|
2949
|
+
const root = document.documentElement;
|
|
2950
|
+
return root.getAttribute("data-theme-appearance") === "dark" || root.classList.contains("dark") ? "dark" : "default";
|
|
2951
|
+
}
|
|
2952
|
+
function useMermaidTheme() {
|
|
2953
|
+
const [theme, setTheme] = useState(readMermaidTheme);
|
|
2954
|
+
useEffect(() => {
|
|
2955
|
+
const root = document.documentElement;
|
|
2956
|
+
const observer = new MutationObserver(() => setTheme(readMermaidTheme()));
|
|
2957
|
+
observer.observe(root, {
|
|
2958
|
+
attributes: true,
|
|
2959
|
+
attributeFilter: ["class", "data-theme-appearance"]
|
|
2960
|
+
});
|
|
2961
|
+
return () => observer.disconnect();
|
|
2962
|
+
}, []);
|
|
2963
|
+
return theme;
|
|
2964
|
+
}
|
|
2965
|
+
function ChatMermaidDiagram({ isStreaming, source, texts }) {
|
|
2966
|
+
const reactId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
2967
|
+
const renderSequence = useRef(0);
|
|
2968
|
+
const theme = useMermaidTheme();
|
|
2969
|
+
const normalizedSource = source.trim();
|
|
2970
|
+
const renderKey = `${theme}:${normalizedSource}`;
|
|
2971
|
+
const [state, setState] = useState(null);
|
|
2972
|
+
const diagramLabel = texts.mermaidDiagramLabel ?? "Mermaid diagram";
|
|
2973
|
+
const errorLabel = texts.mermaidRenderErrorLabel ?? "Diagram could not be rendered";
|
|
2974
|
+
useEffect(() => {
|
|
2975
|
+
let active = true;
|
|
2976
|
+
const renderDiagram = async () => {
|
|
2977
|
+
try {
|
|
2978
|
+
const mermaid = await loadMermaid();
|
|
2979
|
+
if (!active) return;
|
|
2980
|
+
renderSequence.current += 1;
|
|
2981
|
+
const diagramId = `nextclaw-mermaid-${reactId}-${renderSequence.current}`;
|
|
2982
|
+
mermaid.initialize({
|
|
2983
|
+
securityLevel: "strict",
|
|
2984
|
+
startOnLoad: false,
|
|
2985
|
+
suppressErrorRendering: true,
|
|
2986
|
+
theme
|
|
2987
|
+
});
|
|
2988
|
+
const valid = await mermaid.parse(normalizedSource, { suppressErrors: true });
|
|
2989
|
+
if (!active) return;
|
|
2990
|
+
if (!valid) throw new Error("Invalid Mermaid diagram");
|
|
2991
|
+
const { svg } = await mermaid.render(diagramId, normalizedSource);
|
|
2992
|
+
if (active) setState({
|
|
2993
|
+
key: renderKey,
|
|
2994
|
+
status: "rendered",
|
|
2995
|
+
svg
|
|
2996
|
+
});
|
|
2997
|
+
} catch {
|
|
2998
|
+
if (active && !isStreaming) setState({
|
|
2999
|
+
key: renderKey,
|
|
3000
|
+
status: "error"
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
};
|
|
3004
|
+
const timeout = window.setTimeout(() => void renderDiagram(), isStreaming ? STREAMING_RENDER_DELAY_MS : 0);
|
|
3005
|
+
return () => {
|
|
3006
|
+
active = false;
|
|
3007
|
+
window.clearTimeout(timeout);
|
|
3008
|
+
};
|
|
3009
|
+
}, [
|
|
3010
|
+
isStreaming,
|
|
3011
|
+
normalizedSource,
|
|
3012
|
+
reactId,
|
|
3013
|
+
renderKey,
|
|
3014
|
+
theme
|
|
3015
|
+
]);
|
|
3016
|
+
const currentState = state?.key === renderKey ? state : null;
|
|
3017
|
+
if (currentState?.status === "error") return /* @__PURE__ */ jsxs("div", {
|
|
3018
|
+
"data-chat-mermaid-error": "true",
|
|
3019
|
+
className: "my-3 overflow-hidden rounded-xl border border-border bg-muted/25",
|
|
3020
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
3021
|
+
className: "border-b border-border px-3 py-2 text-xs text-muted-foreground",
|
|
3022
|
+
children: errorLabel
|
|
3023
|
+
}), /* @__PURE__ */ jsx(ChatCodeBlock, {
|
|
3024
|
+
className: "language-mermaid",
|
|
3025
|
+
texts,
|
|
3026
|
+
children: normalizedSource
|
|
3027
|
+
})]
|
|
3028
|
+
});
|
|
3029
|
+
const renderedState = state?.status === "rendered" ? state : null;
|
|
3030
|
+
if (!renderedState) return /* @__PURE__ */ jsx(ChatCodeBlock, {
|
|
3031
|
+
className: "language-mermaid",
|
|
3032
|
+
texts,
|
|
3033
|
+
children: normalizedSource
|
|
3034
|
+
});
|
|
3035
|
+
return /* @__PURE__ */ jsx("figure", {
|
|
3036
|
+
"aria-label": diagramLabel,
|
|
3037
|
+
"aria-busy": !currentState,
|
|
3038
|
+
"data-chat-mermaid-diagram": "true",
|
|
3039
|
+
"data-chat-mermaid-updating": !currentState || void 0,
|
|
3040
|
+
className: "my-3 min-h-24 overflow-auto rounded-xl border border-border bg-muted/20 p-3",
|
|
3041
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
3042
|
+
className: "min-w-fit [&_svg]:mx-auto [&_svg]:h-auto [&_svg]:max-w-full",
|
|
3043
|
+
dangerouslySetInnerHTML: { __html: renderedState.svg }
|
|
3044
|
+
})
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
3047
|
+
//#endregion
|
|
3048
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-file/chat-message-image-preview.tsx
|
|
3049
|
+
function ChatMessageImageLightbox({ alt, closeLabel, onClose, src }) {
|
|
3050
|
+
const titleId = useId();
|
|
3051
|
+
useEffect(() => {
|
|
3052
|
+
const previousOverflow = document.body.style.overflow;
|
|
3053
|
+
document.body.style.overflow = "hidden";
|
|
3054
|
+
const onKeyDown = (event) => {
|
|
3055
|
+
if (event.key === "Escape") {
|
|
3056
|
+
event.preventDefault();
|
|
3057
|
+
onClose();
|
|
3058
|
+
}
|
|
3059
|
+
};
|
|
3060
|
+
window.addEventListener("keydown", onKeyDown);
|
|
3061
|
+
return () => {
|
|
3062
|
+
document.body.style.overflow = previousOverflow;
|
|
3063
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
3064
|
+
};
|
|
3065
|
+
}, [onClose]);
|
|
3066
|
+
return createPortal(/* @__PURE__ */ jsxs("div", {
|
|
3067
|
+
role: "dialog",
|
|
3068
|
+
"aria-modal": "true",
|
|
3069
|
+
"aria-labelledby": titleId,
|
|
3070
|
+
className: "fixed inset-0 z-[var(--z-modal,10050)] flex items-center justify-center bg-black/80 p-4 backdrop-blur-[2px]",
|
|
3071
|
+
"data-testid": "chat-message-image-lightbox",
|
|
3072
|
+
onClick: onClose,
|
|
3073
|
+
children: [
|
|
3074
|
+
/* @__PURE__ */ jsx("span", {
|
|
3075
|
+
id: titleId,
|
|
3076
|
+
className: "sr-only",
|
|
3077
|
+
children: alt
|
|
3078
|
+
}),
|
|
3079
|
+
/* @__PURE__ */ jsx("button", {
|
|
3080
|
+
type: "button",
|
|
3081
|
+
"aria-label": closeLabel,
|
|
3082
|
+
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",
|
|
3083
|
+
onClick: (event) => {
|
|
3084
|
+
event.stopPropagation();
|
|
3085
|
+
onClose();
|
|
3086
|
+
},
|
|
3087
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
3088
|
+
className: "h-4 w-4",
|
|
3089
|
+
strokeWidth: 2
|
|
3090
|
+
})
|
|
3091
|
+
}),
|
|
3092
|
+
/* @__PURE__ */ jsx("img", {
|
|
3093
|
+
src,
|
|
3094
|
+
alt,
|
|
3095
|
+
className: "max-h-[min(92vh,100%)] max-w-[min(96vw,100%)] object-contain shadow-2xl",
|
|
3096
|
+
onClick: (event) => event.stopPropagation()
|
|
3097
|
+
})
|
|
3098
|
+
]
|
|
3099
|
+
}), document.body);
|
|
3100
|
+
}
|
|
3101
|
+
function ChatMessageImagePreview({ alt, expandLabel, closeLabel, sizeLabel, src }) {
|
|
3102
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
3103
|
+
const openLightbox = () => setIsExpanded(true);
|
|
3104
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
3105
|
+
"data-chat-message-image-preview": true,
|
|
3106
|
+
className: "group/image relative block w-fit max-w-[min(100%,32rem)] overflow-hidden rounded-lg",
|
|
3107
|
+
children: [
|
|
3108
|
+
/* @__PURE__ */ jsx("button", {
|
|
3109
|
+
type: "button",
|
|
3110
|
+
className: "block w-fit max-w-[min(100%,32rem)] text-left focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
|
|
3111
|
+
onClick: openLightbox,
|
|
3112
|
+
onDoubleClick: (event) => {
|
|
3113
|
+
event.preventDefault();
|
|
3114
|
+
openLightbox();
|
|
3115
|
+
},
|
|
3116
|
+
"aria-label": expandLabel,
|
|
3117
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
3118
|
+
src,
|
|
3119
|
+
alt,
|
|
3120
|
+
className: "block h-auto w-auto max-h-[26rem] max-w-full rounded-lg bg-transparent object-contain"
|
|
3121
|
+
})
|
|
3122
|
+
}),
|
|
3123
|
+
/* @__PURE__ */ jsx("button", {
|
|
3124
|
+
type: "button",
|
|
3125
|
+
"aria-label": expandLabel,
|
|
3126
|
+
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",
|
|
3127
|
+
onClick: (event) => {
|
|
3128
|
+
event.preventDefault();
|
|
3129
|
+
event.stopPropagation();
|
|
3130
|
+
openLightbox();
|
|
3131
|
+
},
|
|
3132
|
+
children: /* @__PURE__ */ jsx(Expand, {
|
|
3133
|
+
className: "h-3.5 w-3.5",
|
|
3134
|
+
strokeWidth: 2
|
|
3135
|
+
})
|
|
3136
|
+
}),
|
|
3137
|
+
sizeLabel ? /* @__PURE__ */ jsx("span", {
|
|
3138
|
+
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",
|
|
3139
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
3140
|
+
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",
|
|
3141
|
+
children: sizeLabel
|
|
3142
|
+
})
|
|
3143
|
+
}) : null
|
|
3144
|
+
]
|
|
3145
|
+
}), isExpanded ? /* @__PURE__ */ jsx(ChatMessageImageLightbox, {
|
|
3146
|
+
alt,
|
|
3147
|
+
closeLabel,
|
|
3148
|
+
onClose: () => setIsExpanded(false),
|
|
3149
|
+
src
|
|
3150
|
+
}) : null] });
|
|
3151
|
+
}
|
|
3152
|
+
//#endregion
|
|
3153
|
+
//#region src/components/chat/ui/chat-message-list/utils/chat-local-resource.utils.ts
|
|
3154
|
+
const SAFE_BROWSER_LINK_PROTOCOLS = new Set([
|
|
2935
3155
|
"http:",
|
|
2936
3156
|
"https:",
|
|
2937
3157
|
"mailto:",
|
|
2938
3158
|
"tel:"
|
|
2939
3159
|
]);
|
|
2940
|
-
const
|
|
2941
|
-
const
|
|
2942
|
-
const
|
|
2943
|
-
const
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
if (value.length <= MARKDOWN_MAX_CHARS) return value;
|
|
2947
|
-
return `${value.slice(0, MARKDOWN_MAX_CHARS)}\n\n...`;
|
|
3160
|
+
const URI_SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z\d+.-]*:/;
|
|
3161
|
+
const WINDOWS_ABSOLUTE_PATH_PATTERN = /^[a-z]:[\\/]/i;
|
|
3162
|
+
const WINDOWS_FILE_URI_PATH_PATTERN = /^\/[a-z]:[\\/]/i;
|
|
3163
|
+
const FILE_URI_PROTOCOL = "file:";
|
|
3164
|
+
function isSchemeLessResourceHref(href) {
|
|
3165
|
+
return !href.startsWith("//") && !URI_SCHEME_PATTERN.test(href);
|
|
2948
3166
|
}
|
|
2949
|
-
function
|
|
2950
|
-
|
|
2951
|
-
|
|
3167
|
+
function readPositiveInteger(value) {
|
|
3168
|
+
const parsed = Number(value);
|
|
3169
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
3170
|
+
}
|
|
3171
|
+
function readFileUriHref(href) {
|
|
3172
|
+
let url;
|
|
2952
3173
|
try {
|
|
2953
|
-
|
|
2954
|
-
|
|
3174
|
+
url = new URL(href);
|
|
3175
|
+
} catch {
|
|
3176
|
+
return null;
|
|
3177
|
+
}
|
|
3178
|
+
if (url.protocol !== FILE_URI_PROTOCOL || url.hostname && url.hostname.toLowerCase() !== "localhost") return null;
|
|
3179
|
+
let path;
|
|
3180
|
+
try {
|
|
3181
|
+
path = decodeURIComponent(url.pathname);
|
|
2955
3182
|
} catch {
|
|
2956
3183
|
return null;
|
|
2957
3184
|
}
|
|
3185
|
+
if (WINDOWS_FILE_URI_PATH_PATTERN.test(path)) path = path.slice(1);
|
|
3186
|
+
return path ? {
|
|
3187
|
+
path,
|
|
3188
|
+
query: url.searchParams,
|
|
3189
|
+
fragment: url.hash.slice(1)
|
|
3190
|
+
} : null;
|
|
3191
|
+
}
|
|
3192
|
+
function readSchemeLessFileHref(href) {
|
|
3193
|
+
const hashIndex = href.indexOf("#");
|
|
3194
|
+
const hrefWithoutFragment = hashIndex >= 0 ? href.slice(0, hashIndex) : href;
|
|
3195
|
+
const fragment = hashIndex >= 0 ? href.slice(hashIndex + 1) : "";
|
|
3196
|
+
const queryIndex = hrefWithoutFragment.indexOf("?");
|
|
3197
|
+
const encodedPath = queryIndex >= 0 ? hrefWithoutFragment.slice(0, queryIndex) : hrefWithoutFragment;
|
|
3198
|
+
const encodedQuery = queryIndex >= 0 ? hrefWithoutFragment.slice(queryIndex + 1) : "";
|
|
3199
|
+
let path;
|
|
3200
|
+
try {
|
|
3201
|
+
path = decodeURIComponent(encodedPath);
|
|
3202
|
+
} catch {
|
|
3203
|
+
return null;
|
|
3204
|
+
}
|
|
3205
|
+
return path ? {
|
|
3206
|
+
path,
|
|
3207
|
+
query: new URLSearchParams(encodedQuery),
|
|
3208
|
+
fragment
|
|
3209
|
+
} : null;
|
|
3210
|
+
}
|
|
3211
|
+
function readLinePosition(fragment) {
|
|
3212
|
+
const match = /^L(\d+)(?:C(\d+))?(?:-L\d+(?:C\d+)?)?$/i.exec(fragment);
|
|
3213
|
+
const line = readPositiveInteger(match?.[1]);
|
|
3214
|
+
return {
|
|
3215
|
+
line,
|
|
3216
|
+
column: line ? readPositiveInteger(match?.[2]) : void 0
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
function transformChatResourceHref(href) {
|
|
3220
|
+
if (WINDOWS_ABSOLUTE_PATH_PATTERN.test(href) || isSchemeLessResourceHref(href)) return href;
|
|
3221
|
+
try {
|
|
3222
|
+
const url = new URL(href);
|
|
3223
|
+
if (SAFE_BROWSER_LINK_PROTOCOLS.has(url.protocol)) return href;
|
|
3224
|
+
return url.protocol === FILE_URI_PROTOCOL && readFileUriHref(href) ? href : "";
|
|
3225
|
+
} catch {
|
|
3226
|
+
return "";
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
function resolveSafeChatResourceHref(href) {
|
|
3230
|
+
if (!href) return null;
|
|
3231
|
+
return transformChatResourceHref(href) || null;
|
|
2958
3232
|
}
|
|
2959
|
-
function
|
|
3233
|
+
function isExternalChatResourceHref(href) {
|
|
2960
3234
|
return /^https?:\/\//i.test(href);
|
|
2961
3235
|
}
|
|
2962
|
-
function
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
const viewer =
|
|
2967
|
-
const
|
|
2968
|
-
const
|
|
2969
|
-
|
|
2970
|
-
const
|
|
2971
|
-
const
|
|
2972
|
-
const line = lineMatch?.[2] ? Number(lineMatch[2]) : void 0;
|
|
2973
|
-
const column = lineMatch?.[3] ? Number(lineMatch[3]) : void 0;
|
|
3236
|
+
function parseChatLocalFileAction(href) {
|
|
3237
|
+
const fileHref = href.toLowerCase().startsWith(FILE_URI_PROTOCOL) ? readFileUriHref(href) : readSchemeLessFileHref(href);
|
|
3238
|
+
const path = fileHref?.path;
|
|
3239
|
+
if (!path || path.startsWith("#") || path.startsWith("//") || !WINDOWS_ABSOLUTE_PATH_PATTERN.test(path) && URI_SCHEME_PATTERN.test(path)) return null;
|
|
3240
|
+
const viewer = fileHref.query.get("viewer");
|
|
3241
|
+
const fragmentPosition = readLinePosition(fileHref.fragment);
|
|
3242
|
+
const lineMatch = /^(.*?)(?::(\d+)(?::(\d+))?)$/.exec(path);
|
|
3243
|
+
const rawPath = lineMatch?.[1] ?? path;
|
|
3244
|
+
const line = fragmentPosition.line ?? readPositiveInteger(lineMatch?.[2]);
|
|
3245
|
+
const column = fragmentPosition.column ?? (line ? readPositiveInteger(lineMatch?.[3]) : void 0);
|
|
2974
3246
|
return {
|
|
2975
3247
|
path: rawPath,
|
|
2976
|
-
label: rawPath.split(
|
|
3248
|
+
label: rawPath.split(/[\\/]/).filter(Boolean).pop() ?? rawPath,
|
|
2977
3249
|
viewMode: "preview",
|
|
2978
3250
|
...viewer === "source" || viewer === "rendered" ? { previewViewer: viewer } : {},
|
|
2979
3251
|
...typeof line === "number" ? { line } : {},
|
|
2980
3252
|
...typeof column === "number" ? { column } : {}
|
|
2981
3253
|
};
|
|
2982
3254
|
}
|
|
3255
|
+
//#endregion
|
|
3256
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-markdown.tsx
|
|
3257
|
+
const MARKDOWN_MAX_CHARS = 14e4;
|
|
3258
|
+
const INLINE_TOKEN_KIND_ATTR = "data-chat-inline-token-kind";
|
|
3259
|
+
const INLINE_TOKEN_KEY_ATTR = "data-chat-inline-token-key";
|
|
3260
|
+
const INLINE_TOKEN_LABEL_ATTR = "data-chat-inline-token-label";
|
|
3261
|
+
const INLINE_TOKEN_RAW_TEXT_ATTR = "data-chat-inline-token-raw-text";
|
|
3262
|
+
function trimMarkdown(value) {
|
|
3263
|
+
if (value.length <= MARKDOWN_MAX_CHARS) return value;
|
|
3264
|
+
return `${value.slice(0, MARKDOWN_MAX_CHARS)}\n\n...`;
|
|
3265
|
+
}
|
|
3266
|
+
function isSingleLineImageParagraph(node) {
|
|
3267
|
+
let imageCount = 0;
|
|
3268
|
+
for (const child of node?.children ?? []) {
|
|
3269
|
+
if (child.type === "text") {
|
|
3270
|
+
const text = child.value ?? "";
|
|
3271
|
+
if (text.trim().length > 0 || text.includes("\n") || text.includes("\r")) return false;
|
|
3272
|
+
continue;
|
|
3273
|
+
}
|
|
3274
|
+
if (child.tagName !== "img") return false;
|
|
3275
|
+
imageCount += 1;
|
|
3276
|
+
}
|
|
3277
|
+
return imageCount >= 3;
|
|
3278
|
+
}
|
|
2983
3279
|
function prepareInlineTokens(inlineTokens) {
|
|
2984
3280
|
if (!inlineTokens || inlineTokens.length === 0) return [];
|
|
2985
3281
|
const seenRawTexts = /* @__PURE__ */ new Set();
|
|
@@ -3046,144 +3342,170 @@ function splitTextNodeByInlineTokens(value, tokens) {
|
|
|
3046
3342
|
}];
|
|
3047
3343
|
}
|
|
3048
3344
|
function transformInlineTokenTextNodes(node, tokens) {
|
|
3049
|
-
if (node.type === "code" || node.type === "inlineCode" || !node.children) return;
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
continue;
|
|
3055
|
-
}
|
|
3056
|
-
transformInlineTokenTextNodes(child, tokens);
|
|
3057
|
-
nextChildren.push(child);
|
|
3058
|
-
}
|
|
3059
|
-
node.children = nextChildren;
|
|
3345
|
+
if (node.type === "code" || node.type === "inlineCode" || !node.children) return node;
|
|
3346
|
+
return {
|
|
3347
|
+
...node,
|
|
3348
|
+
children: node.children.flatMap((child) => child.type === "text" && typeof child.value === "string" ? splitTextNodeByInlineTokens(child.value, tokens) : [transformInlineTokenTextNodes(child, tokens)])
|
|
3349
|
+
};
|
|
3060
3350
|
}
|
|
3061
3351
|
function createRemarkInlineTokenPlugin(inlineTokens) {
|
|
3062
3352
|
const tokens = prepareInlineTokens(inlineTokens);
|
|
3063
|
-
return () => (tree) =>
|
|
3064
|
-
if (tokens.length === 0) return;
|
|
3065
|
-
transformInlineTokenTextNodes(tree, tokens);
|
|
3066
|
-
};
|
|
3353
|
+
return () => (tree) => tokens.length > 0 ? transformInlineTokenTextNodes(tree, tokens) : tree;
|
|
3067
3354
|
}
|
|
3068
3355
|
function readStringProp(props, key) {
|
|
3069
3356
|
const value = props[key];
|
|
3070
3357
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
3071
3358
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
const
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3359
|
+
const ChatMessageMarkdownRuntimeContext = createContext(null);
|
|
3360
|
+
function useChatMessageMarkdownRuntime() {
|
|
3361
|
+
const runtime = useContext(ChatMessageMarkdownRuntimeContext);
|
|
3362
|
+
if (!runtime) throw new Error("Chat message Markdown renderer requires its runtime context");
|
|
3363
|
+
return runtime;
|
|
3364
|
+
}
|
|
3365
|
+
const CHAT_MESSAGE_MARKDOWN_COMPONENTS = {
|
|
3366
|
+
p: function ChatMarkdownParagraph({ node, children }) {
|
|
3367
|
+
const { inline } = useChatMessageMarkdownRuntime();
|
|
3368
|
+
return inline ? /* @__PURE__ */ jsx(Fragment$1, { children }) : /* @__PURE__ */ jsx("p", {
|
|
3369
|
+
"data-chat-image-row": isSingleLineImageParagraph(node) ? "three-column" : void 0,
|
|
3370
|
+
children
|
|
3371
|
+
});
|
|
3372
|
+
},
|
|
3373
|
+
span: function ChatMarkdownSpan({ node: _node, children, ...rest }) {
|
|
3374
|
+
const { isUser, onInlineTokenClick } = useChatMessageMarkdownRuntime();
|
|
3375
|
+
const restProps = rest;
|
|
3376
|
+
const kind = readStringProp(restProps, INLINE_TOKEN_KIND_ATTR);
|
|
3377
|
+
const key = readStringProp(restProps, INLINE_TOKEN_KEY_ATTR);
|
|
3378
|
+
const label = readStringProp(restProps, INLINE_TOKEN_LABEL_ATTR);
|
|
3379
|
+
const rawText = readStringProp(restProps, INLINE_TOKEN_RAW_TEXT_ATTR);
|
|
3380
|
+
if (kind && key && label && rawText) return /* @__PURE__ */ jsx(ChatInlineTokenBadge, {
|
|
3381
|
+
kind,
|
|
3382
|
+
label,
|
|
3383
|
+
isUser,
|
|
3384
|
+
onClick: onInlineTokenClick ? () => onInlineTokenClick({
|
|
3084
3385
|
kind,
|
|
3386
|
+
key,
|
|
3085
3387
|
label,
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
if (!safeHref) return /* @__PURE__ */ jsx("span", {
|
|
3102
|
-
className: "chat-link-invalid",
|
|
3103
|
-
children
|
|
3104
|
-
});
|
|
3105
|
-
const external = isExternalHref(safeHref);
|
|
3106
|
-
const localFileAction = external ? null : parseLocalFileAction(safeHref);
|
|
3107
|
-
const handleClick = (event) => {
|
|
3108
|
-
if (!onFileOpen || !localFileAction) return;
|
|
3109
|
-
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
3388
|
+
rawText
|
|
3389
|
+
}) : void 0
|
|
3390
|
+
});
|
|
3391
|
+
return /* @__PURE__ */ jsx("span", {
|
|
3392
|
+
...rest,
|
|
3393
|
+
children
|
|
3394
|
+
});
|
|
3395
|
+
},
|
|
3396
|
+
a: function ChatMarkdownLink({ node: _node, href, children, ...rest }) {
|
|
3397
|
+
const { onFileOpen } = useChatMessageMarkdownRuntime();
|
|
3398
|
+
const safeHref = resolveSafeChatResourceHref(href);
|
|
3399
|
+
const external = safeHref ? isExternalChatResourceHref(safeHref) : false;
|
|
3400
|
+
const localFileAction = external ? null : safeHref ? parseChatLocalFileAction(safeHref) : null;
|
|
3401
|
+
const handleClick = (event) => {
|
|
3402
|
+
if (!safeHref) {
|
|
3110
3403
|
event.preventDefault();
|
|
3111
|
-
|
|
3112
|
-
}
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3406
|
+
if (!onFileOpen || !localFileAction) return;
|
|
3407
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
3408
|
+
event.preventDefault();
|
|
3409
|
+
onFileOpen(localFileAction);
|
|
3410
|
+
};
|
|
3411
|
+
return /* @__PURE__ */ jsx("a", {
|
|
3412
|
+
...rest,
|
|
3413
|
+
className: cn(rest.className, !safeHref && "chat-link-invalid"),
|
|
3414
|
+
href: safeHref ?? "#",
|
|
3415
|
+
"aria-disabled": !safeHref || void 0,
|
|
3416
|
+
onClick: handleClick,
|
|
3417
|
+
target: external ? "_blank" : void 0,
|
|
3418
|
+
rel: external ? "noreferrer noopener" : void 0,
|
|
3419
|
+
children
|
|
3420
|
+
});
|
|
3421
|
+
},
|
|
3422
|
+
table: function ChatMarkdownTable({ node: _node, children, ...rest }) {
|
|
3423
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3123
3424
|
className: "chat-table-wrap",
|
|
3124
3425
|
children: /* @__PURE__ */ jsx("table", {
|
|
3125
3426
|
...rest,
|
|
3126
3427
|
children
|
|
3127
3428
|
})
|
|
3128
|
-
})
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3429
|
+
});
|
|
3430
|
+
},
|
|
3431
|
+
input: function ChatMarkdownInput({ node: _node, type, checked, ...rest }) {
|
|
3432
|
+
if (type !== "checkbox") return /* @__PURE__ */ jsx("input", {
|
|
3433
|
+
...rest,
|
|
3434
|
+
type
|
|
3435
|
+
});
|
|
3436
|
+
return /* @__PURE__ */ jsx("input", {
|
|
3437
|
+
...rest,
|
|
3438
|
+
type: "checkbox",
|
|
3439
|
+
checked,
|
|
3440
|
+
readOnly: true,
|
|
3441
|
+
disabled: true,
|
|
3442
|
+
className: "chat-task-checkbox"
|
|
3443
|
+
});
|
|
3444
|
+
},
|
|
3445
|
+
img: function ChatMarkdownImage({ node: _node, src, alt }) {
|
|
3446
|
+
const { resolveFileContentUrl, texts } = useChatMessageMarkdownRuntime();
|
|
3447
|
+
const safeSrc = resolveSafeChatResourceHref(src);
|
|
3448
|
+
if (!safeSrc) return null;
|
|
3449
|
+
const localFileAction = parseChatLocalFileAction(safeSrc);
|
|
3450
|
+
const resolvedSrc = localFileAction && resolveFileContentUrl ? resolveFileContentUrl(localFileAction) : safeSrc;
|
|
3451
|
+
if (!resolvedSrc) return null;
|
|
3452
|
+
return /* @__PURE__ */ jsx(ChatMessageImagePreview, {
|
|
3453
|
+
alt: alt || "",
|
|
3454
|
+
closeLabel: texts.attachmentCloseLabel ?? "Close preview",
|
|
3455
|
+
expandLabel: texts.attachmentExpandLabel ?? "Expand image",
|
|
3456
|
+
sizeLabel: null,
|
|
3457
|
+
src: resolvedSrc
|
|
3458
|
+
});
|
|
3459
|
+
},
|
|
3460
|
+
code: function ChatMarkdownCode({ node: _node, className, children, ...rest }) {
|
|
3461
|
+
const { isStreaming, renderInlineDisplay, texts } = useChatMessageMarkdownRuntime();
|
|
3462
|
+
const plainText = String(children ?? "");
|
|
3463
|
+
if (!className && !plainText.includes("\n")) return /* @__PURE__ */ jsx("code", {
|
|
3464
|
+
...rest,
|
|
3465
|
+
className: cn("chat-inline-code", className),
|
|
3466
|
+
children
|
|
3467
|
+
});
|
|
3468
|
+
const inlineDisplay = isChatInlineDisplayLanguage(className) ? parseChatInlineDisplayDirective(plainText) : null;
|
|
3469
|
+
if (inlineDisplay) return /* @__PURE__ */ jsx(ChatInlineDisplay, {
|
|
3470
|
+
display: inlineDisplay,
|
|
3471
|
+
renderInlineDisplay
|
|
3472
|
+
});
|
|
3473
|
+
if (className?.split(" ").includes("language-mermaid")) return /* @__PURE__ */ jsx(ChatMermaidDiagram, {
|
|
3474
|
+
isStreaming,
|
|
3475
|
+
source: plainText,
|
|
3476
|
+
texts
|
|
3477
|
+
});
|
|
3478
|
+
return /* @__PURE__ */ jsx(ChatCodeBlock, {
|
|
3479
|
+
className,
|
|
3480
|
+
texts,
|
|
3481
|
+
children
|
|
3482
|
+
});
|
|
3483
|
+
}
|
|
3484
|
+
};
|
|
3485
|
+
function ChatMessageMarkdown({ text, role, texts, inline = false, isStreaming = false, inlineTokens, onFileOpen, onInlineTokenClick, resolveFileContentUrl, renderInlineDisplay }) {
|
|
3486
|
+
const isUser = role === "user";
|
|
3487
|
+
const remarkPlugins = inlineTokens?.length ? [remarkGfm, createRemarkInlineTokenPlugin(inlineTokens)] : [remarkGfm];
|
|
3488
|
+
const WrapperTag = inline ? "span" : "div";
|
|
3489
|
+
return /* @__PURE__ */ jsx(ChatMessageMarkdownRuntimeContext.Provider, {
|
|
3490
|
+
value: {
|
|
3491
|
+
inline,
|
|
3492
|
+
isStreaming,
|
|
3493
|
+
isUser,
|
|
3494
|
+
onFileOpen,
|
|
3495
|
+
onInlineTokenClick,
|
|
3496
|
+
renderInlineDisplay,
|
|
3497
|
+
resolveFileContentUrl,
|
|
3498
|
+
texts
|
|
3153
3499
|
},
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
display: inlineDisplay,
|
|
3164
|
-
renderInlineDisplay
|
|
3165
|
-
});
|
|
3166
|
-
return /* @__PURE__ */ jsx(ChatCodeBlock, {
|
|
3167
|
-
className,
|
|
3168
|
-
texts,
|
|
3169
|
-
children
|
|
3170
|
-
});
|
|
3171
|
-
}
|
|
3172
|
-
}), [
|
|
3173
|
-
inline,
|
|
3174
|
-
isUser,
|
|
3175
|
-
onFileOpen,
|
|
3176
|
-
onInlineTokenClick,
|
|
3177
|
-
renderInlineDisplay,
|
|
3178
|
-
texts
|
|
3179
|
-
]);
|
|
3180
|
-
return /* @__PURE__ */ jsx(inline ? "span" : "div", {
|
|
3181
|
-
className: cn("chat-markdown", isUser ? "chat-markdown-user" : "chat-markdown-assistant"),
|
|
3182
|
-
children: /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
3183
|
-
skipHtml: true,
|
|
3184
|
-
remarkPlugins,
|
|
3185
|
-
components: markdownComponents,
|
|
3186
|
-
children: trimMarkdown(text)
|
|
3500
|
+
children: /* @__PURE__ */ jsx(WrapperTag, {
|
|
3501
|
+
className: cn("chat-markdown", isUser ? "chat-markdown-user" : "chat-markdown-assistant"),
|
|
3502
|
+
children: /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
3503
|
+
skipHtml: true,
|
|
3504
|
+
remarkPlugins,
|
|
3505
|
+
components: CHAT_MESSAGE_MARKDOWN_COMPONENTS,
|
|
3506
|
+
urlTransform: transformChatResourceHref,
|
|
3507
|
+
children: trimMarkdown(text)
|
|
3508
|
+
})
|
|
3187
3509
|
})
|
|
3188
3510
|
});
|
|
3189
3511
|
}
|
|
@@ -3451,109 +3773,6 @@ function FileCategoryGlyph({ category, isUser }) {
|
|
|
3451
3773
|
})
|
|
3452
3774
|
});
|
|
3453
3775
|
}
|
|
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
3776
|
function renderFileCardHeader(params) {
|
|
3558
3777
|
const { category, file, categoryLabel, sizeLabel, isUser, action } = params;
|
|
3559
3778
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -3633,11 +3852,12 @@ function ChatMessageFile({ file, isUser = false, texts, onOpen }) {
|
|
|
3633
3852
|
const closeLabel = texts?.attachmentCloseLabel ?? "Close preview";
|
|
3634
3853
|
const categoryLabel = readFileCategoryLabel(category, texts);
|
|
3635
3854
|
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, {
|
|
3855
|
+
if (renderAsImage && file.dataUrl) return /* @__PURE__ */ jsx(ChatMessageImagePreview, {
|
|
3856
|
+
alt: file.label,
|
|
3637
3857
|
expandLabel,
|
|
3638
3858
|
closeLabel,
|
|
3639
|
-
|
|
3640
|
-
|
|
3859
|
+
sizeLabel,
|
|
3860
|
+
src: file.dataUrl
|
|
3641
3861
|
});
|
|
3642
3862
|
if (renderAsAudio || renderAsVideo) return renderInlineMediaCard({
|
|
3643
3863
|
category,
|
|
@@ -3676,6 +3896,54 @@ function ChatMessageFile({ file, isUser = false, texts, onOpen }) {
|
|
|
3676
3896
|
});
|
|
3677
3897
|
}
|
|
3678
3898
|
//#endregion
|
|
3899
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-file/chat-message-image-row.tsx
|
|
3900
|
+
function ChatMessageImageRow({ group, indexOffset, isUser, texts, onOpen }) {
|
|
3901
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3902
|
+
"data-chat-message-image-row": "three-column",
|
|
3903
|
+
"data-chat-message-wide-content": "true",
|
|
3904
|
+
className: "grid w-full grid-cols-3 items-start gap-3",
|
|
3905
|
+
children: group.items.map(({ index, part }) => /* @__PURE__ */ jsx(ChatMessageFile, {
|
|
3906
|
+
file: part.file,
|
|
3907
|
+
isUser,
|
|
3908
|
+
texts,
|
|
3909
|
+
onOpen
|
|
3910
|
+
}, `file-${indexOffset + index}`))
|
|
3911
|
+
});
|
|
3912
|
+
}
|
|
3913
|
+
//#endregion
|
|
3914
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-file/chat-message-image-group.utils.ts
|
|
3915
|
+
function isPreviewableImageBlock(block) {
|
|
3916
|
+
return Boolean(block?.kind === "part" && block.part.type === "file" && block.part.file.isImage && block.part.file.dataUrl);
|
|
3917
|
+
}
|
|
3918
|
+
function groupConsecutiveImageFileBlocks(blocks) {
|
|
3919
|
+
const grouped = [];
|
|
3920
|
+
let index = 0;
|
|
3921
|
+
while (index < blocks.length) {
|
|
3922
|
+
const block = blocks[index];
|
|
3923
|
+
if (!isPreviewableImageBlock(block)) {
|
|
3924
|
+
if (block) grouped.push(block);
|
|
3925
|
+
index += 1;
|
|
3926
|
+
continue;
|
|
3927
|
+
}
|
|
3928
|
+
const startIndex = index;
|
|
3929
|
+
const items = [];
|
|
3930
|
+
while (isPreviewableImageBlock(blocks[index])) {
|
|
3931
|
+
items.push(blocks[index]);
|
|
3932
|
+
index += 1;
|
|
3933
|
+
}
|
|
3934
|
+
if (items.length < 3) {
|
|
3935
|
+
grouped.push(...items);
|
|
3936
|
+
continue;
|
|
3937
|
+
}
|
|
3938
|
+
grouped.push({
|
|
3939
|
+
kind: "image-group",
|
|
3940
|
+
key: `image-group-${startIndex}`,
|
|
3941
|
+
items
|
|
3942
|
+
});
|
|
3943
|
+
}
|
|
3944
|
+
return grouped;
|
|
3945
|
+
}
|
|
3946
|
+
//#endregion
|
|
3679
3947
|
//#region src/components/chat/hooks/use-reasoning-block-open-state.ts
|
|
3680
3948
|
function useReasoningBlockOpenState(params) {
|
|
3681
3949
|
const { isInProgress } = params;
|
|
@@ -3711,7 +3979,6 @@ const DEFAULT_STICKY_THRESHOLD_PX = 10;
|
|
|
3711
3979
|
function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey, scrollRef, stickyThresholdPx }) {
|
|
3712
3980
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
3713
3981
|
const isStickyRef = useRef(true);
|
|
3714
|
-
const isProgrammaticScrollRef = useRef(false);
|
|
3715
3982
|
const previousResetKeyRef = useRef(null);
|
|
3716
3983
|
const pendingInitialScrollRef = useRef(false);
|
|
3717
3984
|
const scheduledScrollFrameRef = useRef(null);
|
|
@@ -3729,7 +3996,6 @@ function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey
|
|
|
3729
3996
|
scheduledScrollFrameRef.current = null;
|
|
3730
3997
|
const currentElement = scrollRef.current;
|
|
3731
3998
|
if (!currentElement) return;
|
|
3732
|
-
isProgrammaticScrollRef.current = true;
|
|
3733
3999
|
if (typeof currentElement.scrollTo === "function") currentElement.scrollTo({
|
|
3734
4000
|
top: currentElement.scrollHeight,
|
|
3735
4001
|
behavior
|
|
@@ -3742,10 +4008,6 @@ function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey
|
|
|
3742
4008
|
queueScrollToBottom();
|
|
3743
4009
|
}, [queueScrollToBottom, updateStickyState]);
|
|
3744
4010
|
const onScroll = useCallback(() => {
|
|
3745
|
-
if (isProgrammaticScrollRef.current) {
|
|
3746
|
-
isProgrammaticScrollRef.current = false;
|
|
3747
|
-
return;
|
|
3748
|
-
}
|
|
3749
4011
|
const element = scrollRef.current;
|
|
3750
4012
|
if (!element) return;
|
|
3751
4013
|
updateStickyState(resolveIsAtBottom(element));
|
|
@@ -3803,15 +4065,23 @@ function useStickyBottomScroll({ contentVersion, hasContent, isLoading, resetKey
|
|
|
3803
4065
|
* Collapsed process/tool rows should feel like ordinary body text lines.
|
|
3804
4066
|
*/
|
|
3805
4067
|
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";
|
|
4068
|
+
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
4069
|
function ChatProcessLeadingIcon({ children, className }) {
|
|
3808
4070
|
return /* @__PURE__ */ jsx("span", {
|
|
3809
4071
|
className: cn(CHAT_PROCESS_LEADING_COL_CLASS, className),
|
|
3810
4072
|
children
|
|
3811
4073
|
});
|
|
3812
4074
|
}
|
|
4075
|
+
function ChatProcessWorkflowRail({ position }) {
|
|
4076
|
+
return /* @__PURE__ */ jsx("span", {
|
|
4077
|
+
"aria-hidden": "true",
|
|
4078
|
+
"data-tool-workflow-rail": "true",
|
|
4079
|
+
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")
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
3813
4082
|
function ChatProcessMetaRow({ children, className, interactive = false, onClick, role, tabIndex, onKeyDown }) {
|
|
3814
4083
|
return /* @__PURE__ */ jsx("div", {
|
|
4084
|
+
"data-chat-process-meta-row": "true",
|
|
3815
4085
|
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
4086
|
onClick,
|
|
3817
4087
|
role,
|
|
@@ -3827,22 +4097,33 @@ function ChatProcessMetaRow({ children, className, interactive = false, onClick,
|
|
|
3827
4097
|
* Rendered as a plain button-like row (not <summary>) so browsers never inject
|
|
3828
4098
|
* a default disclosure label such as "详情" / "Details".
|
|
3829
4099
|
*/
|
|
3830
|
-
function ChatCollapsibleMetaSummary({ label, openGroup, open = false, className, labelClassName, onClick }) {
|
|
4100
|
+
function ChatCollapsibleMetaSummary({ label, openGroup, open = false, icon: Icon, className, leadingIconClassName, labelClassName, onClick }) {
|
|
3831
4101
|
return /* @__PURE__ */ jsxs("button", {
|
|
3832
4102
|
type: "button",
|
|
4103
|
+
"data-chat-process-meta-row": "true",
|
|
3833
4104
|
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
4105
|
"aria-expanded": open,
|
|
3835
4106
|
onClick,
|
|
3836
|
-
children: [
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
4107
|
+
children: [
|
|
4108
|
+
Icon ? /* @__PURE__ */ jsx(ChatProcessLeadingIcon, {
|
|
4109
|
+
className: cn("relative z-[1] rounded-sm", leadingIconClassName),
|
|
4110
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
4111
|
+
className: "h-[1.05em] w-[1.05em]",
|
|
4112
|
+
strokeWidth: 2.25
|
|
4113
|
+
})
|
|
4114
|
+
}) : null,
|
|
4115
|
+
/* @__PURE__ */ jsx("span", {
|
|
4116
|
+
className: cn("min-w-0 shrink truncate", labelClassName),
|
|
4117
|
+
children: label
|
|
4118
|
+
}),
|
|
4119
|
+
/* @__PURE__ */ jsx("span", {
|
|
4120
|
+
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"),
|
|
4121
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {
|
|
4122
|
+
className: cn("h-[1.05em] w-[1.05em] transition-transform", open && "rotate-90"),
|
|
4123
|
+
strokeWidth: 2.25
|
|
4124
|
+
})
|
|
3844
4125
|
})
|
|
3845
|
-
|
|
4126
|
+
]
|
|
3846
4127
|
});
|
|
3847
4128
|
}
|
|
3848
4129
|
//#endregion
|
|
@@ -3857,7 +4138,7 @@ function ChatReasoningBlock({ label, text, characterCountTemplates, isUser, isIn
|
|
|
3857
4138
|
const displayLabel = formatReasoningLabel(label, text, isInProgress ? characterCountTemplates?.inProgress : characterCountTemplates?.completed);
|
|
3858
4139
|
const { onScroll } = useStickyBottomScroll({
|
|
3859
4140
|
scrollRef,
|
|
3860
|
-
resetKey:
|
|
4141
|
+
resetKey: "reasoning",
|
|
3861
4142
|
isLoading: false,
|
|
3862
4143
|
hasContent: text.length > 0,
|
|
3863
4144
|
contentVersion: text,
|
|
@@ -3868,6 +4149,8 @@ function ChatReasoningBlock({ label, text, characterCountTemplates, isUser, isIn
|
|
|
3868
4149
|
children: [/* @__PURE__ */ jsx(ChatCollapsibleMetaSummary, {
|
|
3869
4150
|
openGroup: "reasoning",
|
|
3870
4151
|
open: isOpen,
|
|
4152
|
+
icon: Brain,
|
|
4153
|
+
leadingIconClassName: isUser ? "bg-primary" : "bg-card",
|
|
3871
4154
|
label: displayLabel,
|
|
3872
4155
|
labelClassName: isUser ? "text-primary-100" : void 0,
|
|
3873
4156
|
onClick: onSummaryClick
|
|
@@ -3902,6 +4185,38 @@ function ToolCardContent({ children, className }) {
|
|
|
3902
4185
|
children
|
|
3903
4186
|
});
|
|
3904
4187
|
}
|
|
4188
|
+
function ToolCardDetailSection({ label, tone, children }) {
|
|
4189
|
+
const style = {
|
|
4190
|
+
input: {
|
|
4191
|
+
dot: "bg-muted-foreground/60",
|
|
4192
|
+
body: "text-foreground"
|
|
4193
|
+
},
|
|
4194
|
+
output: {
|
|
4195
|
+
dot: "bg-primary/70",
|
|
4196
|
+
body: "text-foreground"
|
|
4197
|
+
},
|
|
4198
|
+
error: {
|
|
4199
|
+
dot: "bg-rose-500/80",
|
|
4200
|
+
body: "text-rose-950/85"
|
|
4201
|
+
}
|
|
4202
|
+
}[tone];
|
|
4203
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
4204
|
+
className: "overflow-hidden rounded-md border border-border/70 bg-muted/20",
|
|
4205
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
4206
|
+
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",
|
|
4207
|
+
children: [/* @__PURE__ */ jsx("span", { className: cn("h-1.5 w-1.5 rounded-full", style.dot) }), /* @__PURE__ */ jsx("span", {
|
|
4208
|
+
className: "normal-case tracking-normal",
|
|
4209
|
+
children: label
|
|
4210
|
+
})]
|
|
4211
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
4212
|
+
className: "w-full overflow-hidden",
|
|
4213
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
4214
|
+
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),
|
|
4215
|
+
children
|
|
4216
|
+
})
|
|
4217
|
+
})]
|
|
4218
|
+
});
|
|
4219
|
+
}
|
|
3905
4220
|
//#endregion
|
|
3906
4221
|
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-status.tsx
|
|
3907
4222
|
const STATUS_STYLES = {
|
|
@@ -4077,6 +4392,9 @@ function readVisibleLineNumber(line) {
|
|
|
4077
4392
|
const value = line.newLineNumber ?? line.oldLineNumber;
|
|
4078
4393
|
return typeof value === "number" ? String(value) : "";
|
|
4079
4394
|
}
|
|
4395
|
+
function isTargetLine(line, targetLine) {
|
|
4396
|
+
return typeof targetLine === "number" && (line.newLineNumber ?? line.oldLineNumber) === targetLine;
|
|
4397
|
+
}
|
|
4080
4398
|
function readLineKey(prefix, line, index) {
|
|
4081
4399
|
return `${prefix}-${index}-${line.oldLineNumber ?? "x"}-${line.newLineNumber ?? "x"}`;
|
|
4082
4400
|
}
|
|
@@ -4120,25 +4438,34 @@ function getCodeRowTone(line) {
|
|
|
4120
4438
|
if (line.kind === "add") return "bg-emerald-50 text-emerald-950";
|
|
4121
4439
|
return "bg-card text-foreground";
|
|
4122
4440
|
}
|
|
4123
|
-
function FileOperationLineNumberCell({ layout, line, lineNumberColumnWidth }) {
|
|
4441
|
+
function FileOperationLineNumberCell({ layout, line, lineNumberColumnWidth, target }) {
|
|
4124
4442
|
return /* @__PURE__ */ jsx("span", {
|
|
4125
4443
|
"data-file-line-number-cell": "true",
|
|
4126
4444
|
style: layout === "compact" ? {
|
|
4127
4445
|
width: lineNumberColumnWidth,
|
|
4128
4446
|
minWidth: lineNumberColumnWidth
|
|
4129
4447
|
} : void 0,
|
|
4130
|
-
className: cn(FILE_LINE_NUMBER_CELL_CLASS_NAME, layout === "compact" ? "sticky left-0 z-[1]" : "w-full shrink-0", getLineNumberTone(line)),
|
|
4448
|
+
className: cn(FILE_LINE_NUMBER_CELL_CLASS_NAME, layout === "compact" ? "sticky left-0 z-[1]" : "w-full shrink-0", getLineNumberTone(line), target ? "bg-gray-200 font-medium text-gray-950" : null),
|
|
4131
4449
|
children: readVisibleLineNumber(line)
|
|
4132
4450
|
});
|
|
4133
4451
|
}
|
|
4134
|
-
function FileOperationCodeCell({ language, line }) {
|
|
4452
|
+
function FileOperationCodeCell({ language, line, target, targetColumn, targetRef }) {
|
|
4135
4453
|
const highlightedCode = useMemo(() => chatCodeSyntaxHighlighter.highlight(line.text || " ", language), [language, line.text]);
|
|
4136
|
-
|
|
4454
|
+
const visibleTargetColumn = typeof targetColumn === "number" && Number.isSafeInteger(targetColumn) && targetColumn > 0 ? Math.min(targetColumn, line.text.length + 1) : null;
|
|
4455
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
4456
|
+
ref: visibleTargetColumn ? void 0 : targetRef,
|
|
4137
4457
|
"data-file-code-row": "true",
|
|
4138
4458
|
"data-file-code-language": highlightedCode.language,
|
|
4139
4459
|
"data-highlighted": highlightedCode.highlighted ? "true" : "false",
|
|
4140
|
-
|
|
4141
|
-
|
|
4460
|
+
"data-file-target-column": visibleTargetColumn ?? void 0,
|
|
4461
|
+
className: cn("chat-file-code-syntax hljs relative block min-w-0 flex-1 whitespace-pre px-2.5", readLanguageClassName(highlightedCode.language), getCodeRowTone(line), target ? "bg-gray-100/90" : null),
|
|
4462
|
+
children: [/* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: highlightedCode.html } }), visibleTargetColumn ? /* @__PURE__ */ jsx("span", {
|
|
4463
|
+
ref: targetRef,
|
|
4464
|
+
"aria-hidden": "true",
|
|
4465
|
+
"data-file-target-caret": "true",
|
|
4466
|
+
className: "pointer-events-none absolute inset-y-0 w-px bg-primary",
|
|
4467
|
+
style: { left: `calc(0.625rem + ${visibleTargetColumn - 1}ch)` }
|
|
4468
|
+
}) : null]
|
|
4142
4469
|
});
|
|
4143
4470
|
}
|
|
4144
4471
|
function FileOperationLineRow({ language, line, showLineNumbers, lineNumberColumnWidth }) {
|
|
@@ -4155,11 +4482,22 @@ function FileOperationLineRow({ language, line, showLineNumbers, lineNumberColum
|
|
|
4155
4482
|
})]
|
|
4156
4483
|
});
|
|
4157
4484
|
}
|
|
4158
|
-
function FileOperationWorkspaceSurface({ block }) {
|
|
4485
|
+
function FileOperationWorkspaceSurface({ block, targetColumn, targetLine }) {
|
|
4159
4486
|
const showLineNumbers = readHasBlockLineNumbers(block);
|
|
4160
4487
|
const lineNumberColumnWidth = readLineNumberColumnWidth(block);
|
|
4161
4488
|
const codeColumnWidth = readCodeColumnWidth(block);
|
|
4162
4489
|
const language = readBlockLanguage(block);
|
|
4490
|
+
const targetRef = useRef(null);
|
|
4491
|
+
useLayoutEffect(() => {
|
|
4492
|
+
targetRef.current?.scrollIntoView({
|
|
4493
|
+
block: "center",
|
|
4494
|
+
inline: targetColumn ? "center" : "nearest"
|
|
4495
|
+
});
|
|
4496
|
+
}, [
|
|
4497
|
+
block,
|
|
4498
|
+
targetColumn,
|
|
4499
|
+
targetLine
|
|
4500
|
+
]);
|
|
4163
4501
|
return /* @__PURE__ */ jsxs("div", {
|
|
4164
4502
|
"data-file-code-surface": "true",
|
|
4165
4503
|
"data-file-code-surface-layout": "workspace",
|
|
@@ -4174,7 +4512,8 @@ function FileOperationWorkspaceSurface({ block }) {
|
|
|
4174
4512
|
children: [block.lines.map((line, index) => /* @__PURE__ */ jsx(FileOperationLineNumberCell, {
|
|
4175
4513
|
layout: "workspace",
|
|
4176
4514
|
line,
|
|
4177
|
-
lineNumberColumnWidth
|
|
4515
|
+
lineNumberColumnWidth,
|
|
4516
|
+
target: isTargetLine(line, targetLine)
|
|
4178
4517
|
}, readLineKey("gutter", line, index))), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 border-r border-border bg-muted" })]
|
|
4179
4518
|
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
4180
4519
|
"data-file-code-canvas": "true",
|
|
@@ -4183,20 +4522,32 @@ function FileOperationWorkspaceSurface({ block }) {
|
|
|
4183
4522
|
"data-file-code-stack": "true",
|
|
4184
4523
|
className: "min-w-full",
|
|
4185
4524
|
style: { minWidth: codeColumnWidth },
|
|
4186
|
-
children: block.lines.map((line, index) =>
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4525
|
+
children: block.lines.map((line, index) => {
|
|
4526
|
+
const target = isTargetLine(line, targetLine);
|
|
4527
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4528
|
+
"data-file-code-canvas-row": "true",
|
|
4529
|
+
"data-file-target-line": target ? "true" : void 0,
|
|
4530
|
+
"aria-current": target ? "location" : void 0,
|
|
4531
|
+
className: FILE_ROW_CLASS_NAME,
|
|
4532
|
+
children: /* @__PURE__ */ jsx(FileOperationCodeCell, {
|
|
4533
|
+
language,
|
|
4534
|
+
line,
|
|
4535
|
+
target,
|
|
4536
|
+
targetColumn: target ? targetColumn : null,
|
|
4537
|
+
targetRef: target ? targetRef : void 0
|
|
4538
|
+
})
|
|
4539
|
+
}, readLineKey("code", line, index));
|
|
4540
|
+
})
|
|
4194
4541
|
}), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-card" })]
|
|
4195
4542
|
})]
|
|
4196
4543
|
});
|
|
4197
4544
|
}
|
|
4198
|
-
function FileOperationCodeSurface({ block, layout = "compact" }) {
|
|
4199
|
-
if (layout === "workspace") return /* @__PURE__ */ jsx(FileOperationWorkspaceSurface, {
|
|
4545
|
+
function FileOperationCodeSurface({ block, layout = "compact", targetColumn, targetLine }) {
|
|
4546
|
+
if (layout === "workspace") return /* @__PURE__ */ jsx(FileOperationWorkspaceSurface, {
|
|
4547
|
+
block,
|
|
4548
|
+
targetColumn,
|
|
4549
|
+
targetLine
|
|
4550
|
+
});
|
|
4200
4551
|
const showLineNumbers = readHasBlockLineNumbers(block);
|
|
4201
4552
|
const lineNumberColumnWidth = readLineNumberColumnWidth(block);
|
|
4202
4553
|
const codeColumnWidth = readCodeColumnWidth(block);
|
|
@@ -4803,9 +5154,12 @@ function TerminalExecutionView({ card, toolLabel }) {
|
|
|
4803
5154
|
})] });
|
|
4804
5155
|
}
|
|
4805
5156
|
function FileOperationView({ card, toolLabel, onFileOpen }) {
|
|
5157
|
+
const input = card.input?.trim() ?? "";
|
|
4806
5158
|
const output = card.output?.trim() ?? "";
|
|
4807
5159
|
const isRunning = card.statusTone === "running";
|
|
4808
|
-
const
|
|
5160
|
+
const hasStructuredPreview = Boolean(card.fileOperation?.blocks.length);
|
|
5161
|
+
const showRawInput = Boolean(input) && !hasStructuredPreview;
|
|
5162
|
+
const hasContent = hasStructuredPreview || Boolean(input) || Boolean(output);
|
|
4809
5163
|
const previewBlocks = card.fileOperation?.blocks ?? [];
|
|
4810
5164
|
const previewLineCount = previewBlocks.reduce((count, block) => count + block.lines.length, 0);
|
|
4811
5165
|
const previewCharCount = previewBlocks.reduce((count, block) => {
|
|
@@ -4830,13 +5184,21 @@ function FileOperationView({ card, toolLabel, onFileOpen }) {
|
|
|
4830
5184
|
canExpand: hasContent || isRunning,
|
|
4831
5185
|
hideSummary: false,
|
|
4832
5186
|
onToggle
|
|
4833
|
-
}), expanded && hasContent ? /* @__PURE__ */
|
|
5187
|
+
}), expanded && hasContent ? /* @__PURE__ */ jsxs(ToolCardContent, {
|
|
4834
5188
|
className: "bg-transparent py-0",
|
|
4835
|
-
children:
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
5189
|
+
children: [
|
|
5190
|
+
showRawInput ? /* @__PURE__ */ jsx(ToolCardDetailSection, {
|
|
5191
|
+
label: card.inputLabel?.trim() || "Input",
|
|
5192
|
+
tone: "input",
|
|
5193
|
+
children: input
|
|
5194
|
+
}) : null,
|
|
5195
|
+
showRawInput && output ? /* @__PURE__ */ jsx("div", { className: "h-2" }) : null,
|
|
5196
|
+
hasStructuredPreview || output ? /* @__PURE__ */ jsx(ToolCardFileOperationContent, {
|
|
5197
|
+
card,
|
|
5198
|
+
onFileOpen,
|
|
5199
|
+
showPathRow: true
|
|
5200
|
+
}) : null
|
|
5201
|
+
]
|
|
4840
5202
|
}) : null] });
|
|
4841
5203
|
}
|
|
4842
5204
|
function SearchSnippetView({ card, toolLabel, icon: Icon = Search }) {
|
|
@@ -4865,38 +5227,6 @@ function SearchSnippetView({ card, toolLabel, icon: Icon = Search }) {
|
|
|
4865
5227
|
}
|
|
4866
5228
|
//#endregion
|
|
4867
5229
|
//#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
5230
|
function buildToolActionSlot(card, onToolAction, renderToolAgent) {
|
|
4901
5231
|
const agentAction = card.agentId && renderToolAgent ? renderToolAgent(card.agentId) : null;
|
|
4902
5232
|
const toolAction = card.action && onToolAction ? /* @__PURE__ */ jsx(ToolCardHeaderAction, {
|
|
@@ -4930,13 +5260,13 @@ function GenericToolCard({ card, toolLabel, icon: Icon = Wrench, onToolAction, r
|
|
|
4930
5260
|
}), expanded && hasContent ? /* @__PURE__ */ jsxs(ToolCardContent, {
|
|
4931
5261
|
className: "bg-transparent py-0",
|
|
4932
5262
|
children: [
|
|
4933
|
-
hasInputSection ? /* @__PURE__ */ jsx(
|
|
5263
|
+
hasInputSection ? /* @__PURE__ */ jsx(ToolCardDetailSection, {
|
|
4934
5264
|
label: card.inputLabel?.trim() || "Input",
|
|
4935
5265
|
tone: "input",
|
|
4936
5266
|
children: input
|
|
4937
5267
|
}) : null,
|
|
4938
5268
|
hasInputSection && hasOutputSection ? /* @__PURE__ */ jsx("div", { className: "h-2" }) : null,
|
|
4939
|
-
hasOutputSection ? /* @__PURE__ */ jsx(
|
|
5269
|
+
hasOutputSection ? /* @__PURE__ */ jsx(ToolCardDetailSection, {
|
|
4940
5270
|
label: card.outputLabel?.trim() || "Output",
|
|
4941
5271
|
tone: card.statusTone === "error" ? "error" : "output",
|
|
4942
5272
|
children: output
|
|
@@ -5085,8 +5415,7 @@ function ChatToolCard({ card, toolStatusLabels, onToolAction, onFileOpen, render
|
|
|
5085
5415
|
}
|
|
5086
5416
|
//#endregion
|
|
5087
5417
|
//#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);
|
|
5418
|
+
function ChatToolActivityGroup({ group, open, isUser, reasoningCharacterCountTemplates, toolStatusLabels, onToolAction, onFileOpen, renderToolAgent, renderPanelAppCard, onOpenChange }) {
|
|
5090
5419
|
const toolCount = group.parts.filter((part) => part.type === "tool-card").length;
|
|
5091
5420
|
const showWorkflowRail = open && toolCount > 1;
|
|
5092
5421
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -5094,34 +5423,29 @@ function ChatToolActivityGroup({ group, isUser, reasoningCharacterCountTemplates
|
|
|
5094
5423
|
children: [/* @__PURE__ */ jsx(ChatCollapsibleMetaSummary, {
|
|
5095
5424
|
openGroup: "tool-activity",
|
|
5096
5425
|
open,
|
|
5426
|
+
icon: Workflow,
|
|
5427
|
+
leadingIconClassName: "bg-card",
|
|
5097
5428
|
label: group.label,
|
|
5098
|
-
onClick: () =>
|
|
5429
|
+
onClick: () => onOpenChange(!open)
|
|
5099
5430
|
}), open ? /* @__PURE__ */ jsx("div", {
|
|
5100
5431
|
className: "text-[0.925rem] leading-[1.72]",
|
|
5101
5432
|
children: group.parts.map((part, index) => {
|
|
5102
5433
|
const isLast = index === group.parts.length - 1;
|
|
5103
5434
|
return /* @__PURE__ */ jsxs("div", {
|
|
5104
5435
|
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, {
|
|
5436
|
+
children: [showWorkflowRail ? /* @__PURE__ */ jsx(ChatProcessWorkflowRail, { position: index === 0 ? "first" : isLast ? "last" : "middle" }) : null, part.type === "tool-card" ? /* @__PURE__ */ jsx(ChatToolCard, {
|
|
5110
5437
|
card: part.card,
|
|
5111
5438
|
toolStatusLabels,
|
|
5112
5439
|
onToolAction,
|
|
5113
5440
|
onFileOpen,
|
|
5114
5441
|
renderToolAgent,
|
|
5115
5442
|
renderPanelAppCard
|
|
5116
|
-
}) : /* @__PURE__ */ jsx(
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
isUser,
|
|
5123
|
-
isInProgress: false
|
|
5124
|
-
})
|
|
5443
|
+
}) : /* @__PURE__ */ jsx(ChatReasoningBlock, {
|
|
5444
|
+
label: part.label,
|
|
5445
|
+
text: part.text,
|
|
5446
|
+
characterCountTemplates: reasoningCharacterCountTemplates,
|
|
5447
|
+
isUser,
|
|
5448
|
+
isInProgress: false
|
|
5125
5449
|
})]
|
|
5126
5450
|
}, `tool-group-item-${group.startIndex + index}`);
|
|
5127
5451
|
})
|
|
@@ -5134,6 +5458,9 @@ const MAX_SUMMARY_SEGMENTS = 3;
|
|
|
5134
5458
|
function isToolCardPart(part) {
|
|
5135
5459
|
return part.type === "tool-card";
|
|
5136
5460
|
}
|
|
5461
|
+
function isGroupableToolPart(part) {
|
|
5462
|
+
return part?.type === "reasoning" || part?.type === "tool-card" && !part.card.panelApp;
|
|
5463
|
+
}
|
|
5137
5464
|
function resolveToolActivityFamily(toolName) {
|
|
5138
5465
|
const lowered = toolName.toLowerCase();
|
|
5139
5466
|
if (lowered === "list_dir") return "directory";
|
|
@@ -5212,17 +5539,29 @@ function formatToolActivityGroupLabel(params) {
|
|
|
5212
5539
|
if (hiddenCount > 0) parts.push(`+${hiddenCount}`);
|
|
5213
5540
|
return parts.join(" · ");
|
|
5214
5541
|
}
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5542
|
+
function collectToolGroupParts(parts, startIndex) {
|
|
5543
|
+
const candidateParts = [];
|
|
5544
|
+
let index = startIndex;
|
|
5545
|
+
let lastToolOffset = -1;
|
|
5546
|
+
while (true) {
|
|
5547
|
+
const candidate = parts[index];
|
|
5548
|
+
if (!isGroupableToolPart(candidate)) break;
|
|
5549
|
+
candidateParts.push(candidate);
|
|
5550
|
+
if (candidate.type === "tool-card") lastToolOffset = candidateParts.length - 1;
|
|
5551
|
+
index += 1;
|
|
5552
|
+
}
|
|
5553
|
+
return {
|
|
5554
|
+
candidateParts,
|
|
5555
|
+
lastToolOffset
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
/** Groups tool cards across reasoning; content and interactive panel apps remain stable boundaries. */
|
|
5220
5559
|
function groupConsecutiveToolParts(parts, labels) {
|
|
5221
5560
|
const blocks = [];
|
|
5222
5561
|
let index = 0;
|
|
5223
5562
|
while (index < parts.length) {
|
|
5224
5563
|
const part = parts[index];
|
|
5225
|
-
if (part.type !== "tool-card") {
|
|
5564
|
+
if (part.type !== "tool-card" || part.card.panelApp) {
|
|
5226
5565
|
blocks.push({
|
|
5227
5566
|
kind: "part",
|
|
5228
5567
|
key: `part-${index}`,
|
|
@@ -5233,14 +5572,7 @@ function groupConsecutiveToolParts(parts, labels) {
|
|
|
5233
5572
|
continue;
|
|
5234
5573
|
}
|
|
5235
5574
|
const startIndex = index;
|
|
5236
|
-
const candidateParts =
|
|
5237
|
-
let lastToolOffset = -1;
|
|
5238
|
-
while (index < parts.length && (parts[index]?.type === "tool-card" || parts[index]?.type === "reasoning")) {
|
|
5239
|
-
const candidate = parts[index];
|
|
5240
|
-
candidateParts.push(candidate);
|
|
5241
|
-
if (candidate.type === "tool-card") lastToolOffset = candidateParts.length - 1;
|
|
5242
|
-
index += 1;
|
|
5243
|
-
}
|
|
5575
|
+
const { candidateParts, lastToolOffset } = collectToolGroupParts(parts, startIndex);
|
|
5244
5576
|
const toolParts = candidateParts.filter(isToolCardPart);
|
|
5245
5577
|
if (toolParts.length === 1) {
|
|
5246
5578
|
blocks.push({
|
|
@@ -5261,9 +5593,9 @@ function groupConsecutiveToolParts(parts, labels) {
|
|
|
5261
5593
|
});
|
|
5262
5594
|
blocks.push({
|
|
5263
5595
|
kind: "tool-group",
|
|
5264
|
-
key: `tool-group-${startIndex}
|
|
5596
|
+
key: `tool-group-${startIndex}`,
|
|
5265
5597
|
group: {
|
|
5266
|
-
key: `tool-group-${startIndex}
|
|
5598
|
+
key: `tool-group-${startIndex}`,
|
|
5267
5599
|
startIndex,
|
|
5268
5600
|
endIndex: index - 1,
|
|
5269
5601
|
parts: groupedParts,
|
|
@@ -5347,7 +5679,7 @@ function splitAssistantProcess(message) {
|
|
|
5347
5679
|
finalParts: message.parts.slice(lastProcessPartIndex + 1)
|
|
5348
5680
|
};
|
|
5349
5681
|
}
|
|
5350
|
-
function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, part, renderInlineDisplay, renderPanelAppCard, renderToolAgent, role, texts }) {
|
|
5682
|
+
function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, part, renderInlineDisplay, renderPanelAppCard, renderToolAgent, resolveFileContentUrl, role, texts }) {
|
|
5351
5683
|
const { type } = part;
|
|
5352
5684
|
if (type === "markdown") {
|
|
5353
5685
|
const { inlineTokens, text } = part;
|
|
@@ -5355,9 +5687,11 @@ function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAtta
|
|
|
5355
5687
|
text,
|
|
5356
5688
|
role,
|
|
5357
5689
|
texts,
|
|
5690
|
+
isStreaming: isInProgress && isLastPart,
|
|
5358
5691
|
inlineTokens,
|
|
5359
5692
|
onFileOpen,
|
|
5360
5693
|
onInlineTokenClick,
|
|
5694
|
+
resolveFileContentUrl,
|
|
5361
5695
|
renderInlineDisplay
|
|
5362
5696
|
}, `markdown-${index}`);
|
|
5363
5697
|
}
|
|
@@ -5373,14 +5707,17 @@ function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAtta
|
|
|
5373
5707
|
}
|
|
5374
5708
|
if (type === "tool-card") {
|
|
5375
5709
|
const { card } = part;
|
|
5376
|
-
return /* @__PURE__ */
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5710
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5711
|
+
className: "relative min-w-0",
|
|
5712
|
+
children: [/* @__PURE__ */ jsx(ChatProcessWorkflowRail, { position: "single" }), /* @__PURE__ */ jsx(ChatToolCard, {
|
|
5713
|
+
card,
|
|
5714
|
+
toolStatusLabels: texts.toolStatusLabels,
|
|
5715
|
+
onToolAction,
|
|
5716
|
+
onFileOpen,
|
|
5717
|
+
renderToolAgent,
|
|
5718
|
+
renderPanelAppCard
|
|
5719
|
+
})]
|
|
5720
|
+
}, `tool-${index}`);
|
|
5384
5721
|
}
|
|
5385
5722
|
if (type === "file") {
|
|
5386
5723
|
const { file } = part;
|
|
@@ -5411,17 +5748,26 @@ function renderChatMessagePart({ index, isInProgress, isLastPart, isUser, onAtta
|
|
|
5411
5748
|
return null;
|
|
5412
5749
|
}
|
|
5413
5750
|
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) => {
|
|
5751
|
+
const { isInProgress, isUser, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolActivityOpenChange, onToolAction, openToolGroupKeys, parts, renderInlineDisplay, renderPanelAppCard, renderToolAgent, resolveFileContentUrl, role, texts, indexOffset = 0 } = params;
|
|
5752
|
+
return groupConsecutiveImageFileBlocks(groupConsecutiveToolParts(parts, resolveToolActivityLabels(texts))).map((block) => {
|
|
5753
|
+
if (block.kind === "image-group") return /* @__PURE__ */ jsx(ChatMessageImageRow, {
|
|
5754
|
+
group: block,
|
|
5755
|
+
indexOffset,
|
|
5756
|
+
isUser,
|
|
5757
|
+
texts,
|
|
5758
|
+
onOpen: onAttachmentOpen
|
|
5759
|
+
}, `${block.key}-${indexOffset}`);
|
|
5416
5760
|
if (block.kind === "tool-group") return /* @__PURE__ */ jsx(ChatToolActivityGroup, {
|
|
5417
5761
|
group: block.group,
|
|
5762
|
+
open: openToolGroupKeys.has(block.group.key),
|
|
5418
5763
|
isUser,
|
|
5419
5764
|
reasoningCharacterCountTemplates: texts.reasoningCharacterCountTemplates,
|
|
5420
5765
|
toolStatusLabels: texts.toolStatusLabels,
|
|
5421
5766
|
onToolAction,
|
|
5422
5767
|
onFileOpen,
|
|
5423
5768
|
renderToolAgent,
|
|
5424
|
-
renderPanelAppCard
|
|
5769
|
+
renderPanelAppCard,
|
|
5770
|
+
onOpenChange: (open) => onToolActivityOpenChange(block.group.key, open)
|
|
5425
5771
|
}, block.key);
|
|
5426
5772
|
return renderChatMessagePart({
|
|
5427
5773
|
part: block.part,
|
|
@@ -5435,20 +5781,33 @@ function renderMessageParts(params) {
|
|
|
5435
5781
|
onFileOpen,
|
|
5436
5782
|
onAttachmentOpen,
|
|
5437
5783
|
onInlineTokenClick,
|
|
5784
|
+
resolveFileContentUrl,
|
|
5438
5785
|
renderInlineDisplay,
|
|
5439
5786
|
renderToolAgent,
|
|
5440
5787
|
renderPanelAppCard
|
|
5441
5788
|
});
|
|
5442
5789
|
});
|
|
5443
5790
|
}
|
|
5444
|
-
const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, onFileOpen, onAttachmentOpen, onInlineTokenClick, renderInlineDisplay, renderToolAgent, renderPanelAppCard }) {
|
|
5791
|
+
const ChatMessage = memo(function ChatMessage({ layout, message, texts, onToolAction, onFileOpen, onAttachmentOpen, onInlineTokenClick, renderInlineDisplay, renderToolAgent, renderPanelAppCard, resolveFileContentUrl }) {
|
|
5445
5792
|
const { role } = message;
|
|
5446
5793
|
const isUser = role === "user";
|
|
5794
|
+
const isFlat = layout === "flat" && !isUser;
|
|
5447
5795
|
const isInProgress = isMessageInProgress(message.status);
|
|
5448
5796
|
const processSplit = splitAssistantProcess(message);
|
|
5449
5797
|
const [processOpen, setProcessOpen] = useState(false);
|
|
5798
|
+
const [openToolGroupKeys, setOpenToolGroupKeys] = useState(() => /* @__PURE__ */ new Set());
|
|
5799
|
+
const handleToolActivityOpenChange = (groupKey, open) => {
|
|
5800
|
+
setOpenToolGroupKeys((current) => {
|
|
5801
|
+
const next = new Set(current);
|
|
5802
|
+
if (open) next.add(groupKey);
|
|
5803
|
+
else next.delete(groupKey);
|
|
5804
|
+
return next;
|
|
5805
|
+
});
|
|
5806
|
+
if (open) setProcessOpen(true);
|
|
5807
|
+
};
|
|
5450
5808
|
return /* @__PURE__ */ jsx("div", {
|
|
5451
|
-
|
|
5809
|
+
"data-chat-message-surface": isFlat ? "flat" : "card",
|
|
5810
|
+
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
5811
|
children: /* @__PURE__ */ jsx("div", {
|
|
5453
5812
|
className: "space-y-0",
|
|
5454
5813
|
children: processSplit ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
@@ -5458,6 +5817,8 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5458
5817
|
children: /* @__PURE__ */ jsx(ChatCollapsibleMetaSummary, {
|
|
5459
5818
|
openGroup: "process",
|
|
5460
5819
|
open: processOpen,
|
|
5820
|
+
icon: ListChecks,
|
|
5821
|
+
leadingIconClassName: "bg-card",
|
|
5461
5822
|
label: message.processSummary?.label,
|
|
5462
5823
|
onClick: () => setProcessOpen((current) => !current)
|
|
5463
5824
|
})
|
|
@@ -5473,6 +5834,9 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5473
5834
|
onFileOpen,
|
|
5474
5835
|
onAttachmentOpen,
|
|
5475
5836
|
onInlineTokenClick,
|
|
5837
|
+
resolveFileContentUrl,
|
|
5838
|
+
openToolGroupKeys,
|
|
5839
|
+
onToolActivityOpenChange: handleToolActivityOpenChange,
|
|
5476
5840
|
renderInlineDisplay,
|
|
5477
5841
|
renderToolAgent,
|
|
5478
5842
|
renderPanelAppCard
|
|
@@ -5488,6 +5852,9 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5488
5852
|
onFileOpen,
|
|
5489
5853
|
onAttachmentOpen,
|
|
5490
5854
|
onInlineTokenClick,
|
|
5855
|
+
resolveFileContentUrl,
|
|
5856
|
+
openToolGroupKeys,
|
|
5857
|
+
onToolActivityOpenChange: handleToolActivityOpenChange,
|
|
5491
5858
|
renderInlineDisplay,
|
|
5492
5859
|
renderToolAgent,
|
|
5493
5860
|
renderPanelAppCard,
|
|
@@ -5502,6 +5869,9 @@ const ChatMessage = memo(function ChatMessage({ message, texts, onToolAction, on
|
|
|
5502
5869
|
onFileOpen,
|
|
5503
5870
|
onAttachmentOpen,
|
|
5504
5871
|
onInlineTokenClick,
|
|
5872
|
+
resolveFileContentUrl,
|
|
5873
|
+
openToolGroupKeys,
|
|
5874
|
+
onToolActivityOpenChange: handleToolActivityOpenChange,
|
|
5505
5875
|
renderInlineDisplay,
|
|
5506
5876
|
renderToolAgent,
|
|
5507
5877
|
renderPanelAppCard
|
|
@@ -5520,14 +5890,25 @@ function ChatMessageActionCopy({ message, texts }) {
|
|
|
5520
5890
|
}).filter((text) => !!text && text.trim().length > 0).join("\n\n");
|
|
5521
5891
|
}, [message.parts]);
|
|
5522
5892
|
const { copied, copy } = useCopyFeedback({ text: messageText });
|
|
5893
|
+
const label = copied ? texts.copiedMessageLabel : texts.copyMessageLabel;
|
|
5894
|
+
const { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } = ChatUiPrimitives;
|
|
5523
5895
|
if (!messageText) return null;
|
|
5524
|
-
return /* @__PURE__ */ jsx(
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5896
|
+
return /* @__PURE__ */ jsx(TooltipProvider, {
|
|
5897
|
+
delayDuration: 250,
|
|
5898
|
+
children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
5899
|
+
asChild: true,
|
|
5900
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
5901
|
+
type: "button",
|
|
5902
|
+
onClick: () => void copy(),
|
|
5903
|
+
className: "flex items-center justify-center rounded-md p-1 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35",
|
|
5904
|
+
"aria-label": label,
|
|
5905
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
|
|
5906
|
+
})
|
|
5907
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
5908
|
+
side: "top",
|
|
5909
|
+
className: "text-xs",
|
|
5910
|
+
children: label
|
|
5911
|
+
})] })
|
|
5531
5912
|
});
|
|
5532
5913
|
}
|
|
5533
5914
|
//#endregion
|
|
@@ -5542,7 +5923,6 @@ const TYPING_TEXT_SHEEN_CSS = `
|
|
|
5542
5923
|
background-position: -60% 0;
|
|
5543
5924
|
}
|
|
5544
5925
|
}
|
|
5545
|
-
|
|
5546
5926
|
.nextclaw-chat-typing-indicator__text {
|
|
5547
5927
|
background-image: linear-gradient(
|
|
5548
5928
|
100deg,
|
|
@@ -5596,16 +5976,17 @@ function ChatMessageTypingFooter() {
|
|
|
5596
5976
|
})
|
|
5597
5977
|
});
|
|
5598
5978
|
}
|
|
5599
|
-
function ChatTypingIndicator({ label }) {
|
|
5979
|
+
function ChatTypingIndicator({ label, layout }) {
|
|
5600
5980
|
return /* @__PURE__ */ jsxs("div", {
|
|
5601
|
-
|
|
5981
|
+
"data-chat-message-surface": layout === "flat" ? "flat" : "card",
|
|
5982
|
+
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
5983
|
children: [/* @__PURE__ */ jsx("style", { children: TYPING_TEXT_SHEEN_CSS }), /* @__PURE__ */ jsx("span", {
|
|
5603
5984
|
className: "nextclaw-chat-typing-indicator__text",
|
|
5604
5985
|
children: label
|
|
5605
5986
|
})]
|
|
5606
5987
|
});
|
|
5607
5988
|
}
|
|
5608
|
-
function ChatMessageList({ className, isSending, messages, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, renderInlineDisplay, renderPanelAppCard, renderToolAgent, texts }) {
|
|
5989
|
+
function ChatMessageList({ className, isSending, layout = "card", messages, onAttachmentOpen, onFileOpen, onInlineTokenClick, onToolAction, renderInlineDisplay, renderPanelAppCard, renderToolAgent, resolveFileContentUrl, texts }) {
|
|
5609
5990
|
const visibleMessages = messages.filter(hasRenderableMessageContent);
|
|
5610
5991
|
const hasRenderableAssistantDraft = visibleMessages.some((message) => message.role === "assistant" && (message.status === "streaming" || message.status === "pending"));
|
|
5611
5992
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -5613,23 +5994,63 @@ function ChatMessageList({ className, isSending, messages, onAttachmentOpen, onF
|
|
|
5613
5994
|
children: [visibleMessages.map((message) => {
|
|
5614
5995
|
const isUser = message.role === "user";
|
|
5615
5996
|
const isGenerating = !isUser && (message.status === "streaming" || message.status === "pending");
|
|
5997
|
+
const content = /* @__PURE__ */ jsx(ChatMessage, {
|
|
5998
|
+
layout,
|
|
5999
|
+
message,
|
|
6000
|
+
texts,
|
|
6001
|
+
onToolAction,
|
|
6002
|
+
onFileOpen,
|
|
6003
|
+
onAttachmentOpen,
|
|
6004
|
+
onInlineTokenClick,
|
|
6005
|
+
resolveFileContentUrl,
|
|
6006
|
+
renderInlineDisplay,
|
|
6007
|
+
renderToolAgent,
|
|
6008
|
+
renderPanelAppCard
|
|
6009
|
+
});
|
|
6010
|
+
if (layout === "flat" && !isUser) return /* @__PURE__ */ jsxs("article", {
|
|
6011
|
+
"data-chat-message-layout": "flat",
|
|
6012
|
+
className: "w-full min-w-0 space-y-2",
|
|
6013
|
+
children: [
|
|
6014
|
+
/* @__PURE__ */ jsxs("div", {
|
|
6015
|
+
"data-chat-message-header": "flat",
|
|
6016
|
+
className: "flex min-w-0 items-center gap-2.5",
|
|
6017
|
+
children: [/* @__PURE__ */ jsx(ChatMessageAvatar, {
|
|
6018
|
+
role: message.role,
|
|
6019
|
+
size: "compact"
|
|
6020
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
6021
|
+
className: "truncate text-sm font-semibold text-foreground",
|
|
6022
|
+
children: message.roleLabel
|
|
6023
|
+
})]
|
|
6024
|
+
}),
|
|
6025
|
+
/* @__PURE__ */ jsx("div", {
|
|
6026
|
+
"data-chat-message-body": "flat",
|
|
6027
|
+
className: "min-w-0 w-full",
|
|
6028
|
+
children: content
|
|
6029
|
+
}),
|
|
6030
|
+
isGenerating ? /* @__PURE__ */ jsx("div", {
|
|
6031
|
+
className: "w-full",
|
|
6032
|
+
children: /* @__PURE__ */ jsx(ChatMessageTypingFooter, {})
|
|
6033
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
6034
|
+
"data-chat-message-footer": "flat",
|
|
6035
|
+
className: "flex items-center gap-2",
|
|
6036
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
6037
|
+
className: "px-1 text-[11px] leading-4 text-muted-foreground",
|
|
6038
|
+
children: message.timestampLabel
|
|
6039
|
+
}), /* @__PURE__ */ jsx(ChatMessageActionCopy, {
|
|
6040
|
+
message,
|
|
6041
|
+
texts
|
|
6042
|
+
})]
|
|
6043
|
+
})
|
|
6044
|
+
]
|
|
6045
|
+
}, message.id);
|
|
5616
6046
|
return /* @__PURE__ */ jsxs("div", {
|
|
6047
|
+
"data-chat-message-layout": "card",
|
|
5617
6048
|
className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"),
|
|
5618
6049
|
children: [
|
|
5619
6050
|
!isUser ? /* @__PURE__ */ jsx(ChatMessageAvatar, { role: message.role }) : null,
|
|
5620
6051
|
/* @__PURE__ */ jsxs("div", {
|
|
5621
6052
|
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", {
|
|
6053
|
+
children: [content, /* @__PURE__ */ jsx("div", {
|
|
5633
6054
|
className: cn("flex items-center gap-2", isUser && "justify-end"),
|
|
5634
6055
|
children: isGenerating ? /* @__PURE__ */ jsx(ChatMessageTypingFooter, {}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
5635
6056
|
className: cn("px-1 text-[11px] leading-4 text-muted-foreground", isUser ? "text-right" : "text-left"),
|
|
@@ -5638,18 +6059,25 @@ function ChatMessageList({ className, isSending, messages, onAttachmentOpen, onF
|
|
|
5638
6059
|
" · ",
|
|
5639
6060
|
message.timestampLabel
|
|
5640
6061
|
]
|
|
5641
|
-
}),
|
|
6062
|
+
}), /* @__PURE__ */ jsx(ChatMessageActionCopy, {
|
|
5642
6063
|
message,
|
|
5643
6064
|
texts
|
|
5644
|
-
})
|
|
6065
|
+
})] })
|
|
5645
6066
|
})]
|
|
5646
6067
|
}),
|
|
5647
6068
|
isUser ? /* @__PURE__ */ jsx(ChatMessageAvatar, { role: message.role }) : null
|
|
5648
6069
|
]
|
|
5649
6070
|
}, message.id);
|
|
5650
6071
|
}), isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs("div", {
|
|
6072
|
+
"data-chat-message-layout": layout,
|
|
5651
6073
|
className: "flex justify-start gap-3",
|
|
5652
|
-
children: [/* @__PURE__ */ jsx(ChatMessageAvatar, {
|
|
6074
|
+
children: [/* @__PURE__ */ jsx(ChatMessageAvatar, {
|
|
6075
|
+
role: "assistant",
|
|
6076
|
+
size: layout === "flat" ? "compact" : "default"
|
|
6077
|
+
}), /* @__PURE__ */ jsx(ChatTypingIndicator, {
|
|
6078
|
+
label: texts.typingLabel,
|
|
6079
|
+
layout
|
|
6080
|
+
})]
|
|
5653
6081
|
}) : null]
|
|
5654
6082
|
});
|
|
5655
6083
|
}
|