@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.
Files changed (248) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -2
  3. package/lib/adapters/event-bus.d.ts +80 -0
  4. package/lib/adapters/event-bus.js +73 -0
  5. package/lib/adapters/index.d.ts +3 -0
  6. package/lib/adapters/index.js +3 -0
  7. package/lib/adapters/labels.d.ts +6 -0
  8. package/lib/adapters/labels.js +17 -0
  9. package/lib/adapters/stream-bridge.d.ts +9 -0
  10. package/lib/adapters/stream-bridge.js +7 -0
  11. package/lib/blocks/cards/AskCard.d.ts +8 -0
  12. package/lib/blocks/cards/AskCard.js +97 -0
  13. package/lib/blocks/cards/CompactionCard.d.ts +5 -0
  14. package/lib/blocks/cards/CompactionCard.js +5 -0
  15. package/lib/blocks/cards/ContextUsage.d.ts +14 -0
  16. package/lib/blocks/cards/ContextUsage.js +136 -0
  17. package/lib/blocks/cards/DisplayCard.d.ts +7 -0
  18. package/lib/blocks/cards/DisplayCard.js +27 -0
  19. package/lib/blocks/cards/MemoryPill.d.ts +5 -0
  20. package/lib/blocks/cards/MemoryPill.js +5 -0
  21. package/lib/blocks/cards/OutputCard.d.ts +5 -0
  22. package/lib/blocks/cards/OutputCard.js +13 -0
  23. package/lib/blocks/cards/PermissionCard.d.ts +21 -0
  24. package/lib/blocks/cards/PermissionCard.js +26 -0
  25. package/lib/blocks/cards/SpringCheck.d.ts +9 -0
  26. package/lib/blocks/cards/SpringCheck.js +17 -0
  27. package/lib/blocks/cards/TodoCard.d.ts +19 -0
  28. package/lib/blocks/cards/TodoCard.js +28 -0
  29. package/lib/blocks/cards/index.d.ts +10 -0
  30. package/lib/blocks/cards/index.js +10 -0
  31. package/lib/blocks/composer/Composer.d.ts +66 -0
  32. package/lib/blocks/composer/Composer.js +73 -0
  33. package/lib/blocks/composer/ComposerAttachments.d.ts +17 -0
  34. package/lib/blocks/composer/ComposerAttachments.js +10 -0
  35. package/lib/blocks/composer/ComposerGrip.d.ts +1 -0
  36. package/lib/blocks/composer/ComposerGrip.js +39 -0
  37. package/lib/blocks/composer/ComposerInput.d.ts +18 -0
  38. package/lib/blocks/composer/ComposerInput.js +60 -0
  39. package/lib/blocks/composer/ComposerMenus.d.ts +10 -0
  40. package/lib/blocks/composer/ComposerMenus.js +78 -0
  41. package/lib/blocks/composer/ComposerToolbar.d.ts +27 -0
  42. package/lib/blocks/composer/ComposerToolbar.js +16 -0
  43. package/lib/blocks/composer/MentionPicker.d.ts +13 -0
  44. package/lib/blocks/composer/MentionPicker.js +71 -0
  45. package/lib/blocks/composer/PromptPop.d.ts +7 -0
  46. package/lib/blocks/composer/PromptPop.js +40 -0
  47. package/lib/blocks/composer/QueuedPrompts.d.ts +14 -0
  48. package/lib/blocks/composer/QueuedPrompts.js +21 -0
  49. package/lib/blocks/composer/SlingButton.d.ts +10 -0
  50. package/lib/blocks/composer/SlingButton.js +112 -0
  51. package/lib/blocks/composer/index.d.ts +7 -0
  52. package/lib/blocks/composer/index.js +1 -0
  53. package/lib/blocks/conversation/ChatEmptyScene.d.ts +4 -0
  54. package/lib/blocks/conversation/ChatEmptyScene.js +21 -0
  55. package/lib/blocks/conversation/ChatHeader.d.ts +13 -0
  56. package/lib/blocks/conversation/ChatHeader.js +20 -0
  57. package/lib/blocks/conversation/ChatPane.d.ts +90 -0
  58. package/lib/blocks/conversation/ChatPane.js +60 -0
  59. package/lib/blocks/conversation/Conversation.d.ts +11 -0
  60. package/lib/blocks/conversation/Conversation.js +41 -0
  61. package/lib/blocks/conversation/LineSidebar.d.ts +14 -0
  62. package/lib/blocks/conversation/LineSidebar.js +42 -0
  63. package/lib/blocks/conversation/index.d.ts +5 -0
  64. package/lib/blocks/conversation/index.js +5 -0
  65. package/lib/blocks/index.d.ts +9 -0
  66. package/lib/blocks/index.js +9 -0
  67. package/lib/blocks/message/DayDivider.d.ts +10 -0
  68. package/lib/blocks/message/DayDivider.js +29 -0
  69. package/lib/blocks/message/DiffView.d.ts +5 -0
  70. package/lib/blocks/message/DiffView.js +18 -0
  71. package/lib/blocks/message/MermaidDiagram.d.ts +4 -0
  72. package/lib/blocks/message/MermaidDiagram.js +43 -0
  73. package/lib/blocks/message/Message.d.ts +17 -0
  74. package/lib/blocks/message/Message.js +45 -0
  75. package/lib/blocks/message/MessageActionBar.d.ts +18 -0
  76. package/lib/blocks/message/MessageActionBar.js +15 -0
  77. package/lib/blocks/message/MessageArtifacts.d.ts +13 -0
  78. package/lib/blocks/message/MessageArtifacts.js +9 -0
  79. package/lib/blocks/message/MessageAttachments.d.ts +5 -0
  80. package/lib/blocks/message/MessageAttachments.js +28 -0
  81. package/lib/blocks/message/MessageBubble.d.ts +11 -0
  82. package/lib/blocks/message/MessageBubble.js +31 -0
  83. package/lib/blocks/message/MessageRow.d.ts +20 -0
  84. package/lib/blocks/message/MessageRow.js +49 -0
  85. package/lib/blocks/message/MessageStream.d.ts +23 -0
  86. package/lib/blocks/message/MessageStream.js +80 -0
  87. package/lib/blocks/message/MessageTrace.d.ts +16 -0
  88. package/lib/blocks/message/MessageTrace.js +39 -0
  89. package/lib/blocks/message/PartView.d.ts +10 -0
  90. package/lib/blocks/message/PartView.js +29 -0
  91. package/lib/blocks/message/code-text.d.ts +15 -0
  92. package/lib/blocks/message/code-text.js +17 -0
  93. package/lib/blocks/message/index.d.ts +12 -0
  94. package/lib/blocks/message/index.js +12 -0
  95. package/lib/blocks/reasoning/Reasoning.d.ts +7 -0
  96. package/lib/blocks/reasoning/Reasoning.js +26 -0
  97. package/lib/blocks/reasoning/ReasoningBlock.d.ts +6 -0
  98. package/lib/blocks/reasoning/ReasoningBlock.js +5 -0
  99. package/lib/blocks/reasoning/index.d.ts +2 -0
  100. package/lib/blocks/reasoning/index.js +2 -0
  101. package/lib/blocks/selection/SelectionActions.d.ts +12 -0
  102. package/lib/blocks/selection/SelectionActions.js +29 -0
  103. package/lib/blocks/selection/SelectionBar.d.ts +10 -0
  104. package/lib/blocks/selection/SelectionBar.js +6 -0
  105. package/lib/blocks/selection/index.d.ts +2 -0
  106. package/lib/blocks/selection/index.js +2 -0
  107. package/lib/blocks/settings/DisplaySettingsPanel.d.ts +5 -0
  108. package/lib/blocks/settings/DisplaySettingsPanel.js +33 -0
  109. package/lib/blocks/settings/SquishSwitch.d.ts +8 -0
  110. package/lib/blocks/settings/SquishSwitch.js +21 -0
  111. package/lib/blocks/settings/index.d.ts +2 -0
  112. package/lib/blocks/settings/index.js +2 -0
  113. package/lib/blocks/status/LatticeLoader.d.ts +12 -0
  114. package/lib/blocks/status/LatticeLoader.js +22 -0
  115. package/lib/blocks/status/TypingIndicator.d.ts +5 -0
  116. package/lib/blocks/status/TypingIndicator.js +5 -0
  117. package/lib/blocks/status/index.d.ts +2 -0
  118. package/lib/blocks/status/index.js +2 -0
  119. package/lib/blocks/tool/SandboxCard.d.ts +5 -0
  120. package/lib/blocks/tool/SandboxCard.js +10 -0
  121. package/lib/blocks/tool/ToolCard.d.ts +10 -0
  122. package/lib/blocks/tool/ToolCard.js +32 -0
  123. package/lib/blocks/tool/ToolGroup.d.ts +6 -0
  124. package/lib/blocks/tool/ToolGroup.js +23 -0
  125. package/lib/blocks/tool/exec-envelope.d.ts +7 -0
  126. package/lib/blocks/tool/exec-envelope.js +43 -0
  127. package/lib/blocks/tool/index.d.ts +4 -0
  128. package/lib/blocks/tool/index.js +4 -0
  129. package/lib/domain/ask.d.ts +14 -0
  130. package/lib/domain/ask.js +50 -0
  131. package/lib/domain/attachments.d.ts +16 -0
  132. package/lib/domain/attachments.js +126 -0
  133. package/lib/domain/attribution.d.ts +4 -0
  134. package/lib/domain/attribution.js +11 -0
  135. package/lib/domain/composer-palette.d.ts +15 -0
  136. package/lib/domain/composer-palette.js +131 -0
  137. package/lib/domain/index.d.ts +12 -0
  138. package/lib/domain/index.js +12 -0
  139. package/lib/domain/mention.d.ts +17 -0
  140. package/lib/domain/mention.js +95 -0
  141. package/lib/domain/message-blocks.d.ts +29 -0
  142. package/lib/domain/message-blocks.js +63 -0
  143. package/lib/domain/message-menu.d.ts +4 -0
  144. package/lib/domain/message-menu.js +46 -0
  145. package/lib/domain/queue.d.ts +23 -0
  146. package/lib/domain/queue.js +52 -0
  147. package/lib/domain/reducer.d.ts +137 -0
  148. package/lib/domain/reducer.js +442 -0
  149. package/lib/domain/selection-actions.d.ts +13 -0
  150. package/lib/domain/selection-actions.js +56 -0
  151. package/lib/domain/selection-gates.d.ts +6 -0
  152. package/lib/domain/selection-gates.js +36 -0
  153. package/lib/domain/slash-commands.d.ts +16 -0
  154. package/lib/domain/slash-commands.js +142 -0
  155. package/lib/hooks/index.d.ts +8 -0
  156. package/lib/hooks/index.js +8 -0
  157. package/lib/hooks/useComposerPalette.d.ts +35 -0
  158. package/lib/hooks/useComposerPalette.js +120 -0
  159. package/lib/hooks/useComposerSubmit.d.ts +31 -0
  160. package/lib/hooks/useComposerSubmit.js +135 -0
  161. package/lib/hooks/useInputHistory.d.ts +14 -0
  162. package/lib/hooks/useInputHistory.js +52 -0
  163. package/lib/hooks/usePendingAsk.d.ts +11 -0
  164. package/lib/hooks/usePendingAsk.js +33 -0
  165. package/lib/hooks/usePendingPermissions.d.ts +10 -0
  166. package/lib/hooks/usePendingPermissions.js +26 -0
  167. package/lib/hooks/useQueuedPrompts.d.ts +12 -0
  168. package/lib/hooks/useQueuedPrompts.js +91 -0
  169. package/lib/hooks/useSelectionPop.d.ts +13 -0
  170. package/lib/hooks/useSelectionPop.js +130 -0
  171. package/lib/hooks/useStreamBridge.d.ts +1 -0
  172. package/lib/hooks/useStreamBridge.js +23 -0
  173. package/lib/index.d.ts +10 -0
  174. package/lib/index.js +10 -0
  175. package/lib/primitives/Badge.d.ts +7 -0
  176. package/lib/primitives/Badge.js +13 -0
  177. package/lib/primitives/Button.d.ts +9 -0
  178. package/lib/primitives/Button.js +21 -0
  179. package/lib/primitives/Collapsible.d.ts +12 -0
  180. package/lib/primitives/Collapsible.js +14 -0
  181. package/lib/primitives/Dialog.d.ts +10 -0
  182. package/lib/primitives/Dialog.js +19 -0
  183. package/lib/primitives/DropdownMenu.d.ts +28 -0
  184. package/lib/primitives/DropdownMenu.js +37 -0
  185. package/lib/primitives/Empty.d.ts +8 -0
  186. package/lib/primitives/Empty.js +6 -0
  187. package/lib/primitives/Input.d.ts +3 -0
  188. package/lib/primitives/Input.js +6 -0
  189. package/lib/primitives/Kbd.d.ts +2 -0
  190. package/lib/primitives/Kbd.js +5 -0
  191. package/lib/primitives/Popover.d.ts +15 -0
  192. package/lib/primitives/Popover.js +32 -0
  193. package/lib/primitives/ScrollArea.d.ts +5 -0
  194. package/lib/primitives/ScrollArea.js +5 -0
  195. package/lib/primitives/Separator.d.ts +5 -0
  196. package/lib/primitives/Separator.js +5 -0
  197. package/lib/primitives/Spinner.d.ts +5 -0
  198. package/lib/primitives/Spinner.js +5 -0
  199. package/lib/primitives/Textarea.d.ts +3 -0
  200. package/lib/primitives/Textarea.js +6 -0
  201. package/lib/primitives/Tooltip.d.ts +8 -0
  202. package/lib/primitives/Tooltip.js +7 -0
  203. package/lib/primitives/index.d.ts +14 -0
  204. package/lib/primitives/index.js +14 -0
  205. package/lib/reactbits/Motion.d.ts +24 -0
  206. package/lib/reactbits/Motion.js +52 -0
  207. package/lib/reactbits/index.d.ts +1 -0
  208. package/lib/reactbits/index.js +1 -0
  209. package/lib/stores/chat-store.d.ts +33 -0
  210. package/lib/stores/chat-store.js +93 -0
  211. package/lib/stores/display-store.d.ts +15 -0
  212. package/lib/stores/display-store.js +116 -0
  213. package/lib/stores/index.d.ts +3 -0
  214. package/lib/stores/index.js +3 -0
  215. package/lib/stores/theme-store.d.ts +8 -0
  216. package/lib/stores/theme-store.js +29 -0
  217. package/lib/tokens/index.d.ts +21 -0
  218. package/lib/tokens/index.js +66 -0
  219. package/lib/types/agent.d.ts +62 -0
  220. package/lib/types/agent.js +1 -0
  221. package/lib/types/callbacks.d.ts +55 -0
  222. package/lib/types/callbacks.js +1 -0
  223. package/lib/types/composer.d.ts +116 -0
  224. package/lib/types/composer.js +1 -0
  225. package/lib/types/conversation.d.ts +54 -0
  226. package/lib/types/conversation.js +1 -0
  227. package/lib/types/display.d.ts +25 -0
  228. package/lib/types/display.js +40 -0
  229. package/lib/types/index.d.ts +8 -0
  230. package/lib/types/index.js +8 -0
  231. package/lib/types/message.d.ts +100 -0
  232. package/lib/types/message.js +1 -0
  233. package/lib/types/selection.d.ts +60 -0
  234. package/lib/types/selection.js +1 -0
  235. package/lib/types/theme.d.ts +100 -0
  236. package/lib/types/theme.js +1 -0
  237. package/lib/utils/clipboard.d.ts +4 -0
  238. package/lib/utils/clipboard.js +41 -0
  239. package/lib/utils/cn.d.ts +2 -0
  240. package/lib/utils/cn.js +14 -0
  241. package/lib/utils/index.d.ts +3 -0
  242. package/lib/utils/index.js +3 -0
  243. package/lib/utils/storage.d.ts +11 -0
  244. package/lib/utils/storage.js +48 -0
  245. package/package.json +93 -4
  246. package/src/styles/blocks.css +2439 -0
  247. package/src/styles/index.css +3 -0
  248. 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,6 @@
