@nextclaw/agent-chat-ui 0.3.5 → 0.3.7
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 +53 -1
- package/dist/index.js +196 -49
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -102,6 +102,7 @@ type ChatSkillPickerProps = {
|
|
|
102
102
|
};
|
|
103
103
|
type ChatInputBarActionsProps = {
|
|
104
104
|
sendError?: string | null;
|
|
105
|
+
sendErrorDetailsLabel?: string;
|
|
105
106
|
isSending: boolean;
|
|
106
107
|
canStopGeneration: boolean;
|
|
107
108
|
sendDisabled: boolean;
|
|
@@ -164,7 +165,13 @@ type ChatFileOperationBlockViewModel = {
|
|
|
164
165
|
display?: "preview" | "diff";
|
|
165
166
|
caption?: string;
|
|
166
167
|
lines: ChatFileOperationLineViewModel[];
|
|
168
|
+
fullLines?: ChatFileOperationLineViewModel[];
|
|
167
169
|
rawText?: string;
|
|
170
|
+
beforeText?: string;
|
|
171
|
+
afterText?: string;
|
|
172
|
+
patchText?: string;
|
|
173
|
+
oldStartLine?: number;
|
|
174
|
+
newStartLine?: number;
|
|
168
175
|
truncated?: boolean;
|
|
169
176
|
};
|
|
170
177
|
type ChatToolActionViewModel = {
|
|
@@ -175,6 +182,20 @@ type ChatToolActionViewModel = {
|
|
|
175
182
|
label?: string;
|
|
176
183
|
parentSessionId?: string;
|
|
177
184
|
};
|
|
185
|
+
type ChatFileOpenActionViewModel = {
|
|
186
|
+
path: string;
|
|
187
|
+
label?: string;
|
|
188
|
+
viewMode: "preview" | "diff";
|
|
189
|
+
line?: number;
|
|
190
|
+
column?: number;
|
|
191
|
+
rawText?: string;
|
|
192
|
+
beforeText?: string;
|
|
193
|
+
afterText?: string;
|
|
194
|
+
patchText?: string;
|
|
195
|
+
oldStartLine?: number;
|
|
196
|
+
newStartLine?: number;
|
|
197
|
+
fullLines?: ChatFileOperationLineViewModel[];
|
|
198
|
+
};
|
|
178
199
|
type ChatToolPartViewModel = {
|
|
179
200
|
kind: "call" | "result";
|
|
180
201
|
toolName: string;
|
|
@@ -262,6 +283,7 @@ type ChatMessageListProps = {
|
|
|
262
283
|
texts: ChatMessageTexts;
|
|
263
284
|
className?: string;
|
|
264
285
|
onToolAction?: (action: ChatToolActionViewModel) => void;
|
|
286
|
+
onFileOpen?: (action: ChatFileOpenActionViewModel) => void;
|
|
265
287
|
renderToolAgent?: (agentId: string) => ReactNode;
|
|
266
288
|
};
|
|
267
289
|
//#endregion
|
|
@@ -279,6 +301,36 @@ declare const ChatInputBar: _$react.ForwardRefExoticComponent<ChatInputBarProps
|
|
|
279
301
|
//#region src/components/chat/ui/chat-message-list/chat-message-list.d.ts
|
|
280
302
|
declare function ChatMessageList(props: ChatMessageListProps): _$react_jsx_runtime0.JSX.Element;
|
|
281
303
|
//#endregion
|
|
304
|
+
//#region src/components/chat/ui/chat-message-list/chat-message-markdown.d.ts
|
|
305
|
+
type ChatMessageMarkdownProps = {
|
|
306
|
+
text: string;
|
|
307
|
+
role: ChatMessageRole;
|
|
308
|
+
texts: Pick<ChatMessageTexts, 'copyCodeLabel' | 'copiedCodeLabel'>;
|
|
309
|
+
inline?: boolean;
|
|
310
|
+
onFileOpen?: (action: ChatFileOpenActionViewModel) => void;
|
|
311
|
+
};
|
|
312
|
+
declare function ChatMessageMarkdown({
|
|
313
|
+
text,
|
|
314
|
+
role,
|
|
315
|
+
texts,
|
|
316
|
+
inline,
|
|
317
|
+
onFileOpen
|
|
318
|
+
}: ChatMessageMarkdownProps): _$react_jsx_runtime0.JSX.Element;
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-file-operation-lines.d.ts
|
|
321
|
+
declare function FileOperationCodeSurface({
|
|
322
|
+
block,
|
|
323
|
+
layout
|
|
324
|
+
}: {
|
|
325
|
+
block: ChatFileOperationBlockViewModel;
|
|
326
|
+
layout?: "compact" | "workspace";
|
|
327
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
328
|
+
declare function FileOperationLinesGrid({
|
|
329
|
+
block
|
|
330
|
+
}: {
|
|
331
|
+
block: ChatFileOperationBlockViewModel;
|
|
332
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
333
|
+
//#endregion
|
|
282
334
|
//#region src/components/chat/hooks/use-active-item-scroll.d.ts
|
|
283
335
|
type UseActiveItemScrollParams = {
|
|
284
336
|
containerRef: RefObject<HTMLElement>;
|
|
@@ -345,4 +397,4 @@ declare function resolveChatComposerSlashTrigger(nodes: ChatComposerNode[], sele
|
|
|
345
397
|
end: number;
|
|
346
398
|
} | null;
|
|
347
399
|
//#endregion
|
|
348
|
-
export { type ChatComposerNode, type ChatComposerSelection, type ChatComposerTextNode, type ChatComposerTokenKind, type ChatComposerTokenNode, type ChatFileOperationBlockViewModel, type ChatFileOperationLineViewModel, type ChatInlineContentSegmentViewModel, type ChatInlineHint, type ChatInlineTokenViewModel, ChatInputBar, type ChatInputBarActionsProps, type ChatInputBarHandle, type ChatInputBarProps, type ChatInputBarToolbarProps, ChatMessageList, type ChatMessageListProps, type ChatMessagePartViewModel, type ChatMessageRole, type ChatMessageTexts, type ChatMessageViewModel, type ChatSelectedItem, type ChatSkillPickerOption, type ChatSkillPickerProps, type ChatSlashItem, type ChatSlashMenuProps, type ChatTexts, type ChatToolActionViewModel, type ChatToolPartViewModel, type ChatToolbarAccessory, type ChatToolbarAccessoryIcon, type ChatToolbarIcon, type ChatToolbarSelect, type ChatToolbarSelectGroup, type ChatToolbarSelectOption, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
|
|
400
|
+
export { type ChatComposerNode, type ChatComposerSelection, type ChatComposerTextNode, type ChatComposerTokenKind, type ChatComposerTokenNode, type ChatFileOpenActionViewModel, type ChatFileOperationBlockViewModel, type ChatFileOperationLineViewModel, type ChatInlineContentSegmentViewModel, type ChatInlineHint, type ChatInlineTokenViewModel, ChatInputBar, type ChatInputBarActionsProps, type ChatInputBarHandle, type ChatInputBarProps, type ChatInputBarToolbarProps, ChatMessageList, type ChatMessageListProps, ChatMessageMarkdown, type ChatMessagePartViewModel, type ChatMessageRole, type ChatMessageTexts, type ChatMessageViewModel, type ChatSelectedItem, type ChatSkillPickerOption, type ChatSkillPickerProps, type ChatSlashItem, type ChatSlashMenuProps, type ChatTexts, type ChatToolActionViewModel, type ChatToolPartViewModel, type ChatToolbarAccessory, type ChatToolbarAccessoryIcon, type ChatToolbarIcon, type ChatToolbarSelect, type ChatToolbarSelectGroup, type ChatToolbarSelectOption, FileOperationCodeSurface, FileOperationLinesGrid, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
|
package/dist/index.js
CHANGED
|
@@ -341,6 +341,12 @@ const ChatButton = React.forwardRef(({ className, variant, size, ...props }, ref
|
|
|
341
341
|
ChatButton.displayName = "ChatButton";
|
|
342
342
|
//#endregion
|
|
343
343
|
//#region src/components/chat/ui/chat-input-bar/chat-input-bar-actions.tsx
|
|
344
|
+
const SEND_ERROR_PREVIEW_MAX_CHARS = 120;
|
|
345
|
+
function buildSendErrorPreview(value) {
|
|
346
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
347
|
+
if (compact.length <= SEND_ERROR_PREVIEW_MAX_CHARS) return compact;
|
|
348
|
+
return `${compact.slice(0, SEND_ERROR_PREVIEW_MAX_CHARS - 1)}…`;
|
|
349
|
+
}
|
|
344
350
|
function StopIcon() {
|
|
345
351
|
return /* @__PURE__ */ jsx("span", {
|
|
346
352
|
"aria-hidden": "true",
|
|
@@ -348,22 +354,49 @@ function StopIcon() {
|
|
|
348
354
|
className: "block h-3 w-3 rounded-[2px] bg-gray-700 shadow-[inset_0_0_0_1px_rgba(17,24,39,0.06)]"
|
|
349
355
|
});
|
|
350
356
|
}
|
|
351
|
-
function ChatInputBarActions(
|
|
352
|
-
const { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } = ChatUiPrimitives;
|
|
357
|
+
function ChatInputBarActions({ sendError, sendErrorDetailsLabel, isSending, canStopGeneration, sendDisabled, stopDisabled, stopHint, sendButtonLabel, stopButtonLabel, onSend, onStop }) {
|
|
358
|
+
const { Popover, PopoverContent, PopoverTrigger, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } = ChatUiPrimitives;
|
|
359
|
+
const normalizedSendError = sendError?.trim() ?? "";
|
|
360
|
+
const sendErrorPreview = normalizedSendError ? buildSendErrorPreview(normalizedSendError) : "";
|
|
361
|
+
const resolvedSendErrorDetailsLabel = sendErrorDetailsLabel?.trim() || "Details";
|
|
353
362
|
return /* @__PURE__ */ jsxs("div", {
|
|
354
363
|
className: "flex flex-col items-end gap-1",
|
|
355
|
-
children: [
|
|
356
|
-
className: "max-w-[420px]
|
|
357
|
-
children:
|
|
364
|
+
children: [normalizedSendError ? /* @__PURE__ */ jsxs("div", {
|
|
365
|
+
className: "flex max-w-[420px] items-start justify-end gap-2 text-right",
|
|
366
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
367
|
+
className: "min-w-0 flex-1 text-[11px] text-red-600",
|
|
368
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
369
|
+
className: "block truncate",
|
|
370
|
+
title: normalizedSendError,
|
|
371
|
+
children: sendErrorPreview
|
|
372
|
+
})
|
|
373
|
+
}), /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
374
|
+
asChild: true,
|
|
375
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
376
|
+
type: "button",
|
|
377
|
+
className: "shrink-0 rounded-full border border-red-200/80 bg-red-50 px-2 py-0.5 text-[10px] font-semibold text-red-700 transition-colors hover:bg-red-100",
|
|
378
|
+
children: resolvedSendErrorDetailsLabel
|
|
379
|
+
})
|
|
380
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
381
|
+
align: "end",
|
|
382
|
+
className: "w-[min(32rem,calc(100vw-1.5rem))] border-red-100/80 p-0",
|
|
383
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
384
|
+
className: "border-b border-red-100 bg-red-50/80 px-4 py-2 text-xs font-semibold text-red-700",
|
|
385
|
+
children: resolvedSendErrorDetailsLabel
|
|
386
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
387
|
+
className: "max-h-80 overflow-auto whitespace-pre-wrap break-words px-4 py-3 text-xs leading-relaxed text-red-700",
|
|
388
|
+
children: normalizedSendError
|
|
389
|
+
})]
|
|
390
|
+
})] })]
|
|
358
391
|
}) : null, /* @__PURE__ */ jsx("div", {
|
|
359
392
|
className: "flex items-center gap-2",
|
|
360
|
-
children:
|
|
393
|
+
children: isSending ? canStopGeneration ? /* @__PURE__ */ jsx(ChatButton, {
|
|
361
394
|
size: "icon",
|
|
362
395
|
variant: "outline",
|
|
363
396
|
className: "h-8 w-8 rounded-full",
|
|
364
|
-
"aria-label":
|
|
365
|
-
onClick: () => void
|
|
366
|
-
disabled:
|
|
397
|
+
"aria-label": stopButtonLabel,
|
|
398
|
+
onClick: () => void onStop(),
|
|
399
|
+
disabled: stopDisabled,
|
|
367
400
|
children: /* @__PURE__ */ jsx(StopIcon, {})
|
|
368
401
|
}) : /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
369
402
|
asChild: true,
|
|
@@ -371,7 +404,7 @@ function ChatInputBarActions(props) {
|
|
|
371
404
|
size: "icon",
|
|
372
405
|
variant: "outline",
|
|
373
406
|
className: "h-8 w-8 rounded-full",
|
|
374
|
-
"aria-label":
|
|
407
|
+
"aria-label": stopButtonLabel,
|
|
375
408
|
disabled: true,
|
|
376
409
|
children: /* @__PURE__ */ jsx(StopIcon, {})
|
|
377
410
|
}) })
|
|
@@ -379,14 +412,14 @@ function ChatInputBarActions(props) {
|
|
|
379
412
|
side: "top",
|
|
380
413
|
children: /* @__PURE__ */ jsx("p", {
|
|
381
414
|
className: "text-xs",
|
|
382
|
-
children:
|
|
415
|
+
children: stopHint
|
|
383
416
|
})
|
|
384
417
|
})] }) }) : /* @__PURE__ */ jsx(ChatButton, {
|
|
385
418
|
size: "icon",
|
|
386
419
|
className: "h-8 w-8 rounded-full",
|
|
387
|
-
"aria-label":
|
|
388
|
-
onClick: () => void
|
|
389
|
-
disabled:
|
|
420
|
+
"aria-label": sendButtonLabel,
|
|
421
|
+
onClick: () => void onSend(),
|
|
422
|
+
disabled: sendDisabled,
|
|
390
423
|
children: /* @__PURE__ */ jsx(ArrowUp, { className: "h-5 w-5" })
|
|
391
424
|
})
|
|
392
425
|
})]
|
|
@@ -2047,7 +2080,26 @@ function resolveSafeHref(href) {
|
|
|
2047
2080
|
function isExternalHref(href) {
|
|
2048
2081
|
return /^https?:\/\//i.test(href);
|
|
2049
2082
|
}
|
|
2050
|
-
function
|
|
2083
|
+
function looksLikeLocalFileHref(href) {
|
|
2084
|
+
return href.startsWith("./") || href.startsWith("../") || href.startsWith("/Users/") || href.startsWith("/home/") || href.startsWith("/tmp/") || href.startsWith("/var/") || /^\/.+\.[A-Za-z0-9_-]+(?::\d+(?::\d+)?)?$/.test(href);
|
|
2085
|
+
}
|
|
2086
|
+
function parseLocalFileAction(href) {
|
|
2087
|
+
const normalizedHref = href.split("#")[0]?.split("?")[0] ?? href;
|
|
2088
|
+
const decodedHref = decodeURIComponent(normalizedHref);
|
|
2089
|
+
if (!looksLikeLocalFileHref(decodedHref)) return null;
|
|
2090
|
+
const lineMatch = /^(.*?)(?::(\d+)(?::(\d+))?)$/.exec(decodedHref);
|
|
2091
|
+
const rawPath = lineMatch?.[1] ?? decodedHref;
|
|
2092
|
+
const line = lineMatch?.[2] ? Number(lineMatch[2]) : void 0;
|
|
2093
|
+
const column = lineMatch?.[3] ? Number(lineMatch[3]) : void 0;
|
|
2094
|
+
return {
|
|
2095
|
+
path: rawPath,
|
|
2096
|
+
label: rawPath.split("/").filter(Boolean).pop() ?? rawPath,
|
|
2097
|
+
viewMode: "preview",
|
|
2098
|
+
...typeof line === "number" ? { line } : {},
|
|
2099
|
+
...typeof column === "number" ? { column } : {}
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
function ChatMessageMarkdown({ text, role, texts, inline = false, onFileOpen }) {
|
|
2051
2103
|
const isUser = role === "user";
|
|
2052
2104
|
const markdownComponents = useMemo(() => ({
|
|
2053
2105
|
p: ({ children }) => inline ? /* @__PURE__ */ jsx(Fragment$1, { children }) : /* @__PURE__ */ jsx("p", { children }),
|
|
@@ -2058,9 +2110,17 @@ function ChatMessageMarkdown({ text, role, texts, inline = false }) {
|
|
|
2058
2110
|
children
|
|
2059
2111
|
});
|
|
2060
2112
|
const external = isExternalHref(safeHref);
|
|
2113
|
+
const localFileAction = external ? null : parseLocalFileAction(safeHref);
|
|
2114
|
+
const handleClick = (event) => {
|
|
2115
|
+
if (!onFileOpen || !localFileAction) return;
|
|
2116
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
2117
|
+
event.preventDefault();
|
|
2118
|
+
onFileOpen(localFileAction);
|
|
2119
|
+
};
|
|
2061
2120
|
return /* @__PURE__ */ jsx("a", {
|
|
2062
2121
|
...rest,
|
|
2063
2122
|
href: safeHref,
|
|
2123
|
+
onClick: handleClick,
|
|
2064
2124
|
target: external ? "_blank" : void 0,
|
|
2065
2125
|
rel: external ? "noreferrer noopener" : void 0,
|
|
2066
2126
|
children
|
|
@@ -2110,7 +2170,11 @@ function ChatMessageMarkdown({ text, role, texts, inline = false }) {
|
|
|
2110
2170
|
children
|
|
2111
2171
|
});
|
|
2112
2172
|
}
|
|
2113
|
-
}), [
|
|
2173
|
+
}), [
|
|
2174
|
+
inline,
|
|
2175
|
+
onFileOpen,
|
|
2176
|
+
texts
|
|
2177
|
+
]);
|
|
2114
2178
|
return /* @__PURE__ */ jsx(inline ? "span" : "div", {
|
|
2115
2179
|
className: cn("chat-markdown", isUser ? "chat-markdown-user" : "chat-markdown-assistant"),
|
|
2116
2180
|
children: /* @__PURE__ */ jsx(ReactMarkdown, {
|
|
@@ -2158,7 +2222,7 @@ function ChatInlineTokenBadge({ kind, label, isUser }) {
|
|
|
2158
2222
|
})]
|
|
2159
2223
|
});
|
|
2160
2224
|
}
|
|
2161
|
-
function ChatMessageInlineContent({ segments, role, texts }) {
|
|
2225
|
+
function ChatMessageInlineContent({ segments, role, texts, onFileOpen }) {
|
|
2162
2226
|
const isUser = role === "user";
|
|
2163
2227
|
return /* @__PURE__ */ jsx("div", {
|
|
2164
2228
|
className: "whitespace-pre-wrap break-words leading-6",
|
|
@@ -2176,7 +2240,8 @@ function ChatMessageInlineContent({ segments, role, texts }) {
|
|
|
2176
2240
|
text: segment.text,
|
|
2177
2241
|
role,
|
|
2178
2242
|
texts,
|
|
2179
|
-
inline: true
|
|
2243
|
+
inline: true,
|
|
2244
|
+
onFileOpen
|
|
2180
2245
|
}, `markdown-${index}`);
|
|
2181
2246
|
})
|
|
2182
2247
|
});
|
|
@@ -2568,7 +2633,7 @@ function ChatReasoningBlock({ label, text, isUser, isInProgress }) {
|
|
|
2568
2633
|
ref: scrollRef,
|
|
2569
2634
|
onScroll,
|
|
2570
2635
|
"data-reasoning-scroll": "true",
|
|
2571
|
-
className: cn("mt-2 w-fit max-w-[500px] max-h-56 overflow-y-auto
|
|
2636
|
+
className: cn("mt-2 w-fit max-w-[500px] max-h-56 overflow-y-auto rounded-lg custom-scrollbar-amber", isUser ? "bg-primary-700/60" : "bg-gray-100"),
|
|
2572
2637
|
children: /* @__PURE__ */ jsx("pre", {
|
|
2573
2638
|
className: "min-w-0 whitespace-pre-wrap break-all p-2 text-[11px]",
|
|
2574
2639
|
children: text
|
|
@@ -2648,15 +2713,15 @@ function ToolCardSessionActionButton({ action, onAction }) {
|
|
|
2648
2713
|
function ToolCardHeader({ card, icon: Icon, expanded, canExpand, hideSummary = false, actionSlot, onToggle }) {
|
|
2649
2714
|
const summaryPart = hideSummary ? "" : card.summary?.replace(/^(command|path|args|query|input):\s*/i, "") ?? "";
|
|
2650
2715
|
return /* @__PURE__ */ jsxs("div", {
|
|
2651
|
-
className: cn("flex items-center
|
|
2716
|
+
className: cn("flex items-center gap-3 px-3 py-2.5 transition-colors bg-transparent", canExpand ? "cursor-pointer hover:bg-amber-100/30" : ""),
|
|
2652
2717
|
onClick: onToggle,
|
|
2653
2718
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
2654
|
-
className: "flex items-center gap-2 font-mono
|
|
2719
|
+
className: "flex min-w-0 flex-1 items-center gap-2 overflow-hidden font-mono text-amber-950/80",
|
|
2655
2720
|
children: [/* @__PURE__ */ jsx(Icon, {
|
|
2656
2721
|
className: "h-4 w-4 text-amber-600/80 shrink-0",
|
|
2657
2722
|
strokeWidth: 3
|
|
2658
2723
|
}), /* @__PURE__ */ jsxs("div", {
|
|
2659
|
-
className: "flex items-center gap-1.5
|
|
2724
|
+
className: "flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden",
|
|
2660
2725
|
children: [/* @__PURE__ */ jsx("span", {
|
|
2661
2726
|
className: "font-bold shrink-0 tracking-tight",
|
|
2662
2727
|
children: card.toolName
|
|
@@ -2664,7 +2729,7 @@ function ToolCardHeader({ card, icon: Icon, expanded, canExpand, hideSummary = f
|
|
|
2664
2729
|
className: "text-amber-300 font-bold select-none shrink-0",
|
|
2665
2730
|
children: "›"
|
|
2666
2731
|
}), /* @__PURE__ */ jsx("span", {
|
|
2667
|
-
className: "
|
|
2732
|
+
className: "block min-w-0 flex-1 truncate font-normal",
|
|
2668
2733
|
title: summaryPart,
|
|
2669
2734
|
children: summaryPart
|
|
2670
2735
|
})] })]
|
|
@@ -2693,8 +2758,9 @@ function ToolCardHeaderSessionAction({ action, onAction }) {
|
|
|
2693
2758
|
}
|
|
2694
2759
|
//#endregion
|
|
2695
2760
|
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-file-operation-lines.tsx
|
|
2696
|
-
const
|
|
2697
|
-
const
|
|
2761
|
+
const FILE_TEXT_CLASS_NAME = "font-mono text-[11px] leading-5";
|
|
2762
|
+
const FILE_ROW_CLASS_NAME = `h-5 ${FILE_TEXT_CLASS_NAME}`;
|
|
2763
|
+
const FILE_LINE_NUMBER_CELL_CLASS_NAME = "flex h-5 items-center justify-center px-2.5 tabular-nums select-none";
|
|
2698
2764
|
function formatLineNumber(value) {
|
|
2699
2765
|
return typeof value === "number" ? String(value) : "";
|
|
2700
2766
|
}
|
|
@@ -2731,6 +2797,22 @@ function getCodeRowTone(line) {
|
|
|
2731
2797
|
if (line.kind === "add") return "bg-emerald-50 text-emerald-950";
|
|
2732
2798
|
return "bg-white text-amber-950/80";
|
|
2733
2799
|
}
|
|
2800
|
+
function FileOperationLineNumberCell({ layout, line, lineNumberColumnWidth }) {
|
|
2801
|
+
const lineNumberCellClassName = layout === "compact" ? "sticky left-0 z-[1]" : "w-full shrink-0";
|
|
2802
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2803
|
+
"data-file-line-number-cell": "true",
|
|
2804
|
+
style: layout === "compact" ? { width: lineNumberColumnWidth } : void 0,
|
|
2805
|
+
className: cn(FILE_LINE_NUMBER_CELL_CLASS_NAME, lineNumberCellClassName, getLineNumberTone(line)),
|
|
2806
|
+
children: readVisibleLineNumber(line)
|
|
2807
|
+
});
|
|
2808
|
+
}
|
|
2809
|
+
function FileOperationCodeCell({ line }) {
|
|
2810
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2811
|
+
"data-file-code-row": "true",
|
|
2812
|
+
className: cn("block min-w-full whitespace-pre px-2.5", getCodeRowTone(line)),
|
|
2813
|
+
children: line.text || " "
|
|
2814
|
+
});
|
|
2815
|
+
}
|
|
2734
2816
|
function FileOperationLineRow({ line, showLineNumbers, lineNumberColumnWidth }) {
|
|
2735
2817
|
return /* @__PURE__ */ jsxs("div", {
|
|
2736
2818
|
"data-file-line-row": "true",
|
|
@@ -2739,22 +2821,50 @@ function FileOperationLineRow({ line, showLineNumbers, lineNumberColumnWidth })
|
|
|
2739
2821
|
showLineNumbers,
|
|
2740
2822
|
lineNumberColumnWidth
|
|
2741
2823
|
}),
|
|
2742
|
-
children: [showLineNumbers ? /* @__PURE__ */ jsx(
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2824
|
+
children: [showLineNumbers ? /* @__PURE__ */ jsx(FileOperationLineNumberCell, {
|
|
2825
|
+
layout: "compact",
|
|
2826
|
+
line,
|
|
2827
|
+
lineNumberColumnWidth
|
|
2828
|
+
}) : null, /* @__PURE__ */ jsx(FileOperationCodeCell, { line })]
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
function FileOperationWorkspaceSurface({ block }) {
|
|
2832
|
+
const showLineNumbers = readHasBlockLineNumbers(block);
|
|
2833
|
+
const lineNumberColumnWidth = readLineNumberColumnWidth(block);
|
|
2834
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2835
|
+
"data-file-code-surface": "true",
|
|
2836
|
+
"data-file-code-surface-layout": "workspace",
|
|
2837
|
+
className: "flex h-full min-h-full min-w-full bg-white",
|
|
2838
|
+
children: [showLineNumbers ? /* @__PURE__ */ jsxs("div", {
|
|
2839
|
+
"data-file-code-gutter": "true",
|
|
2840
|
+
style: {
|
|
2841
|
+
width: lineNumberColumnWidth,
|
|
2842
|
+
minWidth: lineNumberColumnWidth
|
|
2843
|
+
},
|
|
2844
|
+
className: cn("flex h-full min-h-full shrink-0 flex-col", FILE_TEXT_CLASS_NAME),
|
|
2845
|
+
children: [block.lines.map((line, index) => /* @__PURE__ */ jsx(FileOperationLineNumberCell, {
|
|
2846
|
+
layout: "workspace",
|
|
2847
|
+
line,
|
|
2848
|
+
lineNumberColumnWidth
|
|
2849
|
+
}, readLineKey("gutter", line, index))), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 border-r border-stone-200 bg-stone-100" })]
|
|
2850
|
+
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
2851
|
+
"data-file-code-canvas": "true",
|
|
2852
|
+
className: "flex h-full min-h-full min-w-0 flex-1 flex-col bg-white",
|
|
2853
|
+
children: [block.lines.map((line, index) => /* @__PURE__ */ jsx("div", {
|
|
2854
|
+
"data-file-code-canvas-row": "true",
|
|
2855
|
+
className: FILE_ROW_CLASS_NAME,
|
|
2856
|
+
children: /* @__PURE__ */ jsx(FileOperationCodeCell, { line })
|
|
2857
|
+
}, readLineKey("code", line, index))), /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 bg-white" })]
|
|
2751
2858
|
})]
|
|
2752
2859
|
});
|
|
2753
2860
|
}
|
|
2754
|
-
function
|
|
2861
|
+
function FileOperationCodeSurface({ block, layout = "compact" }) {
|
|
2862
|
+
if (layout === "workspace") return /* @__PURE__ */ jsx(FileOperationWorkspaceSurface, { block });
|
|
2755
2863
|
const showLineNumbers = readHasBlockLineNumbers(block);
|
|
2756
2864
|
const lineNumberColumnWidth = readLineNumberColumnWidth(block);
|
|
2757
2865
|
return /* @__PURE__ */ jsx("div", {
|
|
2866
|
+
"data-file-code-surface": "true",
|
|
2867
|
+
"data-file-code-surface-layout": "compact",
|
|
2758
2868
|
className: "overflow-x-auto custom-scrollbar-amber bg-white",
|
|
2759
2869
|
children: block.lines.map((line, index) => /* @__PURE__ */ jsx(FileOperationLineRow, {
|
|
2760
2870
|
line,
|
|
@@ -2763,6 +2873,12 @@ function FileOperationLinesGrid({ block }) {
|
|
|
2763
2873
|
}, readLineKey("row", line, index)))
|
|
2764
2874
|
});
|
|
2765
2875
|
}
|
|
2876
|
+
function FileOperationLinesGrid({ block }) {
|
|
2877
|
+
return /* @__PURE__ */ jsx(FileOperationCodeSurface, {
|
|
2878
|
+
block,
|
|
2879
|
+
layout: "compact"
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2766
2882
|
//#endregion
|
|
2767
2883
|
//#region src/components/chat/ui/chat-message-list/tool-card/tool-card-file-operation.tsx
|
|
2768
2884
|
function isPreviewBlock(block) {
|
|
@@ -2796,6 +2912,20 @@ function renderCaption(caption) {
|
|
|
2796
2912
|
})] }, `${part}-${index}`))
|
|
2797
2913
|
});
|
|
2798
2914
|
}
|
|
2915
|
+
function buildFileOpenAction(block) {
|
|
2916
|
+
return {
|
|
2917
|
+
path: block.path,
|
|
2918
|
+
label: block.path.split("/").filter(Boolean).pop() ?? block.path,
|
|
2919
|
+
viewMode: block.display === "diff" ? "diff" : "preview",
|
|
2920
|
+
...block.rawText ? { rawText: block.rawText } : {},
|
|
2921
|
+
...block.beforeText ? { beforeText: block.beforeText } : {},
|
|
2922
|
+
...block.afterText ? { afterText: block.afterText } : {},
|
|
2923
|
+
...block.patchText ? { patchText: block.patchText } : {},
|
|
2924
|
+
...typeof block.oldStartLine === "number" ? { oldStartLine: block.oldStartLine } : {},
|
|
2925
|
+
...typeof block.newStartLine === "number" ? { newStartLine: block.newStartLine } : {},
|
|
2926
|
+
fullLines: block.fullLines ?? block.lines
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2799
2929
|
function StickyFileOperationScrollArea({ children, contentVersion, resetKey, className, scrollKind }) {
|
|
2800
2930
|
const scrollRef = useRef(null);
|
|
2801
2931
|
const { onScroll } = useStickyBottomScroll({
|
|
@@ -2810,14 +2940,18 @@ function StickyFileOperationScrollArea({ children, contentVersion, resetKey, cla
|
|
|
2810
2940
|
ref: scrollRef,
|
|
2811
2941
|
onScroll,
|
|
2812
2942
|
"data-file-scroll-kind": scrollKind,
|
|
2813
|
-
className: cn("overflow-y-auto
|
|
2943
|
+
className: cn("overflow-y-auto bg-white custom-scrollbar-amber", className),
|
|
2814
2944
|
children
|
|
2815
2945
|
});
|
|
2816
2946
|
}
|
|
2817
|
-
function FileOperationBlock({ block, showPathRow, isFirst }) {
|
|
2947
|
+
function FileOperationBlock({ block, showPathRow, isFirst, onFileOpen }) {
|
|
2818
2948
|
const { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } = ChatUiPrimitives;
|
|
2819
2949
|
const previewBlock = isPreviewBlock(block);
|
|
2820
2950
|
const showMetaRow = showPathRow || Boolean(block.caption);
|
|
2951
|
+
const handlePathClick = () => {
|
|
2952
|
+
if (!onFileOpen) return;
|
|
2953
|
+
onFileOpen(buildFileOpenAction(block));
|
|
2954
|
+
};
|
|
2821
2955
|
return /* @__PURE__ */ jsxs("section", {
|
|
2822
2956
|
className: cn("overflow-hidden bg-white", !isFirst && "border-t border-stone-200/80"),
|
|
2823
2957
|
children: [
|
|
@@ -2829,8 +2963,10 @@ function FileOperationBlock({ block, showPathRow, isFirst }) {
|
|
|
2829
2963
|
delayDuration: 250,
|
|
2830
2964
|
children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
2831
2965
|
asChild: true,
|
|
2832
|
-
children: /* @__PURE__ */ jsx("
|
|
2833
|
-
|
|
2966
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
2967
|
+
type: "button",
|
|
2968
|
+
onClick: handlePathClick,
|
|
2969
|
+
className: cn("w-full truncate whitespace-nowrap text-left font-mono text-[12px] font-medium text-stone-700", onFileOpen && "transition-colors hover:text-amber-900 hover:underline"),
|
|
2834
2970
|
title: block.path,
|
|
2835
2971
|
children: block.path
|
|
2836
2972
|
})
|
|
@@ -2868,7 +3004,7 @@ function FileOperationBlock({ block, showPathRow, isFirst }) {
|
|
|
2868
3004
|
]
|
|
2869
3005
|
});
|
|
2870
3006
|
}
|
|
2871
|
-
function ToolCardFileOperationContent({ card, className }) {
|
|
3007
|
+
function ToolCardFileOperationContent({ card, className, onFileOpen }) {
|
|
2872
3008
|
const blocks = card.fileOperation?.blocks ?? [];
|
|
2873
3009
|
const output = card.output?.trim() ?? "";
|
|
2874
3010
|
if (blocks.length === 0 && !output) return null;
|
|
@@ -2878,7 +3014,8 @@ function ToolCardFileOperationContent({ card, className }) {
|
|
|
2878
3014
|
return /* @__PURE__ */ jsx(FileOperationBlock, {
|
|
2879
3015
|
block,
|
|
2880
3016
|
showPathRow: true,
|
|
2881
|
-
isFirst: index === 0
|
|
3017
|
+
isFirst: index === 0,
|
|
3018
|
+
onFileOpen
|
|
2882
3019
|
}, block.key);
|
|
2883
3020
|
}), output ? /* @__PURE__ */ jsx(StickyFileOperationScrollArea, {
|
|
2884
3021
|
resetKey: `output:${card.toolName}:${card.summary ?? "none"}`,
|
|
@@ -3074,7 +3211,7 @@ function TerminalExecutionView({ card }) {
|
|
|
3074
3211
|
children: [output, isRunning && /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-3 ml-1 bg-amber-500/60 animate-pulse align-middle" })]
|
|
3075
3212
|
}) })] })] });
|
|
3076
3213
|
}
|
|
3077
|
-
function FileOperationView({ card }) {
|
|
3214
|
+
function FileOperationView({ card, onFileOpen }) {
|
|
3078
3215
|
const output = card.output?.trim() ?? "";
|
|
3079
3216
|
const isRunning = card.statusTone === "running";
|
|
3080
3217
|
const hasStructuredPreview = Boolean(card.fileOperation?.blocks.length);
|
|
@@ -3102,7 +3239,10 @@ function FileOperationView({ card }) {
|
|
|
3102
3239
|
onToggle
|
|
3103
3240
|
}), expanded && hasContent && /* @__PURE__ */ jsx(ToolCardContent, {
|
|
3104
3241
|
className: "border-t border-amber-200/20 bg-transparent px-0 pt-0 pb-0",
|
|
3105
|
-
children: /* @__PURE__ */ jsx(ToolCardFileOperationContent, {
|
|
3242
|
+
children: /* @__PURE__ */ jsx(ToolCardFileOperationContent, {
|
|
3243
|
+
card,
|
|
3244
|
+
onFileOpen
|
|
3245
|
+
})
|
|
3106
3246
|
})] });
|
|
3107
3247
|
}
|
|
3108
3248
|
function SearchSnippetView({ card }) {
|
|
@@ -3181,9 +3321,12 @@ function isSearchTool(name) {
|
|
|
3181
3321
|
const lowered = name.toLowerCase();
|
|
3182
3322
|
return lowered === "grep_search" || lowered === "find_files" || lowered.includes("search");
|
|
3183
3323
|
}
|
|
3184
|
-
function ChatToolCard({ card, onToolAction, renderToolAgent }) {
|
|
3324
|
+
function ChatToolCard({ card, onToolAction, onFileOpen, renderToolAgent }) {
|
|
3185
3325
|
if (isTerminalTool(card.toolName)) return /* @__PURE__ */ jsx(TerminalExecutionView, { card });
|
|
3186
|
-
if (isFileEditTool(card.toolName)) return /* @__PURE__ */ jsx(FileOperationView, {
|
|
3326
|
+
if (isFileEditTool(card.toolName)) return /* @__PURE__ */ jsx(FileOperationView, {
|
|
3327
|
+
card,
|
|
3328
|
+
onFileOpen
|
|
3329
|
+
});
|
|
3187
3330
|
if (isSearchTool(card.toolName)) return /* @__PURE__ */ jsx(SearchSnippetView, { card });
|
|
3188
3331
|
return /* @__PURE__ */ jsx(GenericToolCard, {
|
|
3189
3332
|
card,
|
|
@@ -3212,7 +3355,7 @@ function ChatUnknownPart(props) {
|
|
|
3212
3355
|
//#endregion
|
|
3213
3356
|
//#region src/components/chat/ui/chat-message-list/chat-message.tsx
|
|
3214
3357
|
const ChatMessage = memo(function ChatMessage(props) {
|
|
3215
|
-
const { message, texts, onToolAction, renderToolAgent } = props;
|
|
3358
|
+
const { message, texts, onToolAction, onFileOpen, renderToolAgent } = props;
|
|
3216
3359
|
const { role } = message;
|
|
3217
3360
|
const isUser = role === "user";
|
|
3218
3361
|
const isMessageInProgress = message.status === "pending" || message.status === "streaming";
|
|
@@ -3225,12 +3368,14 @@ const ChatMessage = memo(function ChatMessage(props) {
|
|
|
3225
3368
|
if (type === "markdown") return /* @__PURE__ */ jsx(ChatMessageMarkdown, {
|
|
3226
3369
|
text: part.text,
|
|
3227
3370
|
role,
|
|
3228
|
-
texts
|
|
3371
|
+
texts,
|
|
3372
|
+
onFileOpen
|
|
3229
3373
|
}, `markdown-${index}`);
|
|
3230
3374
|
if (type === "inline-content") return /* @__PURE__ */ jsx(ChatMessageInlineContent, {
|
|
3231
3375
|
segments: part.segments,
|
|
3232
3376
|
role,
|
|
3233
|
-
texts
|
|
3377
|
+
texts,
|
|
3378
|
+
onFileOpen
|
|
3234
3379
|
}, `inline-content-${index}`);
|
|
3235
3380
|
if (type === "reasoning") return /* @__PURE__ */ jsx(ChatReasoningBlock, {
|
|
3236
3381
|
label: part.label,
|
|
@@ -3243,6 +3388,7 @@ const ChatMessage = memo(function ChatMessage(props) {
|
|
|
3243
3388
|
children: /* @__PURE__ */ jsx(ChatToolCard, {
|
|
3244
3389
|
card: part.card,
|
|
3245
3390
|
onToolAction,
|
|
3391
|
+
onFileOpen,
|
|
3246
3392
|
renderToolAgent
|
|
3247
3393
|
})
|
|
3248
3394
|
}, `tool-${index}`);
|
|
@@ -3339,6 +3485,7 @@ function ChatMessageList(props) {
|
|
|
3339
3485
|
message,
|
|
3340
3486
|
texts: props.texts,
|
|
3341
3487
|
onToolAction: props.onToolAction,
|
|
3488
|
+
onFileOpen: props.onFileOpen,
|
|
3342
3489
|
renderToolAgent: props.renderToolAgent
|
|
3343
3490
|
}), /* @__PURE__ */ jsx("div", {
|
|
3344
3491
|
className: cn("flex items-center gap-2", isUser && "justify-end"),
|
|
@@ -3365,4 +3512,4 @@ function ChatMessageList(props) {
|
|
|
3365
3512
|
});
|
|
3366
3513
|
}
|
|
3367
3514
|
//#endregion
|
|
3368
|
-
export { ChatInputBar, ChatMessageList, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
|
|
3515
|
+
export { ChatInputBar, ChatMessageList, ChatMessageMarkdown, FileOperationCodeSurface, FileOperationLinesGrid, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
|