@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,27 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ContextUsageSegment, ContextWindowPreset } from "../../types/message";
3
+ import { type ComposerModelSwitch, type PermissionModeSwitch } from "./ComposerMenus";
4
+ export interface ComposerToolbarProps {
5
+ picker: ReactNode;
6
+ permissionMode?: PermissionModeSwitch;
7
+ modelSwitch?: ComposerModelSwitch;
8
+ onManageModel?: () => void;
9
+ streaming: boolean;
10
+ canStop: boolean;
11
+ empty: boolean;
12
+ onStop?: () => void;
13
+ onSubmit: () => void;
14
+ contextUsage?: {
15
+ input: number;
16
+ output?: number;
17
+ window: number;
18
+ breakdown?: readonly ContextUsageSegment[];
19
+ windowPresets?: readonly ContextWindowPreset[];
20
+ onWindowChange?: (window: number) => void;
21
+ };
22
+ visionTag: ReactNode;
23
+ onMic?: () => void;
24
+ micState?: "idle" | "listening";
25
+ trailing?: ReactNode;
26
+ }
27
+ export declare function ComposerToolbar({ picker, permissionMode, modelSwitch, onManageModel, streaming, canStop, empty, onStop, onSubmit, contextUsage, visionTag, onMic, micState, trailing, }: ComposerToolbarProps): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Mic, Square } from "lucide-react";
3
+ import { ContextUsage } from "../cards/ContextUsage";
4
+ import { SlingButton } from "./SlingButton";
5
+ import { ComposerModelChip, ComposerPermChip, } from "./ComposerMenus";
6
+ export function ComposerToolbar({ picker, permissionMode, modelSwitch, onManageModel, streaming, canStop, empty, onStop, onSubmit, contextUsage, visionTag, onMic, micState = "idle", trailing, }) {
7
+ return (_jsxs("div", { className: "ac-composer__bar", children: [_jsxs("div", { className: "ac-composer__left", children: [picker, permissionMode ? (_jsx(ComposerPermChip, { permissionMode: permissionMode })) : null] }), _jsx("span", { className: "ac-composer__count", children: streaming
8
+ ? "Enter 进入待发队列 · Shift+Enter 换行"
9
+ : empty
10
+ ? ""
11
+ : "Enter 发送 · Shift+Enter 换行" }), _jsxs("div", { className: "ac-composer__right", children: [canStop ? (_jsx("button", { type: "button", className: "ac-composer__stop-ring", "aria-label": "\u505C\u6B62\u751F\u6210", title: "\u505C\u6B62\u751F\u6210", onClick: onStop, children: _jsx(Square, { className: "size-3" }) })) : null, modelSwitch ? (_jsx(ComposerModelChip, { modelSwitch: modelSwitch, onManage: onManageModel })) : null, contextUsage ? _jsx(ContextUsage, { ...contextUsage }) : null, visionTag, trailing, onMic ? (_jsx("button", { type: "button", className: "ac-composer__mic", "data-state": micState, "aria-label": micState === "listening" ? "停止录音" : "语音输入", title: micState === "listening" ? "停止录音" : "语音输入", onClick: onMic, children: _jsx(Mic, { className: "size-4" }) })) : null, _jsx(SlingButton, { onSend: canStop ? () => onStop?.() : onSubmit, disabled: !canStop && empty, ariaLabel: canStop
12
+ ? "停止生成"
13
+ : empty
14
+ ? "输入消息后发送(Enter)"
15
+ : "发送消息(Enter · Shift+Enter 换行)", children: canStop ? _jsx(Square, { className: "size-4" }) : undefined })] })] }));
16
+ }
@@ -0,0 +1,13 @@
1
+ import type { ComposerTrigger, PaletteItem } from "../../types/composer";
2
+ export interface MentionPickerProps {
3
+ open: boolean;
4
+ trigger: ComposerTrigger | null;
5
+ selectedAgent?: {
6
+ name: string;
7
+ } | null;
8
+ items: PaletteItem[];
9
+ index: number;
10
+ onMove: (delta: number) => void;
11
+ onPick: (item: PaletteItem) => void;
12
+ }
13
+ export declare function MentionPicker({ open, trigger, selectedAgent, items, index, onMove, onPick, }: MentionPickerProps): import("react").JSX.Element;
@@ -0,0 +1,71 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useId, useRef } from "react";
3
+ import { File, Folder, Users } from "lucide-react";
4
+ const SOURCE_LABEL = {
5
+ builtin: "内置",
6
+ skill: "技能",
7
+ custom: "自定义",
8
+ };
9
+ export function MentionPicker({ open, trigger, selectedAgent, items, index, onMove, onPick, }) {
10
+ const uid = useId();
11
+ const rootRef = useRef(null);
12
+ const shownRef = useRef(false);
13
+ const skillMode = Boolean(selectedAgent);
14
+ const mentionMode = trigger === "@";
15
+ useEffect(() => {
16
+ if (open && !shownRef.current) {
17
+ shownRef.current = true;
18
+ }
19
+ if (!open)
20
+ shownRef.current = false;
21
+ if (!open)
22
+ return;
23
+ const el = rootRef.current?.querySelector(`[data-opt="${index}"]`);
24
+ el?.scrollIntoView({ block: "nearest" });
25
+ }, [open, index]);
26
+ const optId = (i) => `${uid}-opt-${i}`;
27
+ const optionProps = (i) => ({
28
+ id: optId(i),
29
+ role: "option",
30
+ "data-opt": i,
31
+ "data-state": i === index ? "active" : "idle",
32
+ "aria-selected": i === index,
33
+ "aria-current": i === index,
34
+ onMouseEnter: () => onMove(i - index),
35
+ onClick: () => onPick(items[i]),
36
+ });
37
+ const mention = items.filter((i) => i.kind === "all" || i.kind === "agent");
38
+ const commands = items.filter((i) => i.kind === "command");
39
+ const files = items.filter((i) => i.kind === "file");
40
+ const gi = (item) => items.indexOf(item);
41
+ return (_jsx("div", { ref: rootRef, className: "ac-mention shadow-lg", "data-cmp": "ac/mention-menu", "data-trigger": trigger ?? undefined, "data-state": open ? "open" : "closed", hidden: !open, onMouseDown: (e) => e.preventDefault(), children: open ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "ac-mention__list", role: "listbox", "aria-label": skillMode
42
+ ? `@${selectedAgent?.name ?? ""} 的技能`
43
+ : mentionMode
44
+ ? "提及成员与文件"
45
+ : "斜杠命令", "aria-activedescendant": optId(index), children: [skillMode ? (_jsxs("div", { role: "group", "aria-labelledby": `${uid}-g-skill`, children: [_jsxs("div", { className: "ac-mention__label", id: `${uid}-g-skill`, children: ["@", selectedAgent?.name, " \u7684\u6280\u80FD \u00B7 Esc \u8FD4\u56DE"] }), commands.map((item) => {
46
+ if (item.kind !== "command")
47
+ return null;
48
+ const Icon = item.command.icon;
49
+ return (_jsxs("button", { type: "button", className: "ac-mention__cmd", "data-cmp": "ac/mention-skill-row", title: item.command.description, ...optionProps(gi(item)), children: [_jsx(Icon, { className: "ac-mention__cmd-icon", size: 16 }), _jsxs("span", { className: "ac-mention__cmd-body", children: [_jsx("span", { className: "ac-mention__cmd-label", children: item.command.label }), _jsx("span", { className: "ac-mention__cmd-desc", children: item.command.description })] }), _jsx("span", { className: "ac-mention__cmd-trigger", children: item.command.trigger }), _jsx("span", { className: "ac-mention__src", "data-src": "skill", children: "\u6280\u80FD" })] }, item.command.id));
50
+ })] })) : null, !skillMode && mention.length > 0 ? (_jsxs("div", { role: "group", children: [_jsx("div", { className: "ac-mention__label", children: "\u63D0\u53CA" }), mention.map((item) => {
51
+ if (item.kind === "all") {
52
+ return (_jsxs("button", { type: "button", className: "ac-mention__all", ...optionProps(gi(item)), children: [_jsx(Users, { size: 14 }), "\u6240\u6709\u4EBA"] }, "all"));
53
+ }
54
+ if (item.kind === "agent") {
55
+ return (_jsxs("button", { type: "button", className: "ac-mention__item", ...optionProps(gi(item)), children: [_jsx("span", { className: "ac-avatar", "data-size": "sm", "aria-hidden": "true", children: item.agent.badge || item.agent.name.slice(0, 1) }), item.agent.name] }, item.agent.id));
56
+ }
57
+ return null;
58
+ })] })) : null, !skillMode && commands.length > 0 ? (_jsxs("div", { role: "group", children: [_jsx("div", { className: "ac-mention__label", children: "\u547D\u4EE4" }), commands.map((item) => {
59
+ if (item.kind !== "command")
60
+ return null;
61
+ const Icon = item.command.icon;
62
+ return (_jsxs("button", { type: "button", className: "ac-mention__cmd", title: item.command.description, ...optionProps(gi(item)), children: [_jsx(Icon, { className: "ac-mention__cmd-icon", size: 16 }), _jsxs("span", { className: "ac-mention__cmd-body", children: [_jsx("span", { className: "ac-mention__cmd-label", children: item.command.label }), _jsx("span", { className: "ac-mention__cmd-desc", children: item.command.description })] }), _jsx("span", { className: "ac-mention__cmd-trigger", children: item.command.trigger }), _jsx("span", { className: "ac-mention__src", "data-src": item.command.source ?? "builtin", children: SOURCE_LABEL[item.command.source ?? "builtin"] })] }, item.command.id));
63
+ })] })) : null, !skillMode && files.length > 0 ? (_jsxs("div", { role: "group", children: [_jsx("div", { className: "ac-mention__label", children: "\u6587\u4EF6" }), files.map((item) => {
64
+ if (item.kind !== "file")
65
+ return null;
66
+ const Icon = item.entryKind === "dir" ? Folder : File;
67
+ return (_jsxs("button", { type: "button", className: "ac-mention__file", title: item.path, ...optionProps(gi(item)), children: [_jsx(Icon, { size: 14 }), _jsx("span", { className: "ac-mention__file-path", children: item.path })] }, item.path));
68
+ })] })) : null] }), _jsx("div", { className: "ac-mention__hint", children: skillMode
69
+ ? "↑↓ 选择 · Tab/Enter 确认 · Esc 返回"
70
+ : "↑↓ 选择 · Tab/Enter 确认 · Esc 关闭" })] })) : null }));
71
+ }
@@ -0,0 +1,7 @@
1
+ export interface PromptPopProps {
2
+ items: readonly string[];
3
+ onPick: (text: string) => void;
4
+ maxMarks?: number;
5
+ className?: string;
6
+ }
7
+ export declare function PromptPop({ items, onPick, maxMarks, className, }: PromptPopProps): import("react").JSX.Element | null;
@@ -0,0 +1,40 @@
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
+ export function PromptPop({ items, onPick, maxMarks = 8, className, }) {
5
+ const [open, setOpen] = useState(false);
6
+ const [hovered, setHovered] = useState(null);
7
+ const rootRef = useRef(null);
8
+ const closeTimer = useRef(0);
9
+ const marks = items.slice(0, maxMarks);
10
+ useEffect(() => {
11
+ return () => window.clearTimeout(closeTimer.current);
12
+ }, []);
13
+ useEffect(() => {
14
+ if (!open)
15
+ return;
16
+ const onKey = (e) => {
17
+ if (e.key === "Escape")
18
+ setOpen(false);
19
+ };
20
+ window.addEventListener("keydown", onKey);
21
+ return () => window.removeEventListener("keydown", onKey);
22
+ }, [open]);
23
+ if (items.length === 0)
24
+ return null;
25
+ const hold = () => {
26
+ window.clearTimeout(closeTimer.current);
27
+ setOpen(true);
28
+ };
29
+ const release = () => {
30
+ window.clearTimeout(closeTimer.current);
31
+ closeTimer.current = window.setTimeout(() => {
32
+ setOpen(false);
33
+ setHovered(null);
34
+ }, 120);
35
+ };
36
+ return (_jsxs("div", { ref: rootRef, className: cn("ac-prompt-pop", className), "data-cmp": "ai/prompt-pop", "data-id": "prompt-pop", "data-state": open ? "open" : "idle", onMouseEnter: hold, onMouseLeave: release, onFocus: hold, onBlur: release, children: [_jsx("button", { type: "button", className: "ac-prompt-pop__rail", "data-cmp": "ai/prompt-pop-rail", "aria-label": `历史提示词(${items.length} 条)`, "aria-expanded": open, onClick: () => setOpen((v) => !v), children: marks.map((_, i) => (_jsx("span", { className: "ac-prompt-pop__dash", "data-active": hovered === i ? "true" : "false", "data-latest": i === 0 ? "true" : "false", onMouseEnter: () => setHovered(i) }, i))) }), open ? (_jsxs("div", { className: "ac-prompt-pop__panel", "data-cmp": "ai/prompt-pop-panel", "data-id": "prompt-pop-panel", role: "listbox", "aria-label": "\u5386\u53F2\u63D0\u793A\u8BCD", children: [_jsx("div", { className: "ac-prompt-pop__head", children: "\u5386\u53F2\u63D0\u793A\u8BCD" }), items.map((text, i) => (_jsxs("button", { type: "button", role: "option", "aria-selected": false, "data-cmp": "ai/prompt-pop-item", className: "ac-prompt-pop__row", title: text, onMouseEnter: () => setHovered(i), onClick: () => {
37
+ onPick(text);
38
+ setOpen(false);
39
+ }, children: [_jsx("span", { className: "ac-prompt-pop__text", children: text }), _jsx("span", { className: "ac-prompt-pop__marker", "aria-hidden": "true" })] }, `${i}-${text.slice(0, 12)}`)))] })) : null] }));
40
+ }
@@ -0,0 +1,14 @@
1
+ import type { QueuedPrompt } from "../../types/conversation";
2
+ export declare const MAX_QUEUED_PROMPTS = 8;
3
+ export declare const QUEUED_FULL_NOTICE = "\u5F85\u53D1\u961F\u5217\u5DF2\u6EE1\uFF088 \u6761\uFF09\uFF0C\u8BF7\u5148\u5904\u7406";
4
+ export interface QueuedPromptsProps {
5
+ items: readonly QueuedPrompt[];
6
+ canSendNow: boolean;
7
+ onMoveUp: (id: string) => void;
8
+ onMoveDown: (id: string) => void;
9
+ onSendNow: (id: string) => void;
10
+ onEdit: (id: string) => void;
11
+ onRemove: (id: string) => void;
12
+ onClear?: () => void;
13
+ }
14
+ export declare function QueuedPrompts({ items, canSendNow, onMoveUp, onMoveDown, onSendNow, onEdit, onRemove, onClear, }: QueuedPromptsProps): import("react").JSX.Element | null;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { ArrowDown, ArrowUp, ChevronDown, GripVertical, ListOrdered, Paperclip, Pencil, Send, Trash2, X, } from "lucide-react";
4
+ export const MAX_QUEUED_PROMPTS = 8;
5
+ export const QUEUED_FULL_NOTICE = `待发队列已满(${MAX_QUEUED_PROMPTS} 条),请先处理`;
6
+ function ActBtn({ label, disabled, onClick, accent, children, }) {
7
+ return (_jsx("button", { type: "button", "data-cmp": "ac/queued-action", "data-action": label, "data-state": disabled ? "disabled" : "idle", title: label, "aria-label": label, disabled: disabled, className: accent
8
+ ? "ac-queued-row__act ac-queued-row__act--accent"
9
+ : "ac-queued-row__act", onClick: onClick, children: children }));
10
+ }
11
+ export function QueuedPrompts({ items, canSendNow, onMoveUp, onMoveDown, onSendNow, onEdit, onRemove, onClear, }) {
12
+ const [open, setOpen] = useState(true);
13
+ if (items.length === 0)
14
+ return null;
15
+ const full = items.length >= MAX_QUEUED_PROMPTS;
16
+ return (_jsxs("div", { className: "ac-queued", "data-cmp": "ac/queued-list", "data-count": items.length, "data-state": open ? "open" : full ? "full" : "idle", role: "list", "aria-label": "\u5F85\u53D1\u961F\u5217", children: [items.length > 1 ? (_jsxs("div", { className: "ac-queued__head", children: [_jsxs("button", { type: "button", "data-cmp": "ac/queued-toggle", className: "ac-queued__pill", "aria-expanded": open, onClick: () => setOpen((v) => !v), title: open ? "收起待发队列" : "展开待发队列", children: [_jsx(ListOrdered, { className: "ac-queued__head-icon" }), _jsx("span", { className: "ac-queued__head-title", children: "\u5F85\u53D1" }), _jsx("span", { className: "ac-queued__badge", "data-state": full ? "full" : "idle", title: `${items.length}/${MAX_QUEUED_PROMPTS}`, children: items.length }), !open ? (_jsx("span", { className: "ac-queued__preview", children: items[0]?.text })) : null, _jsx(ChevronDown, { className: "ac-queued__chev", "data-open": open ? "true" : "false" })] }), onClear ? (_jsx("button", { type: "button", "data-cmp": "ac/queued-clear", className: "ac-queued__clear", onClick: onClear, title: "\u6E05\u7A7A\u5F85\u53D1\u961F\u5217", children: _jsx(Trash2, { size: 11 }) })) : null] })) : null, open ? (_jsx("div", { className: "ac-queued__body", children: items.map((item, i) => {
17
+ const first = i === 0;
18
+ const last = i === items.length - 1;
19
+ return (_jsxs("div", { className: "ac-queued-row", "data-cmp": "ac/queued-row", "data-state": first ? "head" : "waiting", role: "listitem", children: [_jsx(GripVertical, { className: "ac-queued-row__grip", "aria-hidden": "true", size: 12 }), _jsxs("div", { className: "ac-queued-row__text", children: [_jsx("span", { className: "ac-queued-row__content", title: item.text, children: item.text }), (item.attachments?.length ?? 0) > 0 ? (_jsxs("span", { className: "ac-queued-row__att", title: `含 ${item.attachments?.length} 个附件`, children: [_jsx(Paperclip, { size: 10 }), item.attachments?.length] })) : null] }), _jsxs("div", { className: "ac-queued-row__acts", children: [canSendNow ? (_jsx(ActBtn, { label: "\u7ACB\u5373\u53D1\u9001", accent: true, onClick: () => onSendNow(item.id), children: _jsx(Send, { size: 11 }) })) : null, _jsx(ActBtn, { label: "\u4E0A\u79FB", disabled: first, onClick: () => onMoveUp(item.id), children: _jsx(ArrowUp, { size: 11 }) }), _jsx(ActBtn, { label: "\u4E0B\u79FB", disabled: last, onClick: () => onMoveDown(item.id), children: _jsx(ArrowDown, { size: 11 }) }), _jsx(ActBtn, { label: "\u53D6\u56DE\u7F16\u8F91", onClick: () => onEdit(item.id), children: _jsx(Pencil, { size: 11 }) }), _jsx(ActBtn, { label: "\u79FB\u51FA\u961F\u5217", onClick: () => onRemove(item.id), children: _jsx(X, { size: 11 }) })] })] }, item.id));
20
+ }) })) : null] }));
21
+ }
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from "react";
2
+ export interface SlingButtonProps {
3
+ onSend: () => void;
4
+ disabled?: boolean;
5
+ children?: ReactNode;
6
+ className?: string;
7
+ size?: number;
8
+ ariaLabel?: string;
9
+ }
10
+ export declare function SlingButton({ onSend, disabled, children, className, size, ariaLabel, }: SlingButtonProps): import("react").JSX.Element;
@@ -0,0 +1,112 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState, } from "react";
3
+ import { ArrowUp } from "lucide-react";
4
+ import { cn } from "../../utils/cn";
5
+ const ARM_AT = 36;
6
+ const MAX_PULL = 96;
7
+ export function SlingButton({ onSend, disabled = false, children, className, size = 44, ariaLabel = "发送", }) {
8
+ const wellRef = useRef(null);
9
+ const canvasRef = useRef(null);
10
+ const [pull, setPull] = useState(0);
11
+ const [angle, setAngle] = useState(-Math.PI / 2);
12
+ const pulling = useRef(false);
13
+ const origin = useRef({ x: 0, y: 0 });
14
+ const particles = useRef([]);
15
+ const raf = useRef(0);
16
+ const fire = useCallback(() => {
17
+ const canvas = canvasRef.current;
18
+ if (canvas) {
19
+ const ctx = canvas.getContext("2d");
20
+ if (ctx) {
21
+ const cx = size / 2;
22
+ const cy = size / 2;
23
+ for (let i = 0; i < 14; i++) {
24
+ const a = -Math.PI / 2 + (Math.random() - 0.5) * (Math.PI / 1.5);
25
+ const sp = 1.6 + Math.random() * 3;
26
+ particles.current.push({
27
+ x: cx,
28
+ y: cy,
29
+ vx: Math.cos(a) * sp,
30
+ vy: Math.sin(a) * sp,
31
+ r: 1 + Math.random() * 2,
32
+ life: 1,
33
+ });
34
+ }
35
+ }
36
+ }
37
+ onSend();
38
+ setPull(0);
39
+ const el = wellRef.current;
40
+ if (el) {
41
+ el.animate([
42
+ { transform: "scale(0.82)" },
43
+ { transform: "scale(1.08)" },
44
+ { transform: "scale(1)" },
45
+ ], { duration: 340, easing: "cubic-bezier(0.22, 1, 0.36, 1)" });
46
+ }
47
+ }, [onSend, size]);
48
+ useEffect(() => {
49
+ const canvas = canvasRef.current;
50
+ if (!canvas)
51
+ return;
52
+ const ctx = canvas.getContext("2d");
53
+ if (!ctx)
54
+ return;
55
+ const tick = () => {
56
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
57
+ particles.current = particles.current.filter((p) => p.life > 0);
58
+ for (const p of particles.current) {
59
+ p.x += p.vx;
60
+ p.y += p.vy;
61
+ p.vy += 0.05;
62
+ p.life -= 0.035;
63
+ ctx.globalAlpha = Math.max(0, p.life);
64
+ ctx.fillStyle = "var(--accent-primary)";
65
+ ctx.beginPath();
66
+ ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
67
+ ctx.fill();
68
+ }
69
+ ctx.globalAlpha = 1;
70
+ raf.current = requestAnimationFrame(tick);
71
+ };
72
+ raf.current = requestAnimationFrame(tick);
73
+ return () => cancelAnimationFrame(raf.current);
74
+ }, []);
75
+ const onPointerDown = (e) => {
76
+ if (disabled)
77
+ return;
78
+ pulling.current = true;
79
+ origin.current = { x: e.clientX, y: e.clientY };
80
+ e.currentTarget.setPointerCapture(e.pointerId);
81
+ };
82
+ const onPointerMove = (e) => {
83
+ if (!pulling.current)
84
+ return;
85
+ const dx = e.clientX - origin.current.x;
86
+ const dy = e.clientY - origin.current.y;
87
+ const dist = Math.min(Math.hypot(dx, dy), MAX_PULL);
88
+ setPull(dist);
89
+ setAngle(Math.atan2(dy, dx));
90
+ };
91
+ const onPointerUp = () => {
92
+ if (!pulling.current)
93
+ return;
94
+ pulling.current = false;
95
+ if (pull >= ARM_AT)
96
+ fire();
97
+ else {
98
+ setPull(0);
99
+ if (pull > 0)
100
+ onSend();
101
+ else
102
+ onSend();
103
+ }
104
+ };
105
+ const pct = Math.min(1, pull / MAX_PULL);
106
+ const arcR = size / 2 - 2;
107
+ return (_jsxs("button", { ref: wellRef, type: "button", "data-cmp": "ai/sling-button", "data-id": "send-btn", "data-state": disabled ? "disabled" : pull > 0 ? "pulling" : "idle", "aria-label": ariaLabel, title: ariaLabel, disabled: disabled, className: cn("relative touch-none select-none rounded-full", disabled && "opacity-40", className), style: { width: size, height: size }, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, children: [_jsx("canvas", { ref: canvasRef, width: size * 2, height: size * 2, className: "pointer-events-none absolute inset-0", style: { width: size, height: size } }), _jsxs("svg", { className: "pointer-events-none absolute inset-0", width: size, height: size, "aria-hidden": "true", children: [_jsx("circle", { cx: size / 2, cy: size / 2, r: arcR, fill: "none", stroke: "var(--border-600)", strokeWidth: 2 }), pct > 0 ? (_jsx("circle", { cx: size / 2, cy: size / 2, r: arcR, fill: "none", stroke: "var(--accent-primary)", strokeWidth: 2.5, strokeLinecap: "round", strokeDasharray: 2 * Math.PI * arcR, strokeDashoffset: 2 * Math.PI * arcR * (1 - pct), transform: `rotate(-90 ${size / 2} ${size / 2})` })) : null] }), _jsx("span", { className: "absolute inset-[5px] grid place-items-center rounded-full transition-transform", style: {
108
+ background: "var(--primary)",
109
+ color: "var(--primary-foreground)",
110
+ transform: `translate(${Math.cos(angle) * pull * 0.28}px, ${Math.sin(angle) * pull * 0.28}px) scale(${1 - pct * 0.12})`,
111
+ }, children: children ?? _jsx(ArrowUp, { className: "size-[42%]" }) })] }));
112
+ }
@@ -0,0 +1,7 @@
1
+ export { Composer, ComposerAttachmentsStrip, ComposerGrip, ComposerInput, ComposerModelChip, ComposerPermChip, ComposerToolbar, MentionPicker, QueuedPrompts, VisionTag, focusFieldAt, } from "./Composer";
2
+ export type { ComposerProps, ComposerPaletteLike, ComposerSubmitApi, QueuedPromptsApi, StagedAttachment, ComposerModelSwitch, PermissionModeSwitch, } from "./Composer";
3
+ export type { ComposerInputProps } from "./ComposerInput";
4
+ export type { ComposerToolbarProps } from "./ComposerToolbar";
5
+ export type { MentionPickerProps } from "./MentionPicker";
6
+ export type { ComposerAttachmentsStripProps } from "./ComposerAttachments";
7
+ export type { QueuedPromptsProps } from "./QueuedPrompts";
@@ -0,0 +1 @@
1
+ export { Composer, ComposerAttachmentsStrip, ComposerGrip, ComposerInput, ComposerModelChip, ComposerPermChip, ComposerToolbar, MentionPicker, QueuedPrompts, VisionTag, focusFieldAt, } from "./Composer";
@@ -0,0 +1,4 @@
1
+ export interface ChatEmptySceneProps {
2
+ onStart?: () => void;
3
+ }
4
+ export declare function ChatEmptyScene({ onStart }: ChatEmptySceneProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MessageSquarePlus, Sparkles, Users } from "lucide-react";
3
+ import { Button } from "../../primitives/Button";
4
+ const START_POINTS = [
5
+ {
6
+ icon: Sparkles,
7
+ title: "让 Agent 干活",
8
+ hint: "描述目标,Agent 自己拆步骤、动手、汇报结果",
9
+ },
10
+ {
11
+ icon: Users,
12
+ title: "多 Agent 协作",
13
+ hint: "@ 提及不同角色,让专家们一起把事办成",
14
+ },
15
+ ];
16
+ export function ChatEmptyScene({ onStart }) {
17
+ return (_jsxs("div", { className: "ac-empty-scene", "data-cmp": "ac/chat-empty-scene", "data-state": "empty", children: [_jsx("span", { className: "ac-empty-scene__mark", children: _jsx(MessageSquarePlus, { size: 22 }) }), _jsx("p", { className: "ac-empty-scene__title", children: "\u5F00\u59CB\u4E00\u6BB5\u5BF9\u8BDD" }), _jsx("p", { className: "ac-empty-scene__hint", children: "\u4ECE\u5DE6\u4FA7\u9009\u4E00\u4E2A\u4F1A\u8BDD\u7EE7\u7EED\uFF0C\u6216\u65B0\u5EFA\u4E00\u4E2A\u2014\u2014\u4F60\u7684 Agent \u4F19\u4F34\u5728\u8FD9\u91CC\u7B49\u4F60\u3002" }), onStart ? (_jsx(Button, { className: "ac-empty-scene__cta", "data-cmp": "ac/chat-empty-start", onClick: onStart, children: "\u65B0\u5EFA\u4F1A\u8BDD" })) : null, _jsx("ul", { className: "ac-empty-scene__points", children: START_POINTS.map((point) => {
18
+ const Icon = point.icon;
19
+ return (_jsxs("li", { className: "ac-empty-scene__point", children: [_jsx("span", { className: "ac-empty-scene__point-icon", children: _jsx(Icon, { size: 14 }) }), _jsxs("span", { className: "ac-empty-scene__point-body", children: [_jsx("span", { className: "ac-empty-scene__point-title", children: point.title }), _jsx("span", { className: "ac-empty-scene__point-hint", children: point.hint })] })] }, point.title));
20
+ }) })] }));
21
+ }
@@ -0,0 +1,13 @@
1
+ import type { Agent } from "../../types/agent";
2
+ export interface ChatHeaderProps {
3
+ title: string;
4
+ subtitle: string;
5
+ members?: readonly Agent[];
6
+ timeLabel?: string;
7
+ detailOpen: boolean;
8
+ showBack: boolean;
9
+ onBack: () => void;
10
+ onOpenDetail: () => void;
11
+ onSearch?: () => void;
12
+ }
13
+ export declare function ChatHeader({ title, subtitle, members, timeLabel, detailOpen, showBack, onBack, onOpenDetail, onSearch, }: ChatHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { ArrowLeft, Info, Search } from "lucide-react";
3
+ import { Button } from "../../primitives/Button";
4
+ import { Tooltip } from "../../primitives/Tooltip";
5
+ const AVATAR_STACK_MAX = 4;
6
+ function AvatarStack({ members }) {
7
+ const shown = members.slice(0, AVATAR_STACK_MAX);
8
+ const overflow = members.length - shown.length;
9
+ return (_jsxs("div", { className: "ac-id-avatars", "data-cmp": "ac/chat-header-avatars", "aria-label": `成员 ${members.length} 位`, children: [overflow > 0 ? (_jsxs("span", { className: "ac-id-avatars__more", title: `还有 ${overflow} 位成员`, children: ["+", overflow] })) : null, shown.map((member, index) => (_jsx("span", { className: "ac-id-avatars__item", title: member.name, children: _jsx("span", { className: "ac-avatar", "data-kind": "agent", "data-size": "sm", "data-show-status": index === 0 ? "true" : undefined, "aria-hidden": "true", children: member.badge || member.name.slice(0, 1) }) }, member.id)))] }));
10
+ }
11
+ function IdentityMeta({ models, timeLabel, }) {
12
+ if (models.length === 0 && !timeLabel)
13
+ return null;
14
+ return (_jsxs("div", { className: "ac-id-meta", "data-cmp": "ac/chat-header-meta", children: [models.slice(0, 3).map((model) => (_jsx("span", { className: "ac-id-meta__chip", children: model }, model))), models.length > 3 ? (_jsxs("span", { className: "ac-id-meta__chip", children: ["+", models.length - 3, " \u6A21\u578B"] })) : null, timeLabel ? (_jsx("span", { className: "ac-id-meta__time", children: timeLabel })) : null] }));
15
+ }
16
+ export function ChatHeader({ title, subtitle, members, timeLabel, detailOpen, showBack, onBack, onOpenDetail, onSearch, }) {
17
+ const isGroup = (members?.length ?? 0) > 1;
18
+ const models = Array.from(new Set((members ?? []).map((m) => m.model).filter(Boolean)));
19
+ return (_jsxs("header", { className: "ac-chat__head", "data-cmp": "ac/chat-header", "data-state": detailOpen ? "open" : "closed", children: [showBack ? (_jsx(Button, { variant: "ghost", size: "icon-sm", className: "ac-icon-btn ac-chat__back", "aria-label": "\u8FD4\u56DE\u4F1A\u8BDD\u5217\u8868", onClick: onBack, children: _jsx(ArrowLeft, { size: 15 }) })) : null, _jsxs("div", { className: "ac-chat__titles", children: [isGroup && members ? _jsx(AvatarStack, { members: members }) : null, _jsxs("div", { className: "ac-chat__headings", children: [_jsx("div", { className: "ac-chat__title", children: title }), _jsxs("div", { className: "ac-chat__subline", children: [_jsx("span", { className: "ac-chat__sub", children: subtitle }), _jsx(IdentityMeta, { models: models, timeLabel: timeLabel })] })] })] }), _jsx("span", { className: "ac-chat__grow" }), _jsxs("div", { className: "ac-chat__actions", children: [onSearch ? (_jsx(Tooltip, { content: "\u641C\u7D22\u804A\u5929\uFF08\u2318K\uFF09", children: _jsx(Button, { variant: "ghost", size: "icon-sm", className: "ac-icon-btn", "aria-label": "\u641C\u7D22\u804A\u5929\u8BB0\u5F55", onClick: onSearch, children: _jsx(Search, { size: 15 }) }) })) : null, _jsx(Button, { variant: "ghost", size: "icon-sm", className: "ac-icon-btn", "aria-label": "\u4F1A\u8BDD\u4FE1\u606F", "data-state": detailOpen ? "active" : "idle", onClick: onOpenDetail, children: _jsx(Info, { size: 15 }) })] })] }));
20
+ }
@@ -0,0 +1,90 @@
1
+ import { type ReactNode } from "react";
2
+ import type { Agent } from "../../types/agent";
3
+ import type { Attachment, ChatMessage } from "../../types/message";
4
+ import type { Conversation, ConversationStatus, PendingAsk, PendingPermission } from "../../types/conversation";
5
+ import type { ComposerModelSwitch, ComposerPaletteLike, ComposerSubmitApi, PermissionModeSwitch, QueuedPromptsApi, SlashCommand } from "../../types/composer";
6
+ import type { SelectionTarget } from "../../types/callbacks";
7
+ import { type MessageStreamProps } from "../message/MessageStream";
8
+ import { type TodoState } from "../cards/TodoCard";
9
+ export type SmartStopPlan = {
10
+ kind: "restore";
11
+ draft: string;
12
+ } | {
13
+ kind: "settle";
14
+ };
15
+ export declare function planSmartStop(messages: readonly ChatMessage[]): SmartStopPlan;
16
+ export interface ChatPaneProps {
17
+ conversation: Conversation | null;
18
+ members: Agent[];
19
+ messages: ChatMessage[];
20
+ status: ConversationStatus;
21
+ draft: string;
22
+ onDraftChange: (v: string) => void;
23
+ onSend: (text: string, attachments?: Attachment[]) => void;
24
+ onOpenDetail: () => void;
25
+ onOpenAgent?: (id: string) => void;
26
+ onBack: () => void;
27
+ onStop?: () => void;
28
+ detailOpen?: boolean;
29
+ onSearch?: () => void;
30
+ permissions?: PendingPermission[];
31
+ onRespondPermission?: (permissionId: string, approved: boolean, remembered: boolean) => void;
32
+ pendingAsk?: PendingAsk | null;
33
+ askQueued?: number;
34
+ onAnswerAsk?: (callId: string, answer: string) => void;
35
+ onSkipAsk?: (callId: string) => void;
36
+ onNotice?: (message: string) => void;
37
+ onNewConversation?: () => void;
38
+ onOpenProfile?: () => void;
39
+ modelSwitch?: ComposerModelSwitch;
40
+ permissionMode?: PermissionModeSwitch;
41
+ onMic?: () => void;
42
+ micState?: "idle" | "listening";
43
+ toolbarTrailing?: ReactNode;
44
+ onContextWindowChange?: (window: number) => void;
45
+ slashExtra?: readonly SlashCommand[];
46
+ slashExpand?: (draft: string) => string | null;
47
+ onFavoriteSelection?: (target: SelectionTarget) => void;
48
+ selectionActive?: boolean;
49
+ selectedIds?: string[];
50
+ onToggleSelect?: (messageId: string) => void;
51
+ onBatchCopy?: () => void;
52
+ onBatchRemove?: () => void;
53
+ batchBusy?: boolean;
54
+ onExitSelection?: () => void;
55
+ messageActions?: MessageStreamProps["messageActions"];
56
+ messageArtifacts?: MessageStreamProps["messageArtifacts"];
57
+ selectionPop?: {
58
+ left: number;
59
+ top: number;
60
+ target: SelectionTarget;
61
+ } | null;
62
+ onDismissSelectionPop?: () => void;
63
+ onInsertSelectionText?: (text: string) => void;
64
+ composer?: {
65
+ palette: ComposerPaletteLike;
66
+ submission: ComposerSubmitApi;
67
+ queued?: QueuedPromptsApi;
68
+ onArrowUp?: () => boolean;
69
+ onArrowDown?: () => boolean;
70
+ attachmentPicker?: ReactNode;
71
+ attachmentHeader?: ReactNode;
72
+ stagedAttachments?: import("../composer/Composer").StagedAttachment[];
73
+ onRemoveAttachment?: (id: string) => void;
74
+ dragHandlers?: {
75
+ onPaste?: (e: React.ClipboardEvent) => void;
76
+ onDragOver?: (e: React.DragEvent) => void;
77
+ onDrop?: (e: React.DragEvent) => void;
78
+ };
79
+ visionTag?: ReactNode;
80
+ slashExtra?: readonly SlashCommand[];
81
+ };
82
+ todoState?: TodoState | null;
83
+ promptPop?: {
84
+ items: readonly string[];
85
+ onPick: (text: string) => void;
86
+ };
87
+ subtitle?: string;
88
+ children?: ReactNode;
89
+ }
90
+ export declare function ChatPane(props: ChatPaneProps): import("react").JSX.Element;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { capabilitiesOf } from "../../domain/selection-gates";
4
+ import { Composer } from "../composer/Composer";
5
+ import { PromptPop } from "../composer/PromptPop";
6
+ import { MessageStream, } from "../message/MessageStream";
7
+ import { SelectionActions } from "../selection/SelectionActions";
8
+ import { SelectionBar } from "../selection/SelectionBar";
9
+ import { TodoCard } from "../cards/TodoCard";
10
+ import { ChatEmptyScene } from "./ChatEmptyScene";
11
+ import { ChatHeader } from "./ChatHeader";
12
+ export function planSmartStop(messages) {
13
+ for (let i = messages.length - 1; i >= 0; i--) {
14
+ const m = messages[i];
15
+ if (m.role === "user") {
16
+ return { kind: "restore", draft: m.text };
17
+ }
18
+ if ((m.role === "assistant" && m.text.trim().length > 0) ||
19
+ (m.parts && m.parts.length > 0)) {
20
+ return { kind: "settle" };
21
+ }
22
+ }
23
+ return { kind: "settle" };
24
+ }
25
+ export function ChatPane(props) {
26
+ const { conversation, members, messages, status, draft, onDraftChange, onSend, onOpenDetail, onOpenAgent, onBack, onStop, detailOpen = false, onSearch, permissions, onRespondPermission, pendingAsk, askQueued, onAnswerAsk, onSkipAsk, onNotice, onNewConversation, onOpenProfile, modelSwitch, permissionMode, onMic, micState, toolbarTrailing, onContextWindowChange, slashExtra, slashExpand, onFavoriteSelection, selectionActive = false, selectedIds = [], onToggleSelect, onBatchCopy, onBatchRemove, batchBusy = false, onExitSelection, messageActions, messageArtifacts, selectionPop, onDismissSelectionPop, onInsertSelectionText, composer, todoState, promptPop, subtitle, children, } = props;
27
+ const lastContextUsage = useMemo(() => {
28
+ for (let i = messages.length - 1; i >= 0; i--) {
29
+ const u = messages[i].contextUsage;
30
+ if (u && u.window > 0)
31
+ return u;
32
+ }
33
+ return undefined;
34
+ }, [messages]);
35
+ if (!conversation) {
36
+ return (_jsx("section", { className: "ac-chat", "data-cmp": "ac/chat", "data-state": "idle", children: _jsx(ChatEmptyScene, { onStart: onNewConversation }) }));
37
+ }
38
+ const capabilities = capabilitiesOf(messages, selectedIds);
39
+ const handleSmartStop = () => {
40
+ if (!onStop)
41
+ return;
42
+ const plan = planSmartStop(messages);
43
+ if (plan.kind === "restore" && draft.trim().length === 0) {
44
+ onDraftChange(plan.draft);
45
+ onNotice?.("已停止:输入框已还原上一条消息,可修改后重发");
46
+ }
47
+ onStop();
48
+ };
49
+ return (_jsxs("section", { className: "ac-chat", "data-cmp": "ac/chat", "data-state": status, children: [_jsx(ChatHeader, { title: conversation.title, subtitle: subtitle ?? `${members.length} 位成员`, members: members, timeLabel: conversation.timeLabel, detailOpen: detailOpen, showBack: false, onBack: onBack, onOpenDetail: onOpenDetail, onSearch: onSearch }), _jsx(TodoCard, { state: todoState ?? null }), _jsxs("div", { className: "ac-chat__main", children: [_jsx(MessageStream, { messages: messages, members: members, status: status, onOpenAgent: onOpenAgent, permissions: permissions, onRespondPermission: onRespondPermission, messageActions: messageActions, messageArtifacts: messageArtifacts, selectMode: selectionActive, selectedIds: new Set(selectedIds), onToggleSelect: onToggleSelect }), promptPop ? (_jsx(PromptPop, { items: promptPop.items, onPick: promptPop.onPick, className: "ac-chat__prompt-pop" })) : null] }), selectionActive ? (_jsx(SelectionBar, { count: selectedIds.length, capabilities: {
50
+ copy: capabilities.copy && Boolean(onBatchCopy),
51
+ remove: capabilities.remove && Boolean(onBatchRemove),
52
+ reason: {
53
+ ...capabilities.reason,
54
+ ...(onBatchCopy ? {} : { copy: "未接线:批量复制不可用" }),
55
+ ...(onBatchRemove ? {} : { remove: "未接线:批量删除不可用" }),
56
+ },
57
+ }, busy: batchBusy, onCopy: () => onBatchCopy?.(), onRemove: () => onBatchRemove?.(), onExit: () => onExitSelection?.() })) : composer ? (_jsx(Composer, { value: draft, onChange: onDraftChange, members: members, status: status, conversationId: conversation.id, onSend: onSend, onStop: onStop ? handleSmartStop : undefined, onNotice: onNotice, onNewConversation: onNewConversation, onOpenProfile: onOpenProfile, modelSwitch: modelSwitch, permissionMode: permissionMode, slashExtra: slashExtra ?? composer.slashExtra, slashExpand: slashExpand, contextUsage: lastContextUsage
58
+ ? { ...lastContextUsage, onWindowChange: onContextWindowChange }
59
+ : undefined, pendingAsk: pendingAsk, askQueued: askQueued, onAnswerAsk: onAnswerAsk, onSkipAsk: onSkipAsk, palette: composer.palette, submission: composer.submission, queued: composer.queued, onArrowUp: composer.onArrowUp, onArrowDown: composer.onArrowDown, attachmentPicker: composer.attachmentPicker, attachmentHeader: composer.attachmentHeader, stagedAttachments: composer.stagedAttachments, onRemoveAttachment: composer.onRemoveAttachment, dragHandlers: composer.dragHandlers, visionTag: composer.visionTag, onMic: onMic, micState: micState, toolbarTrailing: toolbarTrailing })) : null, !selectionActive ? (_jsx(SelectionActions, { pop: selectionPop ?? null, onDismiss: () => onDismissSelectionPop?.(), onInsert: (text) => onInsertSelectionText?.(text), onFavorite: onFavoriteSelection })) : null, children] }));
60
+ }
@@ -0,0 +1,11 @@
1
+ import { type ComponentProps, type ReactNode } from "react";
2
+ export declare const NEAR_BOTTOM_PX = 80;
3
+ export declare function ConversationShell({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
4
+ export declare function ConversationContent({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
5
+ export declare function ConversationScroller({ className, children, ...props }: ComponentProps<"div">): import("react").JSX.Element;
6
+ export interface ConversationEmptyProps extends ComponentProps<"div"> {
7
+ title: string;
8
+ hint?: string;
9
+ children?: ReactNode;
10
+ }
11
+ export declare function ConversationEmpty({ title, hint, className, ...props }: ConversationEmptyProps): import("react").JSX.Element;