1
+ export interface ReasoningBlockProps {
2
+ text: string;
3
+ streaming: boolean;
4
+ defaultOpen?: boolean;
5
+ }
6
+ export declare function ReasoningBlock({ text, streaming, defaultOpen, }: ReasoningBlockProps): import("react").JSX.Element;
@@ -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,2 @@
1
+ export { Reasoning, type ReasoningProps } from "./Reasoning";
2
+ export { ReasoningBlock, type ReasoningBlockProps, } from "./ReasoningBlock";
@@ -0,0 +1,2 @@
1
+ export { Reasoning } from "./Reasoning";
2
+ export { ReasoningBlock, } from "./ReasoningBlock";
@@ -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,2 @@
1
+ export { SelectionBar, type SelectionBarProps } from "./SelectionBar";
2
+ export { SelectionActions, type SelectionActionsProps, } from "./SelectionActions";
@@ -0,0 +1,2 @@
1
+ export { SelectionBar } from "./SelectionBar";
2
+ export { SelectionActions, } from "./SelectionActions";
@@ -0,0 +1,5 @@
1
+ export interface DisplaySettingsPanelProps {
2
+ className?: string;
3
+ side?: "top" | "bottom";
4
+ }
5
+ export declare function DisplaySettingsPanel({ className, side, }: DisplaySettingsPanelProps): import("react").JSX.Element;
@@ -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,2 @@
1
+ export { DisplaySettingsPanel, type DisplaySettingsPanelProps, } from "./DisplaySettingsPanel";
2
+ export { SquishSwitch, type SquishSwitchProps, } from "./SquishSwitch";
@@ -0,0 +1,2 @@
1
+ export { DisplaySettingsPanel, } from "./DisplaySettingsPanel";
2
+ export { SquishSwitch, } from "./SquishSwitch";
@@ -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 type { Agent } from "../../types/agent";
2
+ export interface TypingIndicatorProps {
3
+ agent?: Agent;
4
+ }
5
+ export declare function TypingIndicator({ agent }: TypingIndicatorProps): import("react").JSX.Element;
@@ -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,2 @@
1
+ export { TypingIndicator, type TypingIndicatorProps } from "./TypingIndicator";
2
+ export { LatticeLoader, type LatticeLoaderProps, type LatticeStatus, } from "./LatticeLoader";
@@ -0,0 +1,2 @@
1
+ export { TypingIndicator } from "./TypingIndicator";
2
+ export { LatticeLoader, } from "./LatticeLoader";
@@ -0,0 +1,5 @@
1
+ import type { SandboxPart } from "../../types/message";
2
+ export interface SandboxCardProps {
3
+ part: SandboxPart;
4
+ }
5
+ export declare function SandboxCard({ part }: SandboxCardProps): import("react").JSX.Element;
@@ -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;