@springbrand/message-panel 0.1.2 → 0.1.3-alpha.0
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/demo/camel-chat-showcase.tsx +1265 -0
- package/demo/fixtures.ts +292 -0
- package/demo/message-panel-chat-showcase.tsx +250 -0
- package/demo/message-panel-gallery.tsx +389 -0
- package/package.json +6 -16
- package/src/adapters/ai-sdk.ts +32 -0
- package/src/camel/camel-chat-messages.tsx +1423 -0
- package/src/camel/camel-model-select.tsx +88 -0
- package/src/camel/camel-plan-bar.tsx +115 -0
- package/src/camel/camel-prompt-input.tsx +547 -0
- package/src/camel/camel-tool-presentation.tsx +1270 -0
- package/src/camel/camel-turn.ts +316 -0
- package/src/camel/turn-summary-bar.tsx +81 -0
- package/src/chat-summary-panel.tsx +229 -0
- package/src/composer/chat-composer.tsx +216 -0
- package/src/composer/composer-attachments.tsx +138 -0
- package/src/composer/composer-trigger-popover.tsx +236 -0
- package/src/composer/composer.tsx +425 -0
- package/src/composer/drop-controller.ts +80 -0
- package/src/composer/types.ts +79 -0
- package/src/contracts.ts +138 -0
- package/src/conversation.tsx +76 -0
- package/src/internal/cn.ts +3 -0
- package/src/message-panel.tsx +368 -0
- package/src/message.tsx +254 -0
- package/src/parts/index.tsx +1149 -0
- package/src/parts/kind.ts +67 -0
- package/src/parts/plan.ts +63 -0
- package/src/parts/primitives.tsx +439 -0
- package/src/registry.ts +44 -0
- package/src/status.tsx +132 -0
- package/dist/demo/camel-chat-showcase.d.ts +0 -1
- package/dist/demo/camel-chat-showcase.js +0 -1007
- package/dist/demo/fixtures.d.ts +0 -32
- package/dist/demo/fixtures.js +0 -250
- package/dist/demo/index.js +0 -4
- package/dist/demo/message-panel-chat-showcase.d.ts +0 -1
- package/dist/demo/message-panel-chat-showcase.js +0 -114
- package/dist/demo/message-panel-gallery.d.ts +0 -1
- package/dist/demo/message-panel-gallery.js +0 -130
- package/dist/src/adapters/ai-sdk.d.ts +0 -4
- package/dist/src/adapters/ai-sdk.js +0 -25
- package/dist/src/camel/camel-chat-messages.d.ts +0 -44
- package/dist/src/camel/camel-chat-messages.js +0 -421
- package/dist/src/camel/camel-model-select.d.ts +0 -13
- package/dist/src/camel/camel-model-select.js +0 -7
- package/dist/src/camel/camel-plan-bar.d.ts +0 -5
- package/dist/src/camel/camel-plan-bar.js +0 -24
- package/dist/src/camel/camel-prompt-input.d.ts +0 -32
- package/dist/src/camel/camel-prompt-input.js +0 -182
- package/dist/src/camel/camel-tool-presentation.d.ts +0 -21
- package/dist/src/camel/camel-tool-presentation.js +0 -784
- package/dist/src/camel/camel-turn.d.ts +0 -57
- package/dist/src/camel/camel-turn.js +0 -203
- package/dist/src/camel/index.js +0 -7
- package/dist/src/camel/turn-summary-bar.d.ts +0 -8
- package/dist/src/camel/turn-summary-bar.js +0 -12
- package/dist/src/chat-summary-panel.d.ts +0 -22
- package/dist/src/chat-summary-panel.js +0 -26
- package/dist/src/composer/chat-composer.d.ts +0 -4
- package/dist/src/composer/chat-composer.js +0 -90
- package/dist/src/composer/composer-attachments.d.ts +0 -13
- package/dist/src/composer/composer-attachments.js +0 -47
- package/dist/src/composer/composer-trigger-popover.d.ts +0 -31
- package/dist/src/composer/composer-trigger-popover.js +0 -29
- package/dist/src/composer/composer.d.ts +0 -15
- package/dist/src/composer/composer.js +0 -180
- package/dist/src/composer/drop-controller.d.ts +0 -8
- package/dist/src/composer/drop-controller.js +0 -69
- package/dist/src/composer/index.js +0 -6
- package/dist/src/composer/types.d.ts +0 -62
- package/dist/src/composer/types.js +0 -1
- package/dist/src/contracts.d.ts +0 -94
- package/dist/src/contracts.js +0 -1
- package/dist/src/conversation.d.ts +0 -13
- package/dist/src/conversation.js +0 -18
- package/dist/src/index.js +0 -12
- package/dist/src/internal/cn.d.ts +0 -1
- package/dist/src/internal/cn.js +0 -3
- package/dist/src/message-panel.d.ts +0 -4
- package/dist/src/message-panel.js +0 -144
- package/dist/src/message.d.ts +0 -36
- package/dist/src/message.js +0 -83
- package/dist/src/parts/index.d.ts +0 -187
- package/dist/src/parts/index.js +0 -332
- package/dist/src/parts/kind.d.ts +0 -3
- package/dist/src/parts/kind.js +0 -69
- package/dist/src/parts/plan.d.ts +0 -10
- package/dist/src/parts/plan.js +0 -40
- package/dist/src/parts/primitives.d.ts +0 -70
- package/dist/src/parts/primitives.js +0 -153
- package/dist/src/registry.d.ts +0 -4
- package/dist/src/registry.js +0 -31
- package/dist/src/status.d.ts +0 -26
- package/dist/src/status.js +0 -74
- /package/{dist/demo/index.d.ts → demo/index.ts} +0 -0
- /package/{dist/src/camel/index.d.ts → src/camel/index.ts} +0 -0
- /package/{dist/src/composer/index.d.ts → src/composer/index.ts} +0 -0
- /package/{dist/src/index.d.ts → src/index.ts} +0 -0
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ArrowUpIcon, Loader2Icon, PlusIcon, SquareIcon, } from "lucide-react";
|
|
3
|
-
import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState, } from "react";
|
|
4
|
-
import { cn } from "../internal/cn";
|
|
5
|
-
import { AttachmentPreview, AttachmentRemove, AttachmentReorder, AttachmentRetry, Attachments, } from "./composer-attachments";
|
|
6
|
-
import { useComposerDrop } from "./drop-controller";
|
|
7
|
-
export const ComposerHeader = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("sb-composer__header", className), ...props })));
|
|
8
|
-
ComposerHeader.displayName = "ComposerHeader";
|
|
9
|
-
export const ComposerBody = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("sb-composer__body", className), ...props })));
|
|
10
|
-
ComposerBody.displayName = "ComposerBody";
|
|
11
|
-
export const ComposerFooter = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("sb-composer__footer", className), ...props })));
|
|
12
|
-
ComposerFooter.displayName = "ComposerFooter";
|
|
13
|
-
export const ComposerTools = forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("sb-composer__tools", className), ...props })));
|
|
14
|
-
ComposerTools.displayName = "ComposerTools";
|
|
15
|
-
export const ComposerSubmit = forwardRef(({ className, status = "idle", children, ...props }, ref) => (_jsx("button", { ref: ref, type: "button", className: cn("sb-composer__submit", className), ...props, children: children ??
|
|
16
|
-
(status === "submitted" ? (_jsx(Loader2Icon, { size: 16, className: "sb-spin" })) : status === "streaming" ? (_jsx(SquareIcon, { size: 15 })) : (_jsx(ArrowUpIcon, { size: 17 }))) })));
|
|
17
|
-
ComposerSubmit.displayName = "ComposerSubmit";
|
|
18
|
-
export function DropOverlay({ label = "松开以添加文件", }) {
|
|
19
|
-
return (_jsxs("div", { className: "sb-composer__drop", role: "status", children: [_jsx(PlusIcon, { size: 20 }), _jsx("span", { children: label })] }));
|
|
20
|
-
}
|
|
21
|
-
const ATTACHMENT_DRAG_TYPE = "application/x-sb-message-panel-attachment";
|
|
22
|
-
function defaultAttachment(attachment, index) {
|
|
23
|
-
const record = attachment != null && typeof attachment === "object"
|
|
24
|
-
? attachment
|
|
25
|
-
: {};
|
|
26
|
-
return {
|
|
27
|
-
id: String(record.id ?? `attachment-${index}`),
|
|
28
|
-
name: String(record.name ?? record.filename ?? "附件"),
|
|
29
|
-
mediaType: typeof record.mediaType === "string" ? record.mediaType : undefined,
|
|
30
|
-
size: typeof record.size === "number" ? record.size : undefined,
|
|
31
|
-
url: typeof record.url === "string" ? record.url : undefined,
|
|
32
|
-
status: record.status === "uploading" || record.status === "error"
|
|
33
|
-
? record.status
|
|
34
|
-
: "ready",
|
|
35
|
-
note: typeof record.note === "string" ? record.note : undefined,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
function matchesAccept(file, accept) {
|
|
39
|
-
if (!accept?.trim())
|
|
40
|
-
return true;
|
|
41
|
-
return accept
|
|
42
|
-
.split(",")
|
|
43
|
-
.map((value) => value.trim().toLowerCase())
|
|
44
|
-
.some((rule) => {
|
|
45
|
-
if (rule.startsWith("."))
|
|
46
|
-
return file.name.toLowerCase().endsWith(rule);
|
|
47
|
-
if (rule.endsWith("/*"))
|
|
48
|
-
return file.type.startsWith(rule.slice(0, -1));
|
|
49
|
-
return file.type.toLowerCase() === rule;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
function ComposerInner({ value, defaultValue = "", onValueChange, status, attachments = [], getAttachment = defaultAttachment, dropScope = "composer", dropTarget, accept, multiple = true, maxFiles, maxFileSize, onFilesAdded, onAttachmentRemove, onAttachmentRetry, onAttachmentReorder, onValidationError, onSubmit, onStop, placeholder = "问点什么…", tools, textareaProps, textareaRef: externalTextareaRef, className, ...props }, forwardedRef) {
|
|
53
|
-
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
54
|
-
const resolvedValue = value ?? internalValue;
|
|
55
|
-
const rootRef = useRef(null);
|
|
56
|
-
const localTextareaRef = useRef(null);
|
|
57
|
-
const inputRef = useRef(null);
|
|
58
|
-
useImperativeHandle(forwardedRef, () => rootRef.current);
|
|
59
|
-
useImperativeHandle(externalTextareaRef, () => localTextareaRef.current);
|
|
60
|
-
const attachmentViews = useMemo(() => attachments.map((attachment, index) => getAttachment(attachment, index)), [attachments, getAttachment]);
|
|
61
|
-
const generating = status === "submitted" || status === "streaming";
|
|
62
|
-
const allReady = attachmentViews.every((attachment) => attachment.status === "ready");
|
|
63
|
-
const canSend = (resolvedValue.trim().length > 0 || attachments.length > 0) &&
|
|
64
|
-
!generating &&
|
|
65
|
-
allReady;
|
|
66
|
-
const changeValue = useCallback((next) => {
|
|
67
|
-
if (value === undefined)
|
|
68
|
-
setInternalValue(next);
|
|
69
|
-
onValueChange?.(next);
|
|
70
|
-
}, [value, onValueChange]);
|
|
71
|
-
const addFiles = useCallback((incoming) => {
|
|
72
|
-
const all = Array.from(incoming);
|
|
73
|
-
const remaining = maxFiles === undefined
|
|
74
|
-
? all.length
|
|
75
|
-
: Math.max(0, maxFiles - attachments.length);
|
|
76
|
-
const selected = multiple ? all.slice(0, remaining) : all.slice(0, 1);
|
|
77
|
-
const valid = selected.filter((file) => matchesAccept(file, accept) &&
|
|
78
|
-
(maxFileSize === undefined || file.size <= maxFileSize));
|
|
79
|
-
const invalid = selected.filter((file) => !valid.includes(file));
|
|
80
|
-
if (all.length > selected.length) {
|
|
81
|
-
onValidationError?.(`最多只能添加 ${maxFiles ?? 1} 个附件`, all);
|
|
82
|
-
}
|
|
83
|
-
if (invalid.length) {
|
|
84
|
-
onValidationError?.("存在不支持或超过大小限制的文件", invalid);
|
|
85
|
-
}
|
|
86
|
-
if (valid.length)
|
|
87
|
-
void onFilesAdded?.(valid);
|
|
88
|
-
}, [
|
|
89
|
-
accept,
|
|
90
|
-
attachments.length,
|
|
91
|
-
maxFiles,
|
|
92
|
-
maxFileSize,
|
|
93
|
-
multiple,
|
|
94
|
-
onFilesAdded,
|
|
95
|
-
onValidationError,
|
|
96
|
-
]);
|
|
97
|
-
const dragging = useComposerDrop({
|
|
98
|
-
scope: dropScope,
|
|
99
|
-
rootRef,
|
|
100
|
-
target: dropTarget,
|
|
101
|
-
onFiles: addFiles,
|
|
102
|
-
});
|
|
103
|
-
const submit = useCallback(async () => {
|
|
104
|
-
if (!canSend)
|
|
105
|
-
return;
|
|
106
|
-
await onSubmit({ text: resolvedValue.trim(), attachments });
|
|
107
|
-
if (value === undefined)
|
|
108
|
-
setInternalValue("");
|
|
109
|
-
}, [canSend, onSubmit, resolvedValue, attachments, value]);
|
|
110
|
-
const handleKeyDown = (event) => {
|
|
111
|
-
textareaProps?.onKeyDown?.(event);
|
|
112
|
-
if (event.defaultPrevented)
|
|
113
|
-
return;
|
|
114
|
-
if (event.key === "Enter" &&
|
|
115
|
-
!event.shiftKey &&
|
|
116
|
-
!event.nativeEvent.isComposing) {
|
|
117
|
-
event.preventDefault();
|
|
118
|
-
if (canSend)
|
|
119
|
-
void submit();
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
if (event.key === "Backspace" &&
|
|
123
|
-
event.currentTarget.value === "" &&
|
|
124
|
-
attachments.length > 0) {
|
|
125
|
-
const last = attachments[attachments.length - 1];
|
|
126
|
-
if (last) {
|
|
127
|
-
event.preventDefault();
|
|
128
|
-
onAttachmentRemove?.(last);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
const handlePaste = (event) => {
|
|
133
|
-
textareaProps?.onPaste?.(event);
|
|
134
|
-
if (event.defaultPrevented)
|
|
135
|
-
return;
|
|
136
|
-
const files = Array.from(event.clipboardData.items)
|
|
137
|
-
.filter((item) => item.kind === "file")
|
|
138
|
-
.map((item) => item.getAsFile())
|
|
139
|
-
.filter((file) => file !== null);
|
|
140
|
-
if (files.length) {
|
|
141
|
-
event.preventDefault();
|
|
142
|
-
addFiles(files);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
return (_jsxs("div", { ref: rootRef, className: cn("sb-composer", className), "data-drop-scope": dropScope, "data-dragging": dragging ? "true" : undefined, "data-status": status, ...props, children: [_jsx("input", { ref: inputRef, className: "sb-sr-only", type: "file", accept: accept, multiple: multiple, tabIndex: -1, "aria-label": "\u4E0A\u4F20\u6587\u4EF6", onChange: (event) => {
|
|
146
|
-
if (event.currentTarget.files)
|
|
147
|
-
addFiles(event.currentTarget.files);
|
|
148
|
-
event.currentTarget.value = "";
|
|
149
|
-
} }), attachments.length > 0 && (_jsx(ComposerHeader, { children: _jsx(Attachments, { children: attachments.map((attachment, index) => {
|
|
150
|
-
const view = attachmentViews[index];
|
|
151
|
-
return (_jsx(AttachmentPreview, { attachment: view, draggable: Boolean(onAttachmentReorder), "aria-roledescription": onAttachmentReorder ? "可排序附件" : undefined, onDragStart: (event) => {
|
|
152
|
-
if (!onAttachmentReorder)
|
|
153
|
-
return;
|
|
154
|
-
event.dataTransfer.effectAllowed = "move";
|
|
155
|
-
event.dataTransfer.setData(ATTACHMENT_DRAG_TYPE, String(index));
|
|
156
|
-
}, onDragOver: (event) => {
|
|
157
|
-
if (onAttachmentReorder &&
|
|
158
|
-
event.dataTransfer.types.includes(ATTACHMENT_DRAG_TYPE)) {
|
|
159
|
-
event.preventDefault();
|
|
160
|
-
event.dataTransfer.dropEffect = "move";
|
|
161
|
-
}
|
|
162
|
-
}, onDrop: (event) => {
|
|
163
|
-
if (!onAttachmentReorder)
|
|
164
|
-
return;
|
|
165
|
-
const fromIndex = Number(event.dataTransfer.getData(ATTACHMENT_DRAG_TYPE));
|
|
166
|
-
if (Number.isInteger(fromIndex) &&
|
|
167
|
-
fromIndex >= 0 &&
|
|
168
|
-
fromIndex < attachments.length &&
|
|
169
|
-
fromIndex !== index) {
|
|
170
|
-
event.preventDefault();
|
|
171
|
-
onAttachmentReorder(fromIndex, index);
|
|
172
|
-
}
|
|
173
|
-
}, actions: _jsxs(_Fragment, { children: [view.status === "error" && onAttachmentRetry && (_jsx(AttachmentRetry, { onClick: () => void onAttachmentRetry(attachment) })), onAttachmentReorder && index > 0 && (_jsx(AttachmentReorder, { direction: "left", onClick: () => onAttachmentReorder(index, index - 1) })), onAttachmentReorder && index < attachments.length - 1 && (_jsx(AttachmentReorder, { direction: "right", onClick: () => onAttachmentReorder(index, index + 1) })), onAttachmentRemove && (_jsx(AttachmentRemove, { onClick: () => onAttachmentRemove(attachment) }))] }) }, view.id));
|
|
174
|
-
}) }) })), _jsx(ComposerBody, { children: _jsx("textarea", { ...textareaProps, ref: localTextareaRef, value: resolvedValue, rows: 1, placeholder: placeholder, onChange: (event) => changeValue(event.currentTarget.value), onKeyDown: handleKeyDown, onPaste: handlePaste }) }), _jsxs(ComposerFooter, { children: [_jsxs(ComposerTools, { children: [_jsx("button", { type: "button", className: "sb-composer__tool", "aria-label": "\u6DFB\u52A0\u56FE\u7247\u6216\u6587\u4EF6", onClick: () => inputRef.current?.click(), children: _jsx(PlusIcon, { size: 17 }) }), tools] }), _jsx(ComposerSubmit, { status: status === "submitted"
|
|
175
|
-
? "submitted"
|
|
176
|
-
: status === "streaming"
|
|
177
|
-
? "streaming"
|
|
178
|
-
: "idle", disabled: generating ? !onStop : !canSend, "aria-label": generating ? "停止" : "发送", onClick: () => (generating ? void onStop?.() : void submit()) })] }), dragging && _jsx(DropOverlay, {})] }));
|
|
179
|
-
}
|
|
180
|
-
export const Composer = forwardRef(ComposerInner);
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { type RefObject } from "react";
|
|
2
|
-
import type { ComposerDropScope } from "./types";
|
|
3
|
-
export declare function useComposerDrop({ scope, rootRef, target, onFiles, }: {
|
|
4
|
-
scope: ComposerDropScope;
|
|
5
|
-
rootRef: RefObject<HTMLElement | null>;
|
|
6
|
-
target?: HTMLElement | null;
|
|
7
|
-
onFiles: (files: FileList) => void;
|
|
8
|
-
}): boolean;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
function carriesFiles(event) {
|
|
3
|
-
return event.dataTransfer?.types?.includes("Files") === true;
|
|
4
|
-
}
|
|
5
|
-
export function useComposerDrop({ scope, rootRef, target, onFiles, }) {
|
|
6
|
-
const [dragging, setDragging] = useState(false);
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
const element = scope === "document"
|
|
9
|
-
? document
|
|
10
|
-
: scope === "panel"
|
|
11
|
-
? target ??
|
|
12
|
-
rootRef.current?.closest("[data-sb-message-panel]") ??
|
|
13
|
-
rootRef.current
|
|
14
|
-
: rootRef.current;
|
|
15
|
-
if (!element)
|
|
16
|
-
return;
|
|
17
|
-
let depth = 0;
|
|
18
|
-
const over = (event) => {
|
|
19
|
-
const dragEvent = event;
|
|
20
|
-
if (carriesFiles(dragEvent))
|
|
21
|
-
dragEvent.preventDefault();
|
|
22
|
-
};
|
|
23
|
-
const enter = (event) => {
|
|
24
|
-
const dragEvent = event;
|
|
25
|
-
if (!carriesFiles(dragEvent))
|
|
26
|
-
return;
|
|
27
|
-
dragEvent.preventDefault();
|
|
28
|
-
depth += 1;
|
|
29
|
-
setDragging(true);
|
|
30
|
-
};
|
|
31
|
-
const leave = (event) => {
|
|
32
|
-
const dragEvent = event;
|
|
33
|
-
if (!carriesFiles(dragEvent))
|
|
34
|
-
return;
|
|
35
|
-
depth = Math.max(0, depth - 1);
|
|
36
|
-
if (depth === 0)
|
|
37
|
-
setDragging(false);
|
|
38
|
-
};
|
|
39
|
-
const drop = (event) => {
|
|
40
|
-
const dragEvent = event;
|
|
41
|
-
if (!carriesFiles(dragEvent))
|
|
42
|
-
return;
|
|
43
|
-
dragEvent.preventDefault();
|
|
44
|
-
depth = 0;
|
|
45
|
-
setDragging(false);
|
|
46
|
-
if (dragEvent.dataTransfer?.files.length)
|
|
47
|
-
onFiles(dragEvent.dataTransfer.files);
|
|
48
|
-
};
|
|
49
|
-
const cancel = () => {
|
|
50
|
-
depth = 0;
|
|
51
|
-
setDragging(false);
|
|
52
|
-
};
|
|
53
|
-
element.addEventListener("dragover", over);
|
|
54
|
-
element.addEventListener("dragenter", enter);
|
|
55
|
-
element.addEventListener("dragleave", leave);
|
|
56
|
-
element.addEventListener("drop", drop);
|
|
57
|
-
window.addEventListener("dragend", cancel);
|
|
58
|
-
window.addEventListener("blur", cancel);
|
|
59
|
-
return () => {
|
|
60
|
-
element.removeEventListener("dragover", over);
|
|
61
|
-
element.removeEventListener("dragenter", enter);
|
|
62
|
-
element.removeEventListener("dragleave", leave);
|
|
63
|
-
element.removeEventListener("drop", drop);
|
|
64
|
-
window.removeEventListener("dragend", cancel);
|
|
65
|
-
window.removeEventListener("blur", cancel);
|
|
66
|
-
};
|
|
67
|
-
}, [scope, target, rootRef, onFiles]);
|
|
68
|
-
return dragging;
|
|
69
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { HTMLAttributes, ReactNode, RefObject, TextareaHTMLAttributes } from "react";
|
|
2
|
-
import type { MessagePanelStatus } from "../contracts";
|
|
3
|
-
export type ComposerDropScope = "composer" | "panel" | "document";
|
|
4
|
-
export type AttachmentStatus = "uploading" | "ready" | "error";
|
|
5
|
-
export interface ComposerAttachmentView {
|
|
6
|
-
id: string;
|
|
7
|
-
name: string;
|
|
8
|
-
mediaType?: string;
|
|
9
|
-
size?: number;
|
|
10
|
-
url?: string;
|
|
11
|
-
status: AttachmentStatus;
|
|
12
|
-
note?: string;
|
|
13
|
-
}
|
|
14
|
-
export interface ComposerMessage<TAttachment> {
|
|
15
|
-
text: string;
|
|
16
|
-
attachments: readonly TAttachment[];
|
|
17
|
-
}
|
|
18
|
-
export interface ComposerCommand {
|
|
19
|
-
name: string;
|
|
20
|
-
description: string;
|
|
21
|
-
prompt: string;
|
|
22
|
-
icon?: "plan" | "research" | "file" | "time" | "tool" | "mcp";
|
|
23
|
-
}
|
|
24
|
-
export interface ComposerToolbarAction {
|
|
25
|
-
id: string;
|
|
26
|
-
label: string;
|
|
27
|
-
icon?: "sparkles" | "plus";
|
|
28
|
-
disabled?: boolean;
|
|
29
|
-
onSelect: () => void;
|
|
30
|
-
}
|
|
31
|
-
export interface ComposerProps<TAttachment> extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "onSubmit"> {
|
|
32
|
-
value?: string;
|
|
33
|
-
defaultValue?: string;
|
|
34
|
-
onValueChange?: (value: string) => void;
|
|
35
|
-
status: MessagePanelStatus;
|
|
36
|
-
attachments?: readonly TAttachment[];
|
|
37
|
-
getAttachment?: (attachment: TAttachment, index: number) => ComposerAttachmentView;
|
|
38
|
-
dropScope?: ComposerDropScope;
|
|
39
|
-
dropTarget?: HTMLElement | null;
|
|
40
|
-
accept?: string;
|
|
41
|
-
multiple?: boolean;
|
|
42
|
-
maxFiles?: number;
|
|
43
|
-
maxFileSize?: number;
|
|
44
|
-
onFilesAdded?: (files: readonly File[]) => void | Promise<void>;
|
|
45
|
-
onAttachmentRemove?: (attachment: TAttachment) => void;
|
|
46
|
-
onAttachmentRetry?: (attachment: TAttachment) => void | Promise<void>;
|
|
47
|
-
onAttachmentReorder?: (fromIndex: number, toIndex: number) => void;
|
|
48
|
-
onValidationError?: (message: string, files: readonly File[]) => void;
|
|
49
|
-
onSubmit: (message: ComposerMessage<TAttachment>) => void | Promise<void>;
|
|
50
|
-
onStop?: () => void | Promise<void>;
|
|
51
|
-
placeholder?: string;
|
|
52
|
-
tools?: ReactNode;
|
|
53
|
-
textareaProps?: Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "value" | "defaultValue" | "onChange" | "onSubmit">;
|
|
54
|
-
textareaRef?: RefObject<HTMLTextAreaElement | null>;
|
|
55
|
-
}
|
|
56
|
-
export interface ChatComposerProps<TAttachment> extends ComposerProps<TAttachment> {
|
|
57
|
-
commands?: readonly ComposerCommand[];
|
|
58
|
-
toolbarActions?: readonly ComposerToolbarAction[];
|
|
59
|
-
variant?: "docked" | "standalone";
|
|
60
|
-
commandMenuLabel?: string;
|
|
61
|
-
onCommandPick?: (command: ComposerCommand) => void;
|
|
62
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/src/contracts.d.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { ComponentType, HTMLAttributes, ReactNode } from "react";
|
|
2
|
-
export type MessagePanelStatus = "ready" | "submitted" | "streaming" | "error";
|
|
3
|
-
export type MessageRole = "user" | "assistant" | "system" | "tool";
|
|
4
|
-
export interface ToolApprovalView {
|
|
5
|
-
id: string;
|
|
6
|
-
approved?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export type ToolApprovalDecision = "deny" | "allow_once" | "allow_level";
|
|
9
|
-
export interface MessageAdapter<TMessage> {
|
|
10
|
-
getId: (message: TMessage, index: number) => string;
|
|
11
|
-
getRole: (message: TMessage) => MessageRole;
|
|
12
|
-
getParts: (message: TMessage) => readonly unknown[];
|
|
13
|
-
getMetadata?: (message: TMessage) => unknown;
|
|
14
|
-
getText?: (message: TMessage) => string;
|
|
15
|
-
isPartRenderable?: (part: unknown) => boolean;
|
|
16
|
-
/** Extracts provider-specific tool approval state without leaking it into UI code. */
|
|
17
|
-
getToolApproval?: (part: unknown) => ToolApprovalView | undefined;
|
|
18
|
-
/**
|
|
19
|
-
* Marks snapshot-like parts that supersede older parts with the same key.
|
|
20
|
-
* `update_plan` uses the built-in "plan" key when this is omitted.
|
|
21
|
-
*/
|
|
22
|
-
getPartPresentationKey?: (part: unknown) => string | undefined;
|
|
23
|
-
}
|
|
24
|
-
export interface MessagePanelActions {
|
|
25
|
-
onCopy?: (text: string) => void | Promise<void>;
|
|
26
|
-
onRetry?: (messageId: string) => void | Promise<void>;
|
|
27
|
-
onFork?: (messageId: string) => void | Promise<void>;
|
|
28
|
-
onNavigate?: (target: string) => void;
|
|
29
|
-
onPreview?: (artifact: unknown) => void;
|
|
30
|
-
onDownload?: (artifact: unknown) => void;
|
|
31
|
-
onAnswer?: (payload: unknown) => void | Promise<void>;
|
|
32
|
-
onApprove?: (approvalId: string, decision: ToolApprovalDecision) => void | Promise<void>;
|
|
33
|
-
onSuggestion?: (text: string) => void | Promise<void>;
|
|
34
|
-
}
|
|
35
|
-
export interface PartRendererContext {
|
|
36
|
-
messageId: string;
|
|
37
|
-
partIndex: number;
|
|
38
|
-
activityId: string;
|
|
39
|
-
isActive: boolean;
|
|
40
|
-
isLatestOfKind: boolean;
|
|
41
|
-
/** First user message after the assistant message that owns this part. */
|
|
42
|
-
replyText?: string;
|
|
43
|
-
approval?: ToolApprovalView;
|
|
44
|
-
status: MessagePanelStatus;
|
|
45
|
-
actions: MessagePanelActions;
|
|
46
|
-
}
|
|
47
|
-
export interface PartRendererProps<TPart = unknown> {
|
|
48
|
-
part: TPart;
|
|
49
|
-
context: PartRendererContext;
|
|
50
|
-
}
|
|
51
|
-
export type PartRendererComponent<TPart = unknown> = ComponentType<PartRendererProps<TPart>>;
|
|
52
|
-
export interface PartRendererDefinition {
|
|
53
|
-
name: string;
|
|
54
|
-
match: (part: unknown) => boolean;
|
|
55
|
-
component: PartRendererComponent;
|
|
56
|
-
}
|
|
57
|
-
export interface PartRendererRegistry {
|
|
58
|
-
resolve: (part: unknown) => PartRendererComponent | undefined;
|
|
59
|
-
names: readonly string[];
|
|
60
|
-
}
|
|
61
|
-
export interface MessagePanelSlots {
|
|
62
|
-
assistantHeader?: ReactNode | ((messageId: string) => ReactNode);
|
|
63
|
-
empty?: ReactNode;
|
|
64
|
-
hydrating?: ReactNode;
|
|
65
|
-
recovering?: ReactNode;
|
|
66
|
-
background?: ReactNode;
|
|
67
|
-
error?: ReactNode | ((error?: unknown) => ReactNode);
|
|
68
|
-
beforeMessages?: ReactNode;
|
|
69
|
-
afterMessages?: ReactNode;
|
|
70
|
-
}
|
|
71
|
-
export interface MessagePanelProps<TMessage> extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
|
|
72
|
-
messages: readonly TMessage[];
|
|
73
|
-
status: MessagePanelStatus;
|
|
74
|
-
isHydrating?: boolean;
|
|
75
|
-
isRecovering?: boolean;
|
|
76
|
-
isServerStreaming?: boolean;
|
|
77
|
-
error?: unknown;
|
|
78
|
-
adapter: MessageAdapter<TMessage>;
|
|
79
|
-
renderers?: PartRendererRegistry;
|
|
80
|
-
actions?: MessagePanelActions;
|
|
81
|
-
slots?: MessagePanelSlots;
|
|
82
|
-
emptyTitle?: string;
|
|
83
|
-
emptyDescription?: string;
|
|
84
|
-
}
|
|
85
|
-
export type RichPartKind = "text" | "reasoning" | "tool" | "error" | "image" | "chart" | "agent_card" | "plan" | "schedule" | "parallel" | "subagents" | "confirmation" | "file" | "dashboard" | "ask_user" | "suggestions";
|
|
86
|
-
export type MessageToolState = "input-streaming" | "input-available" | "approval-requested" | "approval-responded" | "output-available" | "output-denied" | "output-error";
|
|
87
|
-
export interface AIToolPart {
|
|
88
|
-
type: string;
|
|
89
|
-
state: MessageToolState;
|
|
90
|
-
input?: unknown;
|
|
91
|
-
output?: unknown;
|
|
92
|
-
errorText?: string;
|
|
93
|
-
toolName?: string;
|
|
94
|
-
}
|
package/dist/src/contracts.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { type ComponentProps, type HTMLAttributes } from "react";
|
|
2
|
-
import { StickToBottom } from "use-stick-to-bottom";
|
|
3
|
-
export type ConversationProps = ComponentProps<typeof StickToBottom>;
|
|
4
|
-
export declare const Conversation: ({ className, ...props }: ConversationProps) => import("react").JSX.Element;
|
|
5
|
-
export type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;
|
|
6
|
-
export declare const ConversationContent: ({ className, ...props }: ConversationContentProps) => import("react").JSX.Element;
|
|
7
|
-
export interface ConversationEmptyStateProps extends HTMLAttributes<HTMLDivElement> {
|
|
8
|
-
title?: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
icon?: React.ReactNode;
|
|
11
|
-
}
|
|
12
|
-
export declare const ConversationEmptyState: import("react").ForwardRefExoticComponent<ConversationEmptyStateProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
13
|
-
export declare function ConversationScrollButton({ className, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>): import("react").JSX.Element | null;
|
package/dist/src/conversation.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ArrowDownIcon } from "lucide-react";
|
|
3
|
-
import { forwardRef, useCallback } from "react";
|
|
4
|
-
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
5
|
-
import { cn } from "./internal/cn";
|
|
6
|
-
export const Conversation = ({ className, ...props }) => (_jsx(StickToBottom, { className: cn("sb-conversation", className), initial: "instant", resize: "instant", role: "log", ...props }));
|
|
7
|
-
export const ConversationContent = ({ className, ...props }) => (_jsx(StickToBottom.Content, { className: cn("sb-conversation__content", className), ...props }));
|
|
8
|
-
export const ConversationEmptyState = forwardRef(({ className, title = "开始对话", description, icon, children, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("sb-conversation__empty", className), ...props, children: children ?? (_jsxs(_Fragment, { children: [icon, _jsx("strong", { children: title }), description && _jsx("p", { children: description })] })) })));
|
|
9
|
-
ConversationEmptyState.displayName = "ConversationEmptyState";
|
|
10
|
-
export function ConversationScrollButton({ className, ...props }) {
|
|
11
|
-
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
12
|
-
const handleClick = useCallback(() => {
|
|
13
|
-
void scrollToBottom({ animation: "smooth" });
|
|
14
|
-
}, [scrollToBottom]);
|
|
15
|
-
if (isAtBottom)
|
|
16
|
-
return null;
|
|
17
|
-
return (_jsx("button", { type: "button", className: cn("sb-conversation__scroll", className), onClick: handleClick, "aria-label": "\u56DE\u5230\u6700\u65B0\u6D88\u606F", ...props, children: _jsx(ArrowDownIcon, { size: 16 }) }));
|
|
18
|
-
}
|
package/dist/src/index.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export * from "./adapters/ai-sdk";
|
|
2
|
-
export * from "./camel";
|
|
3
|
-
export * from "./chat-summary-panel";
|
|
4
|
-
export * from "./composer";
|
|
5
|
-
export * from "./contracts";
|
|
6
|
-
export * from "./conversation";
|
|
7
|
-
export * from "./message";
|
|
8
|
-
export * from "./message-panel";
|
|
9
|
-
export * from "./parts";
|
|
10
|
-
export * from "./parts/primitives";
|
|
11
|
-
export * from "./registry";
|
|
12
|
-
export * from "./status";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function cn(...values: Array<string | false | null | undefined>): string;
|
package/dist/src/internal/cn.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, useMemo, } from "react";
|
|
3
|
-
import { Conversation, ConversationContent, ConversationEmptyState, ConversationScrollButton, } from "./conversation";
|
|
4
|
-
import { cn } from "./internal/cn";
|
|
5
|
-
import { AssistantMessageActions, BrandMark, Message, MessageContent, } from "./message";
|
|
6
|
-
import { DefaultPartRenderer, resolvePartKind, stripNoticePrefix, } from "./parts";
|
|
7
|
-
import { PanelStatus, ThinkingIndicator, TopLoadingBar } from "./status";
|
|
8
|
-
function roleOf(adapter, message) {
|
|
9
|
-
return adapter.getRole(message);
|
|
10
|
-
}
|
|
11
|
-
function messageText(adapter, message) {
|
|
12
|
-
if (adapter.getText)
|
|
13
|
-
return adapter.getText(message);
|
|
14
|
-
return adapter
|
|
15
|
-
.getParts(message)
|
|
16
|
-
.map((part) => {
|
|
17
|
-
if (part != null &&
|
|
18
|
-
typeof part === "object" &&
|
|
19
|
-
part.type === "text") {
|
|
20
|
-
return String(part.text ?? "");
|
|
21
|
-
}
|
|
22
|
-
return "";
|
|
23
|
-
})
|
|
24
|
-
.filter(Boolean)
|
|
25
|
-
.join("\n");
|
|
26
|
-
}
|
|
27
|
-
function isNotice(metadata) {
|
|
28
|
-
if (metadata == null || typeof metadata !== "object")
|
|
29
|
-
return false;
|
|
30
|
-
const kind = metadata.deliveryKind;
|
|
31
|
-
return kind === "notice" || kind === "interim";
|
|
32
|
-
}
|
|
33
|
-
function slotValue(slot, messageId) {
|
|
34
|
-
return typeof slot === "function" ? slot(messageId) : slot;
|
|
35
|
-
}
|
|
36
|
-
function presentationKey(adapter, part) {
|
|
37
|
-
const customKey = adapter.getPartPresentationKey?.(part);
|
|
38
|
-
if (customKey)
|
|
39
|
-
return customKey;
|
|
40
|
-
return resolvePartKind(part) === "plan" ? "plan" : undefined;
|
|
41
|
-
}
|
|
42
|
-
function MessageRow({ message, messageId, isLastAssistant, isActive, adapter, renderers, actions, slots, status, latestActivityByKind, replyText, }) {
|
|
43
|
-
const role = roleOf(adapter, message);
|
|
44
|
-
const parts = adapter.getParts(message);
|
|
45
|
-
const visibleParts = adapter.isPartRenderable
|
|
46
|
-
? parts.filter(adapter.isPartRenderable)
|
|
47
|
-
: parts;
|
|
48
|
-
const metadata = adapter.getMetadata?.(message);
|
|
49
|
-
const text = messageText(adapter, message);
|
|
50
|
-
if (isNotice(metadata)) {
|
|
51
|
-
return (_jsx(Message, { from: "assistant", "data-message-id": messageId, children: _jsx("div", { className: "sb-notice-line", children: stripNoticePrefix(text) }) }));
|
|
52
|
-
}
|
|
53
|
-
if (role === "user") {
|
|
54
|
-
return (_jsx(Message, { from: "user", "data-message-id": messageId, "data-message-role": "user", children: _jsx(MessageContent, { children: parts.map((part, partIndex) => {
|
|
55
|
-
const Renderer = renderers?.resolve(part) ?? DefaultPartRenderer;
|
|
56
|
-
const activityId = `${messageId}:${partIndex}`;
|
|
57
|
-
const key = presentationKey(adapter, part);
|
|
58
|
-
return (_jsx(Renderer, { part: part, context: {
|
|
59
|
-
messageId,
|
|
60
|
-
partIndex,
|
|
61
|
-
activityId,
|
|
62
|
-
isActive: false,
|
|
63
|
-
isLatestOfKind: key === undefined ||
|
|
64
|
-
latestActivityByKind.get(key) === activityId,
|
|
65
|
-
approval: adapter.getToolApproval?.(part),
|
|
66
|
-
status,
|
|
67
|
-
actions,
|
|
68
|
-
} }, activityId));
|
|
69
|
-
}) }) }));
|
|
70
|
-
}
|
|
71
|
-
return (_jsxs(Message, { from: "assistant", "data-message-id": messageId, "data-message-role": "assistant", "data-active": isActive ? "true" : undefined, children: [slots.assistantHeader && (_jsx("div", { className: "sb-assistant-header", children: slotValue(slots.assistantHeader, messageId) })), _jsxs(MessageContent, { children: [parts.map((part, partIndex) => {
|
|
72
|
-
const Renderer = renderers?.resolve(part) ?? DefaultPartRenderer;
|
|
73
|
-
const activityId = `${messageId}:${partIndex}`;
|
|
74
|
-
const key = presentationKey(adapter, part);
|
|
75
|
-
return (_jsx(Renderer, { part: part, context: {
|
|
76
|
-
messageId,
|
|
77
|
-
partIndex,
|
|
78
|
-
activityId,
|
|
79
|
-
isActive,
|
|
80
|
-
isLatestOfKind: key === undefined ||
|
|
81
|
-
latestActivityByKind.get(key) === activityId,
|
|
82
|
-
replyText,
|
|
83
|
-
approval: adapter.getToolApproval?.(part),
|
|
84
|
-
status,
|
|
85
|
-
actions,
|
|
86
|
-
} }, activityId));
|
|
87
|
-
}), isActive && visibleParts.length === 0 && _jsx(ThinkingIndicator, {})] }), !isActive && (_jsx(AssistantMessageActions, { text: text, messageId: messageId, canRetry: isLastAssistant && status !== "streaming", onCopy: actions.onCopy, onRetry: actions.onRetry, onFork: actions.onFork }))] }));
|
|
88
|
-
}
|
|
89
|
-
function MessagePanelInner({ messages, status, isHydrating = false, isRecovering = false, isServerStreaming = false, error, adapter, renderers, actions = {}, slots = {}, className, emptyTitle = "开始对话", emptyDescription = "发送消息,Agent 会在这里展示思考、工具和产出。", ...props }, ref) {
|
|
90
|
-
const ids = useMemo(() => messages.map((message, index) => adapter.getId(message, index)), [messages, adapter]);
|
|
91
|
-
const lastAssistantIndex = useMemo(() => {
|
|
92
|
-
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
93
|
-
if (roleOf(adapter, messages[index]) === "assistant")
|
|
94
|
-
return index;
|
|
95
|
-
}
|
|
96
|
-
return -1;
|
|
97
|
-
}, [messages, adapter]);
|
|
98
|
-
const lastRole = messages.length > 0 ? roleOf(adapter, messages[messages.length - 1]) : undefined;
|
|
99
|
-
const showPending = status === "submitted" &&
|
|
100
|
-
(lastRole === "user" || messages.length === 0);
|
|
101
|
-
const latestActivityByKind = useMemo(() => {
|
|
102
|
-
const latest = new Map();
|
|
103
|
-
messages.forEach((message, messageIndex) => {
|
|
104
|
-
if (roleOf(adapter, message) !== "assistant")
|
|
105
|
-
return;
|
|
106
|
-
adapter.getParts(message).forEach((part, partIndex) => {
|
|
107
|
-
const key = presentationKey(adapter, part);
|
|
108
|
-
if (key)
|
|
109
|
-
latest.set(key, `${ids[messageIndex]}:${partIndex}`);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
return latest;
|
|
113
|
-
}, [adapter, ids, messages]);
|
|
114
|
-
const replyTextByMessageId = useMemo(() => {
|
|
115
|
-
const replies = new Map();
|
|
116
|
-
messages.forEach((message, messageIndex) => {
|
|
117
|
-
if (roleOf(adapter, message) !== "assistant")
|
|
118
|
-
return;
|
|
119
|
-
for (let replyIndex = messageIndex + 1; replyIndex < messages.length; replyIndex += 1) {
|
|
120
|
-
const reply = messages[replyIndex];
|
|
121
|
-
if (roleOf(adapter, reply) !== "user")
|
|
122
|
-
continue;
|
|
123
|
-
const text = messageText(adapter, reply).trim();
|
|
124
|
-
if (text)
|
|
125
|
-
replies.set(ids[messageIndex], text);
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
return replies;
|
|
130
|
-
}, [adapter, ids, messages]);
|
|
131
|
-
return (_jsxs("div", { ref: ref, className: cn("sb-message-panel", className), "data-sb-message-panel": "", "data-status": status, "data-hydrating": isHydrating ? "true" : undefined, "aria-busy": status === "submitted" || status === "streaming" || isHydrating, ...props, children: [_jsx(TopLoadingBar, { status: status }), isRecovering &&
|
|
132
|
-
(slots.recovering ?? (_jsx(PanelStatus, { kind: "recovering", children: "\u6B63\u5728\u6062\u590D\u5BF9\u8BDD\u6D41\u2026" }))), !isRecovering &&
|
|
133
|
-
isServerStreaming &&
|
|
134
|
-
(slots.background ?? (_jsx(PanelStatus, { kind: "background", children: "Agent \u6B63\u5728\u540E\u53F0\u66F4\u65B0\u2026" }))), status === "error" &&
|
|
135
|
-
(typeof slots.error === "function"
|
|
136
|
-
? slots.error(error)
|
|
137
|
-
: slots.error ?? (_jsx(PanelStatus, { kind: "error", children: error instanceof Error ? error.message : "这一轮出错了,请重试。" }))), _jsxs(Conversation, { children: [_jsxs(ConversationContent, { children: [slots.beforeMessages, messages.length === 0 &&
|
|
138
|
-
!isHydrating &&
|
|
139
|
-
(slots.empty ?? (_jsx(ConversationEmptyState, { title: emptyTitle, description: emptyDescription, icon: _jsx(BrandMark, { className: "sb-brand-mark--large" }) }))), messages.map((message, index) => (_jsx(MessageRow, { message: message, index: index, messageId: ids[index], isLastAssistant: index === lastAssistantIndex, isActive: status === "streaming" &&
|
|
140
|
-
index === messages.length - 1 &&
|
|
141
|
-
roleOf(adapter, message) === "assistant", adapter: adapter, renderers: renderers, actions: actions, slots: slots, status: status, latestActivityByKind: latestActivityByKind, replyText: replyTextByMessageId.get(ids[index]) }, ids[index]))), showPending && (_jsxs(Message, { from: "assistant", "data-message-role": "assistant", "data-pending": "true", children: [slots.assistantHeader && (_jsx("div", { className: "sb-assistant-header", children: slotValue(slots.assistantHeader, "pending") })), _jsx(ThinkingIndicator, {})] })), slots.afterMessages] }), isHydrating &&
|
|
142
|
-
(slots.hydrating ?? (_jsx("div", { className: "sb-hydrating-overlay", children: _jsx(PanelStatus, { kind: "hydrating", children: "\u6B63\u5728\u6062\u590D\u5386\u53F2\u6D88\u606F\u2026" }) }))), _jsx(ConversationScrollButton, {})] })] }));
|
|
143
|
-
}
|
|
144
|
-
export const MessagePanel = forwardRef(MessagePanelInner);
|