@fromsko/chat-ui 0.0.0-stage → 0.1.6
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/LICENSE +21 -0
- package/README.md +100 -2
- package/lib/adapters/event-bus.d.ts +80 -0
- package/lib/adapters/event-bus.js +73 -0
- package/lib/adapters/index.d.ts +3 -0
- package/lib/adapters/index.js +3 -0
- package/lib/adapters/labels.d.ts +6 -0
- package/lib/adapters/labels.js +17 -0
- package/lib/adapters/stream-bridge.d.ts +9 -0
- package/lib/adapters/stream-bridge.js +7 -0
- package/lib/blocks/cards/AskCard.d.ts +8 -0
- package/lib/blocks/cards/AskCard.js +97 -0
- package/lib/blocks/cards/CompactionCard.d.ts +5 -0
- package/lib/blocks/cards/CompactionCard.js +5 -0
- package/lib/blocks/cards/ContextUsage.d.ts +14 -0
- package/lib/blocks/cards/ContextUsage.js +136 -0
- package/lib/blocks/cards/DisplayCard.d.ts +7 -0
- package/lib/blocks/cards/DisplayCard.js +27 -0
- package/lib/blocks/cards/MemoryPill.d.ts +5 -0
- package/lib/blocks/cards/MemoryPill.js +5 -0
- package/lib/blocks/cards/OutputCard.d.ts +5 -0
- package/lib/blocks/cards/OutputCard.js +13 -0
- package/lib/blocks/cards/PermissionCard.d.ts +21 -0
- package/lib/blocks/cards/PermissionCard.js +26 -0
- package/lib/blocks/cards/SpringCheck.d.ts +9 -0
- package/lib/blocks/cards/SpringCheck.js +17 -0
- package/lib/blocks/cards/TodoCard.d.ts +19 -0
- package/lib/blocks/cards/TodoCard.js +28 -0
- package/lib/blocks/cards/index.d.ts +10 -0
- package/lib/blocks/cards/index.js +10 -0
- package/lib/blocks/composer/Composer.d.ts +66 -0
- package/lib/blocks/composer/Composer.js +73 -0
- package/lib/blocks/composer/ComposerAttachments.d.ts +17 -0
- package/lib/blocks/composer/ComposerAttachments.js +10 -0
- package/lib/blocks/composer/ComposerGrip.d.ts +1 -0
- package/lib/blocks/composer/ComposerGrip.js +39 -0
- package/lib/blocks/composer/ComposerInput.d.ts +18 -0
- package/lib/blocks/composer/ComposerInput.js +60 -0
- package/lib/blocks/composer/ComposerMenus.d.ts +10 -0
- package/lib/blocks/composer/ComposerMenus.js +78 -0
- package/lib/blocks/composer/ComposerToolbar.d.ts +27 -0
- package/lib/blocks/composer/ComposerToolbar.js +16 -0
- package/lib/blocks/composer/MentionPicker.d.ts +13 -0
- package/lib/blocks/composer/MentionPicker.js +71 -0
- package/lib/blocks/composer/PromptPop.d.ts +7 -0
- package/lib/blocks/composer/PromptPop.js +40 -0
- package/lib/blocks/composer/QueuedPrompts.d.ts +14 -0
- package/lib/blocks/composer/QueuedPrompts.js +21 -0
- package/lib/blocks/composer/SlingButton.d.ts +10 -0
- package/lib/blocks/composer/SlingButton.js +112 -0
- package/lib/blocks/composer/index.d.ts +7 -0
- package/lib/blocks/composer/index.js +1 -0
- package/lib/blocks/conversation/ChatEmptyScene.d.ts +4 -0
- package/lib/blocks/conversation/ChatEmptyScene.js +21 -0
- package/lib/blocks/conversation/ChatHeader.d.ts +13 -0
- package/lib/blocks/conversation/ChatHeader.js +20 -0
- package/lib/blocks/conversation/ChatPane.d.ts +90 -0
- package/lib/blocks/conversation/ChatPane.js +60 -0
- package/lib/blocks/conversation/Conversation.d.ts +11 -0
- package/lib/blocks/conversation/Conversation.js +41 -0
- package/lib/blocks/conversation/LineSidebar.d.ts +14 -0
- package/lib/blocks/conversation/LineSidebar.js +42 -0
- package/lib/blocks/conversation/index.d.ts +5 -0
- package/lib/blocks/conversation/index.js +5 -0
- package/lib/blocks/index.d.ts +9 -0
- package/lib/blocks/index.js +9 -0
- package/lib/blocks/message/DayDivider.d.ts +10 -0
- package/lib/blocks/message/DayDivider.js +29 -0
- package/lib/blocks/message/DiffView.d.ts +5 -0
- package/lib/blocks/message/DiffView.js +18 -0
- package/lib/blocks/message/MermaidDiagram.d.ts +4 -0
- package/lib/blocks/message/MermaidDiagram.js +43 -0
- package/lib/blocks/message/Message.d.ts +17 -0
- package/lib/blocks/message/Message.js +45 -0
- package/lib/blocks/message/MessageActionBar.d.ts +18 -0
- package/lib/blocks/message/MessageActionBar.js +15 -0
- package/lib/blocks/message/MessageArtifacts.d.ts +13 -0
- package/lib/blocks/message/MessageArtifacts.js +9 -0
- package/lib/blocks/message/MessageAttachments.d.ts +5 -0
- package/lib/blocks/message/MessageAttachments.js +28 -0
- package/lib/blocks/message/MessageBubble.d.ts +11 -0
- package/lib/blocks/message/MessageBubble.js +31 -0
- package/lib/blocks/message/MessageRow.d.ts +20 -0
- package/lib/blocks/message/MessageRow.js +49 -0
- package/lib/blocks/message/MessageStream.d.ts +23 -0
- package/lib/blocks/message/MessageStream.js +80 -0
- package/lib/blocks/message/MessageTrace.d.ts +16 -0
- package/lib/blocks/message/MessageTrace.js +39 -0
- package/lib/blocks/message/PartView.d.ts +10 -0
- package/lib/blocks/message/PartView.js +29 -0
- package/lib/blocks/message/code-text.d.ts +15 -0
- package/lib/blocks/message/code-text.js +17 -0
- package/lib/blocks/message/index.d.ts +12 -0
- package/lib/blocks/message/index.js +12 -0
- package/lib/blocks/reasoning/Reasoning.d.ts +7 -0
- package/lib/blocks/reasoning/Reasoning.js +26 -0
- package/lib/blocks/reasoning/ReasoningBlock.d.ts +6 -0
- package/lib/blocks/reasoning/ReasoningBlock.js +5 -0
- package/lib/blocks/reasoning/index.d.ts +2 -0
- package/lib/blocks/reasoning/index.js +2 -0
- package/lib/blocks/selection/SelectionActions.d.ts +12 -0
- package/lib/blocks/selection/SelectionActions.js +29 -0
- package/lib/blocks/selection/SelectionBar.d.ts +10 -0
- package/lib/blocks/selection/SelectionBar.js +6 -0
- package/lib/blocks/selection/index.d.ts +2 -0
- package/lib/blocks/selection/index.js +2 -0
- package/lib/blocks/settings/DisplaySettingsPanel.d.ts +5 -0
- package/lib/blocks/settings/DisplaySettingsPanel.js +33 -0
- package/lib/blocks/settings/SquishSwitch.d.ts +8 -0
- package/lib/blocks/settings/SquishSwitch.js +21 -0
- package/lib/blocks/settings/index.d.ts +2 -0
- package/lib/blocks/settings/index.js +2 -0
- package/lib/blocks/status/LatticeLoader.d.ts +12 -0
- package/lib/blocks/status/LatticeLoader.js +22 -0
- package/lib/blocks/status/TypingIndicator.d.ts +5 -0
- package/lib/blocks/status/TypingIndicator.js +5 -0
- package/lib/blocks/status/index.d.ts +2 -0
- package/lib/blocks/status/index.js +2 -0
- package/lib/blocks/tool/SandboxCard.d.ts +5 -0
- package/lib/blocks/tool/SandboxCard.js +10 -0
- package/lib/blocks/tool/ToolCard.d.ts +10 -0
- package/lib/blocks/tool/ToolCard.js +32 -0
- package/lib/blocks/tool/ToolGroup.d.ts +6 -0
- package/lib/blocks/tool/ToolGroup.js +23 -0
- package/lib/blocks/tool/exec-envelope.d.ts +7 -0
- package/lib/blocks/tool/exec-envelope.js +43 -0
- package/lib/blocks/tool/index.d.ts +4 -0
- package/lib/blocks/tool/index.js +4 -0
- package/lib/domain/ask.d.ts +14 -0
- package/lib/domain/ask.js +50 -0
- package/lib/domain/attachments.d.ts +16 -0
- package/lib/domain/attachments.js +126 -0
- package/lib/domain/attribution.d.ts +4 -0
- package/lib/domain/attribution.js +11 -0
- package/lib/domain/composer-palette.d.ts +15 -0
- package/lib/domain/composer-palette.js +131 -0
- package/lib/domain/index.d.ts +12 -0
- package/lib/domain/index.js +12 -0
- package/lib/domain/mention.d.ts +17 -0
- package/lib/domain/mention.js +95 -0
- package/lib/domain/message-blocks.d.ts +29 -0
- package/lib/domain/message-blocks.js +63 -0
- package/lib/domain/message-menu.d.ts +4 -0
- package/lib/domain/message-menu.js +46 -0
- package/lib/domain/queue.d.ts +23 -0
- package/lib/domain/queue.js +52 -0
- package/lib/domain/reducer.d.ts +137 -0
- package/lib/domain/reducer.js +442 -0
- package/lib/domain/selection-actions.d.ts +13 -0
- package/lib/domain/selection-actions.js +56 -0
- package/lib/domain/selection-gates.d.ts +6 -0
- package/lib/domain/selection-gates.js +36 -0
- package/lib/domain/slash-commands.d.ts +16 -0
- package/lib/domain/slash-commands.js +142 -0
- package/lib/hooks/index.d.ts +8 -0
- package/lib/hooks/index.js +8 -0
- package/lib/hooks/useComposerPalette.d.ts +35 -0
- package/lib/hooks/useComposerPalette.js +120 -0
- package/lib/hooks/useComposerSubmit.d.ts +31 -0
- package/lib/hooks/useComposerSubmit.js +135 -0
- package/lib/hooks/useInputHistory.d.ts +14 -0
- package/lib/hooks/useInputHistory.js +52 -0
- package/lib/hooks/usePendingAsk.d.ts +11 -0
- package/lib/hooks/usePendingAsk.js +33 -0
- package/lib/hooks/usePendingPermissions.d.ts +10 -0
- package/lib/hooks/usePendingPermissions.js +26 -0
- package/lib/hooks/useQueuedPrompts.d.ts +12 -0
- package/lib/hooks/useQueuedPrompts.js +91 -0
- package/lib/hooks/useSelectionPop.d.ts +13 -0
- package/lib/hooks/useSelectionPop.js +130 -0
- package/lib/hooks/useStreamBridge.d.ts +1 -0
- package/lib/hooks/useStreamBridge.js +23 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +10 -0
- package/lib/primitives/Badge.d.ts +7 -0
- package/lib/primitives/Badge.js +13 -0
- package/lib/primitives/Button.d.ts +9 -0
- package/lib/primitives/Button.js +21 -0
- package/lib/primitives/Collapsible.d.ts +12 -0
- package/lib/primitives/Collapsible.js +14 -0
- package/lib/primitives/Dialog.d.ts +10 -0
- package/lib/primitives/Dialog.js +19 -0
- package/lib/primitives/DropdownMenu.d.ts +28 -0
- package/lib/primitives/DropdownMenu.js +37 -0
- package/lib/primitives/Empty.d.ts +8 -0
- package/lib/primitives/Empty.js +6 -0
- package/lib/primitives/Input.d.ts +3 -0
- package/lib/primitives/Input.js +6 -0
- package/lib/primitives/Kbd.d.ts +2 -0
- package/lib/primitives/Kbd.js +5 -0
- package/lib/primitives/Popover.d.ts +15 -0
- package/lib/primitives/Popover.js +32 -0
- package/lib/primitives/ScrollArea.d.ts +5 -0
- package/lib/primitives/ScrollArea.js +5 -0
- package/lib/primitives/Separator.d.ts +5 -0
- package/lib/primitives/Separator.js +5 -0
- package/lib/primitives/Spinner.d.ts +5 -0
- package/lib/primitives/Spinner.js +5 -0
- package/lib/primitives/Textarea.d.ts +3 -0
- package/lib/primitives/Textarea.js +6 -0
- package/lib/primitives/Tooltip.d.ts +8 -0
- package/lib/primitives/Tooltip.js +7 -0
- package/lib/primitives/index.d.ts +14 -0
- package/lib/primitives/index.js +14 -0
- package/lib/reactbits/Motion.d.ts +24 -0
- package/lib/reactbits/Motion.js +52 -0
- package/lib/reactbits/index.d.ts +1 -0
- package/lib/reactbits/index.js +1 -0
- package/lib/stores/chat-store.d.ts +33 -0
- package/lib/stores/chat-store.js +93 -0
- package/lib/stores/display-store.d.ts +15 -0
- package/lib/stores/display-store.js +116 -0
- package/lib/stores/index.d.ts +3 -0
- package/lib/stores/index.js +3 -0
- package/lib/stores/theme-store.d.ts +8 -0
- package/lib/stores/theme-store.js +29 -0
- package/lib/tokens/index.d.ts +21 -0
- package/lib/tokens/index.js +66 -0
- package/lib/types/agent.d.ts +62 -0
- package/lib/types/agent.js +1 -0
- package/lib/types/callbacks.d.ts +55 -0
- package/lib/types/callbacks.js +1 -0
- package/lib/types/composer.d.ts +116 -0
- package/lib/types/composer.js +1 -0
- package/lib/types/conversation.d.ts +54 -0
- package/lib/types/conversation.js +1 -0
- package/lib/types/display.d.ts +25 -0
- package/lib/types/display.js +40 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.js +8 -0
- package/lib/types/message.d.ts +100 -0
- package/lib/types/message.js +1 -0
- package/lib/types/selection.d.ts +60 -0
- package/lib/types/selection.js +1 -0
- package/lib/types/theme.d.ts +100 -0
- package/lib/types/theme.js +1 -0
- package/lib/utils/clipboard.d.ts +4 -0
- package/lib/utils/clipboard.js +41 -0
- package/lib/utils/cn.d.ts +2 -0
- package/lib/utils/cn.js +14 -0
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.js +3 -0
- package/lib/utils/storage.d.ts +11 -0
- package/lib/utils/storage.js +48 -0
- package/package.json +93 -4
- package/src/styles/blocks.css +2439 -0
- package/src/styles/index.css +3 -0
- package/src/tokens/theme.css +464 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, useCallback, useEffect, useRef, useState, } from "react";
|
|
3
|
+
import { ArrowDown } from "lucide-react";
|
|
4
|
+
import { Button } from "../../primitives/Button";
|
|
5
|
+
import { keyMessageParts } from "../../domain/message-blocks";
|
|
6
|
+
import { PermissionCard } from "../cards/PermissionCard";
|
|
7
|
+
import { DayDivider, dayDividerFor } from "./DayDivider";
|
|
8
|
+
import { MessageRow } from "./MessageRow";
|
|
9
|
+
import { PartView } from "./PartView";
|
|
10
|
+
const NEAR_BOTTOM_PX = 80;
|
|
11
|
+
function SystemRow({ message }) {
|
|
12
|
+
const keyed = keyMessageParts(message);
|
|
13
|
+
if (keyed.length === 0) {
|
|
14
|
+
return (_jsx("div", { className: "ac-stream__divider", "data-cmp": "ac/divider", children: message.text }));
|
|
15
|
+
}
|
|
16
|
+
return (_jsx("div", { className: "ac-notes", children: keyed.map(({ key, part }) => (_jsx(PartView, { part: part }, key))) }));
|
|
17
|
+
}
|
|
18
|
+
export function MessageStream({ messages, members, status, onOpenAgent, permissions = [], onRespondPermission, selectMode = false, selectedIds, onToggleSelect, messageActions, messageArtifacts, }) {
|
|
19
|
+
const viewportRef = useRef(null);
|
|
20
|
+
const countRef = useRef(messages.length);
|
|
21
|
+
const pinnedRef = useRef(true);
|
|
22
|
+
const [atBottom, setAtBottom] = useState(true);
|
|
23
|
+
const agentById = {};
|
|
24
|
+
for (const a of members)
|
|
25
|
+
agentById[a.id] = a;
|
|
26
|
+
const scrollToBottom = useCallback(() => {
|
|
27
|
+
const el = viewportRef.current;
|
|
28
|
+
if (!el)
|
|
29
|
+
return;
|
|
30
|
+
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
|
|
31
|
+
pinnedRef.current = true;
|
|
32
|
+
setAtBottom(true);
|
|
33
|
+
}, []);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (messages.length !== countRef.current) {
|
|
36
|
+
countRef.current = messages.length;
|
|
37
|
+
const el = viewportRef.current;
|
|
38
|
+
if (el)
|
|
39
|
+
el.scrollTop = el.scrollHeight;
|
|
40
|
+
pinnedRef.current = true;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const el = viewportRef.current;
|
|
45
|
+
if (!el)
|
|
46
|
+
return;
|
|
47
|
+
const measure = () => {
|
|
48
|
+
const near = el.scrollHeight - el.scrollTop - el.clientHeight < NEAR_BOTTOM_PX;
|
|
49
|
+
pinnedRef.current = near;
|
|
50
|
+
setAtBottom((prev) => (prev === near ? prev : near));
|
|
51
|
+
};
|
|
52
|
+
measure();
|
|
53
|
+
el.addEventListener("scroll", measure, { passive: true });
|
|
54
|
+
return () => el.removeEventListener("scroll", measure);
|
|
55
|
+
}, []);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const el = viewportRef.current;
|
|
58
|
+
if (!el)
|
|
59
|
+
return;
|
|
60
|
+
const ro = new ResizeObserver(() => {
|
|
61
|
+
if (pinnedRef.current)
|
|
62
|
+
el.scrollTop = el.scrollHeight;
|
|
63
|
+
});
|
|
64
|
+
for (const child of Array.from(el.children))
|
|
65
|
+
ro.observe(child);
|
|
66
|
+
return () => ro.disconnect();
|
|
67
|
+
});
|
|
68
|
+
return (_jsxs("div", { className: "ac-stream-wrap", children: [_jsxs("div", { className: "ac-stream-viewport", children: [_jsx("div", { ref: viewportRef, className: "ac-stream", "data-cmp": "ac/stream", "data-id": "message-stream", "data-state": status, "data-select-mode": selectMode ? "true" : "false", role: "log", "aria-label": "\u6D88\u606F\u6D41", "aria-live": "polite", children: messages.map((message, i) => {
|
|
69
|
+
const label = dayDividerFor(messages[i - 1], message);
|
|
70
|
+
return (_jsxs(Fragment, { children: [label ? _jsx(DayDivider, { label: label }) : null, message.role === "system" ? (_jsx(SystemRow, { message: message })) : (_jsx(MessageRow, { message: message, agentById: agentById, onOpenAgent: onOpenAgent, selectMode: selectMode, selected: selectedIds?.has(message.id) ?? false, onToggleSelect: onToggleSelect, actions: messageActions?.(message), artifacts: messageArtifacts?.(message)?.items, artifactTotal: messageArtifacts?.(message)?.total, onOpenArtifact: messageArtifacts?.(message)?.onOpen, onOpenAllArtifacts: messageArtifacts?.(message)?.onOpenAll }))] }, message.id));
|
|
71
|
+
}) }), !atBottom ? (_jsx(Button, { variant: "outline", size: "icon-sm", "aria-label": "\u6EDA\u52A8\u5230\u6700\u65B0\u6D88\u606F", "data-cmp": "ac/scroll-bottom", "data-state": "visible", className: "ac-scroll-bottom", onClick: scrollToBottom, children: _jsx(ArrowDown, {}) })) : null] }), permissions.length > 0 ? (_jsx("div", { className: "ac-perm-dock", "data-cmp": "ac/permission-dock", "data-count": permissions.length, children: permissions.map((permission, index) => (_jsx(PermissionCard, { tool: permission.tool, reason: permission.reason, detail: permission.detail, risk: permission.risk, agent: permission.agentId
|
|
72
|
+
? (agentById[permission.agentId] ?? { id: permission.agentId })
|
|
73
|
+
: undefined, queue: { index: index + 1, total: permissions.length }, onApproveRest: onRespondPermission
|
|
74
|
+
? () => {
|
|
75
|
+
for (const rest of permissions.slice(1)) {
|
|
76
|
+
onRespondPermission(rest.permissionId, true, false);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
: undefined, onRespond: (approved, remembered) => onRespondPermission?.(permission.permissionId, approved, remembered) }, permission.permissionId))) })) : null] }));
|
|
80
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Agent } from "../../types/agent";
|
|
2
|
+
import type { ChatMessage } from "../../types/message";
|
|
3
|
+
export interface TraceToolCall {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
args?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export declare function traceToolCalls(message: ChatMessage): TraceToolCall[];
|
|
9
|
+
export interface MessageTraceProps {
|
|
10
|
+
messageId: string | null;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
findMessage: (id: string) => ChatMessage | undefined;
|
|
13
|
+
findAgent?: (id: string) => Agent | undefined;
|
|
14
|
+
onNotice?: (message: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function MessageTrace({ messageId, onClose, findMessage, findAgent, onNotice, }: MessageTraceProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "../../primitives/Button";
|
|
3
|
+
import { Dialog } from "../../primitives/Dialog";
|
|
4
|
+
import { writeClipboard } from "../../utils/clipboard";
|
|
5
|
+
const MISSING = "—";
|
|
6
|
+
export function traceToolCalls(message) {
|
|
7
|
+
if (message.toolCalls && message.toolCalls.length > 0) {
|
|
8
|
+
return message.toolCalls.map((tc) => ({
|
|
9
|
+
id: tc.id,
|
|
10
|
+
name: tc.name,
|
|
11
|
+
args: tc.args,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
return (message.parts ?? [])
|
|
15
|
+
.filter((p) => p.kind === "tool" && typeof p.toolCallId === "string" && p.toolCallId.length > 0)
|
|
16
|
+
.map((p) => ({ id: p.toolCallId, name: p.tool, args: p.args }));
|
|
17
|
+
}
|
|
18
|
+
function Row({ label, children }) {
|
|
19
|
+
return (_jsxs("div", { className: "ac-trace__row flex items-start gap-3 py-1", "data-role": "row", children: [_jsx("span", { className: "ac-trace__label w-24 shrink-0 text-meta text-muted-foreground", children: label }), _jsx("div", { className: "ac-trace__value min-w-0 flex-1 text-meta text-foreground", children: children })] }));
|
|
20
|
+
}
|
|
21
|
+
export function MessageTrace({ messageId, onClose, findMessage, findAgent, onNotice, }) {
|
|
22
|
+
const message = messageId ? findMessage(messageId) : undefined;
|
|
23
|
+
const agent = message?.agentId ? findAgent?.(message.agentId) : undefined;
|
|
24
|
+
const toolCalls = message ? traceToolCalls(message) : [];
|
|
25
|
+
const traceId = message?.sourceId ?? message?.id ?? MISSING;
|
|
26
|
+
const isLocal = !message?.sourceId;
|
|
27
|
+
return (_jsx(Dialog, { open: messageId != null, onOpenChange: (open) => {
|
|
28
|
+
if (!open)
|
|
29
|
+
onClose();
|
|
30
|
+
}, title: "\u6D88\u606F\u6EAF\u6E90", description: "\u4EE5\u4E0B\u4FE1\u606F\u5168\u90E8\u53D6\u81EA\u6D88\u606F\u672C\u4F53\u4E0E\u4E8B\u4EF6\u65E5\u5FD7\uFF0C\u7F3A\u7701\u5B57\u6BB5\u5982\u5B9E\u5C55\u793A\u3002", className: "ac-dialog ac-dialog--trace ac-trace", children: message == null ? (_jsx("p", { className: "ac-trace__missing mt-3 text-meta text-muted-foreground", children: "\u6D88\u606F\u4E0D\u5B58\u5728\u6216\u5DF2\u88AB\u5220\u9664\u3002" })) : (_jsxs("div", { className: "ac-trace__rows mt-3", children: [_jsx(Row, { label: "\u89D2\u8272", children: message.role }), _jsx(Row, { label: "\u5F52\u5C5E Agent", children: message.agentId
|
|
31
|
+
? (agent?.name ?? message.agentId)
|
|
32
|
+
: (agent?.name ?? MISSING) }), _jsx(Row, { label: "request_id", children: message.requestId ?? MISSING }), _jsx(Row, { label: "\u5DE5\u5177\u8C03\u7528", children: toolCalls.length === 0 ? (MISSING) : (_jsx("ul", { className: "ac-trace__tools space-y-1", children: toolCalls.map((call) => (_jsxs("li", { className: "ac-trace__tool", children: [_jsx("code", { className: "font-mono", children: call.name }), " ", _jsx("span", { className: "ac-trace__tool-id text-micro text-muted-foreground", children: call.id }), call.args !== undefined ? (_jsx("pre", { className: "ac-trace__args mt-0.5 overflow-x-auto font-mono text-micro text-muted-foreground", children: JSON.stringify(call.args) })) : null] }, call.id))) })) }), _jsx(Row, { label: "\u521B\u5EFA\u65F6\u95F4", children: message.createdAt ?? (message.timeLabel || MISSING) }), _jsx(Row, { label: "\u6D88\u606F id", children: _jsxs("span", { className: "ac-trace__id inline-flex items-center gap-2", children: [_jsx("code", { className: "font-mono", children: traceId }), isLocal ? _jsx("span", { className: "text-micro", children: "\uFF08\u672C\u5730\uFF09" }) : null, _jsx(Button, { variant: "outline", size: "sm", className: "ac-trace__copy", onClick: () => {
|
|
33
|
+
void writeClipboard(traceId).then((ok) => {
|
|
34
|
+
if (!ok) {
|
|
35
|
+
onNotice?.("复制失败:无法写入剪贴板,请手动选中复制");
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}, children: "\u590D\u5236" })] }) })] })) }));
|
|
39
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { MessagePart } from "../../types/message";
|
|
3
|
+
export interface PartViewProps {
|
|
4
|
+
part: MessagePart;
|
|
5
|
+
labelOf?: (tool: string) => string;
|
|
6
|
+
renderDisplay?: (part: Extract<MessagePart, {
|
|
7
|
+
kind: "display";
|
|
8
|
+
}>) => ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare function PartView({ part, labelOf, renderDisplay }: PartViewProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { toolLabel } from "../../adapters/labels";
|
|
3
|
+
import { ToolCard } from "../tool/ToolCard";
|
|
4
|
+
import { SandboxCard } from "../tool/SandboxCard";
|
|
5
|
+
import { CompactionCard } from "../cards/CompactionCard";
|
|
6
|
+
import { MemoryPill } from "../cards/MemoryPill";
|
|
7
|
+
import { DisplayCard } from "../cards/DisplayCard";
|
|
8
|
+
import { OutputCard } from "../cards/OutputCard";
|
|
9
|
+
import { ReasoningBlock } from "../reasoning/ReasoningBlock";
|
|
10
|
+
export function PartView({ part, labelOf, renderDisplay }) {
|
|
11
|
+
const label = labelOf ?? toolLabel;
|
|
12
|
+
switch (part.kind) {
|
|
13
|
+
case "tool":
|
|
14
|
+
return (_jsx(ToolCard, { name: label(part.tool), state: part.state, args: part.args, result: part.result }));
|
|
15
|
+
case "sandbox":
|
|
16
|
+
return _jsx(SandboxCard, { part: part });
|
|
17
|
+
case "compact":
|
|
18
|
+
return _jsx(CompactionCard, { part: part });
|
|
19
|
+
case "memory":
|
|
20
|
+
return _jsx(MemoryPill, { part: part });
|
|
21
|
+
case "display":
|
|
22
|
+
return _jsx(DisplayCard, { part: part, renderDisplay: renderDisplay });
|
|
23
|
+
case "output":
|
|
24
|
+
return _jsx(OutputCard, { part: part });
|
|
25
|
+
case "reasoning":
|
|
26
|
+
default:
|
|
27
|
+
return (_jsx(ReasoningBlock, { text: part.kind === "reasoning" ? part.text : "", streaming: part.kind === "reasoning" ? part.streaming : false, defaultOpen: part.kind === "reasoning" ? part.streaming : false }));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface HastElement {
|
|
2
|
+
type: "element";
|
|
3
|
+
tagName: string;
|
|
4
|
+
properties?: Record<string, unknown>;
|
|
5
|
+
children?: HastNode[];
|
|
6
|
+
}
|
|
7
|
+
export interface HastText {
|
|
8
|
+
type: "text";
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
export type HastNode = HastElement | HastText | {
|
|
12
|
+
type: string;
|
|
13
|
+
children?: HastNode[];
|
|
14
|
+
};
|
|
15
|
+
export declare function collectCodeText(node: HastNode | undefined): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function collectCodeText(node) {
|
|
2
|
+
if (!node)
|
|
3
|
+
return "";
|
|
4
|
+
if (node.type === "text")
|
|
5
|
+
return node.value;
|
|
6
|
+
const children = node.children;
|
|
7
|
+
if (!Array.isArray(children))
|
|
8
|
+
return "";
|
|
9
|
+
let out = "";
|
|
10
|
+
for (const child of children) {
|
|
11
|
+
if (child.type === "text")
|
|
12
|
+
out += child.value;
|
|
13
|
+
else if (child.type === "element")
|
|
14
|
+
out += collectCodeText(child);
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Message, MessageContent, MessageResponse, type MessageProps, type MessageResponseProps, type MessageRole, } from "./Message";
|
|
2
|
+
export { MessageBubble, type MessageBubbleProps } from "./MessageBubble";
|
|
3
|
+
export { MessageRow, type MessageRowProps } from "./MessageRow";
|
|
4
|
+
export { MessageStream, type MessageStreamProps } from "./MessageStream";
|
|
5
|
+
export { MessageAttachments, type MessageAttachmentsProps, } from "./MessageAttachments";
|
|
6
|
+
export { MessageTrace, traceToolCalls, type MessageTraceProps, type TraceToolCall, } from "./MessageTrace";
|
|
7
|
+
export { PartView, type PartViewProps } from "./PartView";
|
|
8
|
+
export { MessageActionBar, type MessageActionBarProps, } from "./MessageActionBar";
|
|
9
|
+
export { MessageArtifacts, type MessageArtifactsProps, type MessageArtifact, } from "./MessageArtifacts";
|
|
10
|
+
export { DayDivider, dayLabelOf, dayDividerFor, } from "./DayDivider";
|
|
11
|
+
export { MermaidDiagram } from "./MermaidDiagram";
|
|
12
|
+
export { collectCodeText } from "./code-text";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Message, MessageContent, MessageResponse, } from "./Message";
|
|
2
|
+
export { MessageBubble } from "./MessageBubble";
|
|
3
|
+
export { MessageRow } from "./MessageRow";
|
|
4
|
+
export { MessageStream } from "./MessageStream";
|
|
5
|
+
export { MessageAttachments, } from "./MessageAttachments";
|
|
6
|
+
export { MessageTrace, traceToolCalls, } from "./MessageTrace";
|
|
7
|
+
export { PartView } from "./PartView";
|
|
8
|
+
export { MessageActionBar, } from "./MessageActionBar";
|
|
9
|
+
export { MessageArtifacts, } from "./MessageArtifacts";
|
|
10
|
+
export { DayDivider, dayLabelOf, dayDividerFor, } from "./DayDivider";
|
|
11
|
+
export { MermaidDiagram } from "./MermaidDiagram";
|
|
12
|
+
export { collectCodeText } from "./code-text";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
2
|
+
export interface ReasoningProps extends ComponentProps<"div"> {
|
|
3
|
+
streaming?: boolean;
|
|
4
|
+
defaultOpen?: boolean;
|
|
5
|
+
children?: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare function Reasoning({ streaming, defaultOpen, className, children, ...props }: ReasoningProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { ChevronDown } from "lucide-react";
|
|
4
|
+
import { cn } from "../../utils/cn";
|
|
5
|
+
export function Reasoning({ streaming = false, defaultOpen = false, className, children, ...props }) {
|
|
6
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
7
|
+
const wasStreaming = useRef(streaming);
|
|
8
|
+
const bodyRef = useRef(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (streaming && !wasStreaming.current)
|
|
11
|
+
setOpen(true);
|
|
12
|
+
if (!streaming && wasStreaming.current)
|
|
13
|
+
setOpen(false);
|
|
14
|
+
wasStreaming.current = streaming;
|
|
15
|
+
}, [streaming]);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!streaming)
|
|
18
|
+
return;
|
|
19
|
+
const el = bodyRef.current;
|
|
20
|
+
if (el)
|
|
21
|
+
el.scrollTo({ top: el.scrollHeight });
|
|
22
|
+
});
|
|
23
|
+
return (_jsxs("div", { "data-cmp": "ai/reasoning", "data-id": "reasoning", "data-state": streaming ? "streaming" : open ? "open" : "closed", className: cn("w-full overflow-hidden text-meta", className), ...props, children: [_jsxs("button", { type: "button", "aria-label": streaming ? "思考中,点击展开深度思考" : "查看深度思考", onClick: () => setOpen((v) => !v), className: "flex w-full items-center gap-1.5 py-1 text-left text-muted-foreground hover:text-foreground", children: [_jsx("span", { className: streaming
|
|
24
|
+
? "animate-shimmer bg-gradient-to-r from-muted-foreground/50 via-foreground to-muted-foreground/50 bg-[length:200%_100%] bg-clip-text font-medium text-transparent"
|
|
25
|
+
: "font-medium", children: streaming ? "深度思考中…" : "深度思考" }), _jsx(ChevronDown, { "data-state": open ? "open" : "closed", className: cn("size-3.5 transition-transform duration-200", open && "rotate-180") })] }), open ? (_jsx("div", { ref: bodyRef, "data-cmp": "ai/reasoning-content", className: "max-h-72 overflow-y-auto overscroll-contain whitespace-pre-wrap break-words pl-1 text-muted-foreground", children: children })) : null] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Reasoning } from "./Reasoning";
|
|
3
|
+
export function ReasoningBlock({ text, streaming, defaultOpen = false, }) {
|
|
4
|
+
return (_jsx("div", { "data-cmp": "ac/reasoning", "data-state": streaming ? "streaming" : "closed", children: _jsx(Reasoning, { streaming: streaming, defaultOpen: defaultOpen, children: text }) }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SelectionTarget } from "../../types/callbacks";
|
|
2
|
+
export interface SelectionActionsProps {
|
|
3
|
+
pop: {
|
|
4
|
+
left: number;
|
|
5
|
+
top: number;
|
|
6
|
+
target: SelectionTarget;
|
|
7
|
+
} | null;
|
|
8
|
+
onDismiss: () => void;
|
|
9
|
+
onInsert: (text: string) => void;
|
|
10
|
+
onFavorite?: (target: SelectionTarget) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function SelectionActions({ pop, onDismiss, onInsert, onFavorite, }: SelectionActionsProps): import("react").ReactPortal | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
import { Button } from "../../primitives/Button";
|
|
4
|
+
import { writeClipboard } from "../../utils/clipboard";
|
|
5
|
+
import { selectionActionPayload, selectionActionsFor, } from "../../domain/selection-actions";
|
|
6
|
+
export function SelectionActions({ pop, onDismiss, onInsert, onFavorite, }) {
|
|
7
|
+
if (!pop || typeof document === "undefined")
|
|
8
|
+
return null;
|
|
9
|
+
const canFavorite = onFavorite !== undefined;
|
|
10
|
+
const actions = selectionActionsFor(pop.target, { canFavorite });
|
|
11
|
+
const run = (id) => {
|
|
12
|
+
onDismiss();
|
|
13
|
+
const payload = selectionActionPayload(id, pop.target);
|
|
14
|
+
if (!payload)
|
|
15
|
+
return;
|
|
16
|
+
if (payload.kind === "insert")
|
|
17
|
+
onInsert(payload.text);
|
|
18
|
+
else if (payload.kind === "copy")
|
|
19
|
+
void writeClipboard(payload.text);
|
|
20
|
+
else
|
|
21
|
+
onFavorite?.(pop.target);
|
|
22
|
+
};
|
|
23
|
+
return createPortal(_jsx("div", { className: "ac-selection", "data-cmp": "ac/selection-actions", "data-id": "selection-bar", role: "toolbar", "aria-label": "\u9009\u4E2D\u6587\u5B57\u7684\u64CD\u4F5C", style: { left: pop.left, top: pop.top }, onMouseDown: (e) => {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
const target = e.target;
|
|
26
|
+
if (!target.closest("[data-action]"))
|
|
27
|
+
onDismiss();
|
|
28
|
+
}, children: actions.map((action) => (_jsx(Button, { variant: "ghost", size: "xs", className: "ac-selection__btn", "data-action": action.id, onClick: () => run(action.id), children: action.label }, action.id))) }), document.body);
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SelectionCapabilities } from "../../types/selection";
|
|
2
|
+
export interface SelectionBarProps {
|
|
3
|
+
count: number;
|
|
4
|
+
capabilities: SelectionCapabilities;
|
|
5
|
+
busy?: boolean;
|
|
6
|
+
onCopy: () => void;
|
|
7
|
+
onRemove: () => void;
|
|
8
|
+
onExit: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function SelectionBar({ count, capabilities, busy, onCopy, onRemove, onExit, }: SelectionBarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Copy, Trash2, X } from "lucide-react";
|
|
3
|
+
import { Button } from "../../primitives/Button";
|
|
4
|
+
export function SelectionBar({ count, capabilities, busy = false, onCopy, onRemove, onExit, }) {
|
|
5
|
+
return (_jsxs("div", { className: "ac-select-bar", "data-cmp": "ac/select-bar", role: "toolbar", "aria-label": "\u591A\u9009\u64CD\u4F5C", children: [_jsxs("span", { className: "ac-select-bar__count", "data-cmp": "ac/select-count", children: ["\u5DF2\u9009 ", count, " \u6761"] }), _jsxs("div", { className: "ac-select-bar__actions", children: [_jsxs(Button, { variant: "ghost", size: "sm", className: "press-sm ac-select-bar__btn", disabled: !capabilities.copy || busy, title: capabilities.reason.copy ?? "复制选中消息的正文", onClick: onCopy, children: [_jsx(Copy, { size: 13 }), "\u590D\u5236"] }), _jsxs(Button, { variant: "ghost", size: "sm", className: "press-sm ac-select-bar__btn ac-select-bar__btn--danger", disabled: !capabilities.remove || busy, title: capabilities.reason.remove ?? "删除选中消息(可从后端回滚)", onClick: onRemove, children: [_jsx(Trash2, { size: 13 }), busy ? "删除中…" : "删除"] }), _jsx(Button, { variant: "ghost", size: "icon-sm", className: "press-sm", "aria-label": "\u9000\u51FA\u591A\u9009", onClick: onExit, children: _jsx(X, { size: 13 }) })] })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { RotateCcw, SlidersHorizontal } from "lucide-react";
|
|
3
|
+
import { Popover } from "../../primitives/Popover";
|
|
4
|
+
import { cn } from "../../utils/cn";
|
|
5
|
+
import { BLOCK_VISIBILITY_ORDER, } from "../../types/display";
|
|
6
|
+
import { useDisplayStore } from "../../stores/display-store";
|
|
7
|
+
import { SquishSwitch } from "./SquishSwitch";
|
|
8
|
+
function Segment({ label, value, options, onChange, }) {
|
|
9
|
+
return (_jsxs("div", { className: "ac-display__row", children: [_jsx("span", { className: "ac-display__label", children: label }), _jsx("div", { role: "radiogroup", "aria-label": label, className: "ac-display__seg", children: options.map((opt) => (_jsx("button", { type: "button", role: "radio", "aria-checked": value === opt.value, "data-state": value === opt.value ? "selected" : "idle", className: "ac-display__seg-item", onClick: () => onChange(opt.value), children: opt.label }, opt.value))) })] }));
|
|
10
|
+
}
|
|
11
|
+
export function DisplaySettingsPanel({ className, side = "top", }) {
|
|
12
|
+
const { radius, density, messageStyle, streamWidth, composerWidth, hidden, setRadius, setDensity, setMessageStyle, setStreamWidth, setComposerWidth, setBlockHidden, resetDisplay, } = useDisplayStore();
|
|
13
|
+
return (_jsx(Popover, { side: side, align: "end", contentClassName: "ac-display-panel w-72 p-0", trigger: () => (_jsx("span", { role: "button", tabIndex: 0, "data-cmp": "ac/display-settings-trigger", "data-id": "display-settings", "aria-label": "\u663E\u793A\u8BBE\u7F6E", title: "\u663E\u793A\u8BBE\u7F6E\uFF08\u5706\u89D2/\u5E03\u5C40/\u663E\u9690\uFF09", className: cn("inline-grid size-7 cursor-pointer place-items-center rounded-full text-muted-foreground hover:bg-secondary hover:text-foreground", className), children: _jsx(SlidersHorizontal, { className: "size-4" }) })), children: _jsxs("div", { "data-cmp": "ac/display-panel", children: [_jsxs("div", { className: "flex items-center justify-between px-3.5 pt-3", children: [_jsx("span", { className: "text-body font-semibold", children: "\u663E\u793A\u8BBE\u7F6E" }), _jsxs("button", { type: "button", className: "inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-micro text-muted-foreground hover:bg-secondary hover:text-foreground", onClick: resetDisplay, children: [_jsx(RotateCcw, { className: "size-3" }), "\u91CD\u7F6E"] })] }), _jsxs("div", { className: "space-y-1 px-3.5 py-2.5", children: [_jsx(Segment, { label: "\u5706\u89D2", value: radius, options: [
|
|
14
|
+
{ value: "rounded", label: "圆角" },
|
|
15
|
+
{ value: "square", label: "方角" },
|
|
16
|
+
], onChange: setRadius }), _jsx(Segment, { label: "\u5BC6\u5EA6", value: density, options: [
|
|
17
|
+
{ value: "comfortable", label: "舒适" },
|
|
18
|
+
{ value: "compact", label: "紧凑" },
|
|
19
|
+
], onChange: setDensity }), _jsx(Segment, { label: "\u6D88\u606F\u6837\u5F0F", value: messageStyle, options: [
|
|
20
|
+
{ value: "seamless", label: "无感式" },
|
|
21
|
+
{ value: "bubble", label: "气泡" },
|
|
22
|
+
], onChange: setMessageStyle }), _jsx(Segment, { label: "\u6D88\u606F\u5BBD\u5EA6", value: streamWidth, options: [
|
|
23
|
+
{ value: "narrow", label: "窄" },
|
|
24
|
+
{ value: "standard", label: "标准" },
|
|
25
|
+
{ value: "wide", label: "宽" },
|
|
26
|
+
{ value: "full", label: "全宽" },
|
|
27
|
+
], onChange: setStreamWidth }), _jsx(Segment, { label: "\u8F93\u5165\u5BBD\u5EA6", value: composerWidth, options: [
|
|
28
|
+
{ value: "narrow", label: "窄" },
|
|
29
|
+
{ value: "standard", label: "标准" },
|
|
30
|
+
{ value: "wide", label: "宽" },
|
|
31
|
+
{ value: "full", label: "全宽" },
|
|
32
|
+
], onChange: setComposerWidth })] }), _jsxs("div", { className: "border-t border-border px-3.5 py-2.5", children: [_jsx("div", { className: "mb-1.5 text-micro font-medium tracking-wide text-muted-foreground", children: "\u663E\u793A\u5757\u5F00\u5173" }), _jsx("div", { className: "space-y-0.5", children: BLOCK_VISIBILITY_ORDER.map(({ key, label }) => (_jsxs("label", { className: "flex cursor-pointer items-center justify-between rounded px-1 py-1 hover:bg-secondary/60", children: [_jsx("span", { className: "text-body", children: label }), _jsx(SquishSwitch, { checked: !hidden[key], onChange: (next) => setBlockHidden(key, !next), ariaLabel: `显示${label}` })] }, key))) })] }), _jsx("p", { className: "border-t border-border px-3.5 py-2 text-micro text-muted-foreground", children: "\u914D\u7F6E\u5373\u65F6\u751F\u6548\u5E76\u6301\u4E45\u5316\uFF08localStorage\uFF09\u3002" })] }) }));
|
|
33
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SquishSwitchProps {
|
|
2
|
+
checked: boolean;
|
|
3
|
+
onChange: (checked: boolean) => void;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
ariaLabel?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function SquishSwitch({ checked, onChange, disabled, ariaLabel, className, }: SquishSwitchProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { cn } from "../../utils/cn";
|
|
4
|
+
export function SquishSwitch({ checked, onChange, disabled = false, ariaLabel, className, }) {
|
|
5
|
+
const [squish, setSquish] = useState("");
|
|
6
|
+
const first = useRef(true);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (first.current) {
|
|
9
|
+
first.current = false;
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
setSquish("stretches");
|
|
13
|
+
const t1 = window.setTimeout(() => setSquish("squash"), 180);
|
|
14
|
+
const t2 = window.setTimeout(() => setSquish(""), 380);
|
|
15
|
+
return () => {
|
|
16
|
+
window.clearTimeout(t1);
|
|
17
|
+
window.clearTimeout(t2);
|
|
18
|
+
};
|
|
19
|
+
}, [checked]);
|
|
20
|
+
return (_jsx("button", { type: "button", role: "switch", "aria-checked": checked, "aria-label": ariaLabel, disabled: disabled, "data-cmp": "ai/squish-switch", "data-state": checked ? "on" : "off", "data-squish": squish, className: cn("ac-squish", disabled && "pointer-events-none opacity-40", className), onClick: () => onChange(!checked), children: _jsx("span", { className: "ac-squish__track", children: _jsx("span", { className: "ac-squish__thumb" }) }) }));
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type LatticeStatus = "working" | "done" | "error";
|
|
2
|
+
export interface LatticeLoaderProps {
|
|
3
|
+
status: LatticeStatus;
|
|
4
|
+
label?: string;
|
|
5
|
+
doneLabel?: string;
|
|
6
|
+
errorLabel?: string;
|
|
7
|
+
elapsed?: number;
|
|
8
|
+
showTimer?: boolean;
|
|
9
|
+
step?: number;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function LatticeLoader({ status, label, doneLabel, errorLabel, elapsed, showTimer, step, className, }: LatticeLoaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { cn } from "../../utils/cn";
|
|
4
|
+
const ORBIT_DELAYS = [4, 3, 2, 5, 0, 1, 6, 7, 8];
|
|
5
|
+
export function LatticeLoader({ status, label = "思考中", doneLabel = "完成,用时", errorLabel = "失败,用时", elapsed, showTimer = true, step = 90, className, }) {
|
|
6
|
+
const start = useRef(Date.now());
|
|
7
|
+
const [tick, setTick] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (status !== "working" || elapsed !== undefined)
|
|
10
|
+
return;
|
|
11
|
+
const id = window.setInterval(() => setTick((t) => t + 1), 100);
|
|
12
|
+
return () => window.clearInterval(id);
|
|
13
|
+
}, [status, elapsed]);
|
|
14
|
+
const seconds = elapsed ?? (status === "working" ? (Date.now() - start.current) / 1000 : tick / 10);
|
|
15
|
+
const frozen = status !== "working";
|
|
16
|
+
return (_jsxs("div", { className: cn("ac-lattice", className), "data-cmp": "ai/lattice-loader", "data-status": status, children: [_jsx("div", { className: "ac-lattice__grid", "aria-hidden": "true", children: ORBIT_DELAYS.map((delay, i) => (_jsx("span", { className: cn("ac-lattice__cell", !frozen && "ac-lattice__cell--lit"), style: frozen
|
|
17
|
+
? { opacity: 0.15 }
|
|
18
|
+
: {
|
|
19
|
+
animationDelay: `${delay * step}ms`,
|
|
20
|
+
animationDuration: `${step * 6}ms`,
|
|
21
|
+
} }, i))) }), _jsx("span", { className: "ac-lattice__label", children: status === "done" ? doneLabel : status === "error" ? errorLabel : label }), showTimer ? (_jsxs("span", { className: "ac-lattice__timer", children: [status === "done" ? (_jsx("span", { className: "ac-lattice__mark", "data-mark": "done", children: "\u2713" })) : status === "error" ? (_jsx("span", { className: "ac-lattice__mark", "data-mark": "error", children: "\u2717" })) : null, seconds.toFixed(1), "s"] })) : null] }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function TypingIndicator({ agent }) {
|
|
3
|
+
const who = agent?.name ?? "Agent";
|
|
4
|
+
return (_jsxs("div", { className: "ac-typing", "data-cmp": "ac/typing", "data-state": "streaming", role: "status", "aria-label": `${who} 正在回复`, children: [_jsx("i", {}), _jsx("i", {}), _jsx("i", {}), _jsxs("span", { children: [who, " \u601D\u8003\u4E2D"] })] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Terminal } from "lucide-react";
|
|
3
|
+
const STATE_LABEL = {
|
|
4
|
+
running: "运行中",
|
|
5
|
+
finished: "完成",
|
|
6
|
+
error: "失败",
|
|
7
|
+
};
|
|
8
|
+
export function SandboxCard({ part }) {
|
|
9
|
+
return (_jsxs("div", { className: "ac-card", "data-cmp": "ac/sandbox", "data-state": part.state, children: [_jsxs("div", { className: "ac-card__head", children: [_jsx("span", { className: "ac-card__dot", "data-state": part.state }), _jsx(Terminal, { "aria-hidden": "true", size: 12 }), _jsx("span", { className: "ac-card__title", children: part.title }), _jsx("span", { className: "ac-card__state", children: STATE_LABEL[part.state] })] }), part.output ? (_jsx("div", { className: "ac-card__body", "data-kind": "result", children: part.output })) : null] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
import type { ToolCallState } from "../../types/message";
|
|
3
|
+
export interface ToolCardProps extends ComponentProps<"div"> {
|
|
4
|
+
name: string;
|
|
5
|
+
state: ToolCallState;
|
|
6
|
+
args?: unknown;
|
|
7
|
+
result?: string;
|
|
8
|
+
detail?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function ToolCard({ name, state, args, result, detail, className, ...props }: ToolCardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { ChevronDown, Wrench } from "lucide-react";
|
|
4
|
+
import { cn } from "../../utils/cn";
|
|
5
|
+
import { parseExecResult, prettyResult, diffStatOf } from "./exec-envelope";
|
|
6
|
+
import { DiffView, looksLikeDiff } from "../message/DiffView";
|
|
7
|
+
const STATE_TEXT = {
|
|
8
|
+
running: "执行中",
|
|
9
|
+
finished: "完成",
|
|
10
|
+
error: "失败",
|
|
11
|
+
};
|
|
12
|
+
export function ToolCard({ name, state, args, result, detail, className, ...props }) {
|
|
13
|
+
const [open, setOpen] = useState(false);
|
|
14
|
+
const stat = diffStatOf(result);
|
|
15
|
+
return (_jsxs("div", { "data-cmp": "ai/tool-card", "data-id": "tool-card", "data-state": state, className: cn("w-full overflow-hidden text-meta", className), ...props, children: [_jsxs("button", { type: "button", "aria-label": `工具调用 ${name},${STATE_TEXT[state]},点击展开详情`, "data-state": open ? "open" : "closed", onClick: () => setOpen((v) => !v), className: "flex w-full items-center gap-2 py-1 text-left text-muted-foreground hover:text-foreground", children: [_jsx(Wrench, { className: cn("size-3.5 shrink-0", state === "running" && "animate-pulse text-[var(--accent-warning)]", state === "error" && "text-[var(--accent-danger)]") }), _jsx("span", { className: "truncate font-medium text-foreground", children: name }), detail ? (_jsx("span", { className: "truncate text-muted-foreground", children: detail })) : null, stat ? (_jsxs("span", { className: "shrink-0 font-mono text-micro", children: [_jsxs("span", { className: "text-[var(--diff-add-fg)]", children: ["+", stat.add] }), " ", _jsxs("span", { className: "text-[var(--diff-del-fg)]", children: ["\u2212", stat.del] })] })) : null, state === "running" ? (_jsx("span", { className: "ml-auto shrink-0 text-micro text-muted-foreground", children: STATE_TEXT[state] })) : state === "error" ? (_jsx("span", { className: "ml-auto shrink-0 text-micro text-[var(--accent-danger)]", children: STATE_TEXT[state] })) : null, _jsx(ChevronDown, { className: cn("ml-auto size-3.5 shrink-0 transition-transform duration-200", (state !== "running" && state !== "error") || "ml-0", open && "rotate-180") })] }), open ? (_jsxs("div", { "data-cmp": "ai/tool-detail", className: "space-y-2 py-1.5 pl-1", children: [args !== undefined ? (_jsxs("div", { children: [_jsx("span", { className: "font-mono text-micro uppercase tracking-wide text-muted-foreground", children: "\u53C2\u6570" }), _jsx("pre", { className: "max-h-40 overflow-y-auto font-mono text-meta text-muted-foreground", children: JSON.stringify(args, null, 2) })] })) : null, result ? (_jsxs("div", { children: [_jsx("span", { className: "font-mono text-micro uppercase tracking-wide text-muted-foreground", children: "\u7ED3\u679C" }), _jsx(ResultBlocks, { result: result })] })) : null] })) : null] }));
|
|
16
|
+
}
|
|
17
|
+
function OutputBlock({ label, content, tone, }) {
|
|
18
|
+
const isDiff = tone !== "error" && looksLikeDiff(content);
|
|
19
|
+
return (_jsxs("div", { children: [label ? (_jsx("span", { className: cn("font-mono text-micro uppercase tracking-wide", tone === "error" ? "text-destructive" : "text-muted-foreground"), children: label })) : null, isDiff ? (_jsx(DiffView, { code: content, className: "mt-1" })) : (_jsx("pre", { className: cn("max-h-64 min-w-0 overflow-y-auto overflow-x-hidden whitespace-pre-wrap break-all font-mono text-meta", tone === "error" ? "text-destructive" : "text-muted-foreground"), children: content }))] }));
|
|
20
|
+
}
|
|
21
|
+
function ResultBlocks({ result }) {
|
|
22
|
+
const envelope = parseExecResult(result);
|
|
23
|
+
if (envelope) {
|
|
24
|
+
const failed = envelope.exit_code !== 0 || envelope.timed_out === true;
|
|
25
|
+
return (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { "data-tone": failed ? "error" : "ok", className: "flex flex-wrap items-center gap-2 text-meta", children: [_jsx("span", { className: failed ? "text-destructive" : "text-[var(--accent-success)]", children: envelope.timed_out === true
|
|
26
|
+
? "超时"
|
|
27
|
+
: envelope.exit_code === 0
|
|
28
|
+
? "成功"
|
|
29
|
+
: `退出码 ${envelope.exit_code}` }), envelope.duration_ms !== undefined ? (_jsxs("span", { className: "text-muted-foreground", children: ["\u8017\u65F6 ", envelope.duration_ms, " ms"] })) : null, envelope.truncated === true ? (_jsx("span", { className: "text-muted-foreground", children: "\u8F93\u51FA\u5DF2\u622A\u65AD" })) : null] }), envelope.stdout.length > 0 ? (_jsx(OutputBlock, { label: "stdout", content: envelope.stdout })) : null, envelope.stderr.length > 0 ? (_jsx(OutputBlock, { label: "stderr", content: envelope.stderr, tone: "error" })) : null, envelope.stdout.length === 0 && envelope.stderr.length === 0 ? (_jsx("p", { className: "text-meta text-muted-foreground", children: "\uFF08\u65E0\u8F93\u51FA\uFF09" })) : null] }));
|
|
30
|
+
}
|
|
31
|
+
return _jsx(OutputBlock, { content: prettyResult(result) });
|
|
32
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type KeyedToolPart } from "../../domain/message-blocks";
|
|
2
|
+
export interface ToolGroupProps {
|
|
3
|
+
parts: KeyedToolPart[];
|
|
4
|
+
renderPart?: (item: KeyedToolPart) => React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export declare function ToolGroup({ parts, renderPart }: ToolGroupProps): import("react").JSX.Element;
|