@spzxg/cli-ui 0.1.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -14
- package/dist/{cli-ui-embed-BeBZKDZO.js → cli-ui-embed-C0F64Ln7.js} +13493 -12058
- package/dist/{cli-ui-index-CuwqVeUk.js → cli-ui-index--RVPCGIc.js} +1 -1
- package/dist/cli-ui.css +1 -1
- package/dist/cli-ui.js +15 -9
- package/package.json +1 -1
- package/src/board/BrowserPane.tsx +418 -0
- package/src/board/ChangesPane.tsx +142 -0
- package/src/board/CliBoard.tsx +246 -0
- package/src/board/FileViewer.tsx +257 -0
- package/src/board/FilesPane.tsx +109 -0
- package/src/board/blocks.tsx +96 -0
- package/src/board/fileKind.ts +81 -0
- package/src/chat/CliChat.tsx +208 -65
- package/src/core/client.ts +224 -12
- package/src/core/config.ts +4 -2
- package/src/core/conversation.ts +186 -17
- package/src/core/provider.tsx +33 -19
- package/src/core/store.ts +169 -84
- package/src/core/types.ts +142 -4
- package/src/elements.tsx +51 -7
- package/src/embed.ts +15 -4
- package/src/index.ts +11 -5
- package/src/msglist/CliMsgList.tsx +129 -26
- package/src/styles.css +124 -0
- package/src/ui/Markdown.tsx +35 -6
- package/src/ui/fileLinks.ts +46 -0
- package/src/ui/transcriptModel.ts +19 -1
package/src/core/types.ts
CHANGED
|
@@ -46,6 +46,7 @@ export interface CenterTurn {
|
|
|
46
46
|
export interface CenterTask {
|
|
47
47
|
id: string;
|
|
48
48
|
job_id: string;
|
|
49
|
+
seq?: number;
|
|
49
50
|
title: string;
|
|
50
51
|
status: string;
|
|
51
52
|
agent: string | null;
|
|
@@ -57,9 +58,30 @@ export interface CenterTask {
|
|
|
57
58
|
cache_creation_tokens: number;
|
|
58
59
|
browser?: boolean;
|
|
59
60
|
last_error: string | null;
|
|
61
|
+
/** 接续出来的对话:指回旧对话任务里对应的那个对话。 */
|
|
62
|
+
continue_from_task_id?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 对话任务里的一个对话(一个子任务、一个 Agent 会话)。conversation.conversations 的一项。 */
|
|
66
|
+
export interface ConversationInfo {
|
|
67
|
+
task_id: string;
|
|
68
|
+
seq: number;
|
|
69
|
+
title: string;
|
|
70
|
+
status: string;
|
|
71
|
+
/** 关掉了(单独关闭这一个,或整个对话任务结束)。 */
|
|
72
|
+
closed: boolean;
|
|
73
|
+
turns: number;
|
|
74
|
+
queued_turns: number;
|
|
75
|
+
running_turn: boolean;
|
|
76
|
+
last_turn: CenterTurn | null;
|
|
77
|
+
remote_task_id: string | null;
|
|
78
|
+
continue_from_task_id: string | null;
|
|
79
|
+
created_at: string | null;
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
export interface ConversationSummary {
|
|
83
|
+
/** 里面的每个对话(按开的先后)。旧版 center 没有这个字段:当作只有一个对话。 */
|
|
84
|
+
conversations?: ConversationInfo[];
|
|
63
85
|
turns: number;
|
|
64
86
|
queued_turns: number;
|
|
65
87
|
running_turn: boolean;
|
|
@@ -81,9 +103,11 @@ export interface CenterJob {
|
|
|
81
103
|
tasks: CenterTask[];
|
|
82
104
|
}
|
|
83
105
|
|
|
84
|
-
/** POST /api/jobs/:id/messages */
|
|
106
|
+
/** POST /api/jobs/:id/messages、POST /api/jobs/:id/conversations */
|
|
85
107
|
export interface SendResult {
|
|
86
108
|
job_id: string;
|
|
109
|
+
/** 实际接收这条消息的对话(对话任务过期接续后是新对话任务里对应的那个)。 */
|
|
110
|
+
task_id?: string;
|
|
87
111
|
status: JobStatus;
|
|
88
112
|
duplicate: boolean;
|
|
89
113
|
/** true = 原对话已结束,这条消息开了一个新对话(job_id 是新的)。 */
|
|
@@ -199,19 +223,61 @@ export type NodeEvent =
|
|
|
199
223
|
|
|
200
224
|
/** center 实时流 chunk.events 里的一项。 */
|
|
201
225
|
export type StreamItem =
|
|
202
|
-
|
|
203
|
-
| { type: "
|
|
226
|
+
/** remote_task_id:一次性任务的流里才有(大任务里可能同时有几个节点对话)。 */
|
|
227
|
+
| { type: "transcript"; remote_task_id?: string; messages: Message[] | null; pending: PendingMessage[] | null; running: boolean | null }
|
|
228
|
+
| { type: "node"; remote_task_id?: string; event: NodeEvent }
|
|
204
229
|
| { type: "turn"; turn: CenterTurn }
|
|
205
|
-
| { type: "job"; job: { id: string; status: JobStatus; closed_reason
|
|
230
|
+
| { type: "job"; job: { id: string; status: JobStatus; closed_reason?: string | null; node_id: string | null } }
|
|
231
|
+
/** 正在看的这个对话的状态变化(关闭了:closed=true);一次性任务的流里是子任务(BatchStreamTask)。 */
|
|
232
|
+
| { type: "task"; task: StreamTask | BatchStreamTask };
|
|
233
|
+
|
|
234
|
+
/** 实时流看的那个对话(GET /api/jobs/:id/stream?task=)。 */
|
|
235
|
+
export interface StreamTask { id: string; seq: number; title: string; status: string; closed: boolean; remote_task_id: string | null; continue_from_task_id: string | null }
|
|
206
236
|
|
|
207
237
|
export interface StreamSnapshot {
|
|
208
238
|
job: { id: string; status: JobStatus; closed_reason: string | null; node_id: string | null; mode: string; title: string; conversation: ConversationSummary };
|
|
239
|
+
/** 旧版 center 没有。 */
|
|
240
|
+
task?: StreamTask;
|
|
209
241
|
turns: CenterTurn[];
|
|
210
242
|
}
|
|
211
243
|
|
|
244
|
+
/**
|
|
245
|
+
* 一次性任务(含续派)的子任务,实时流里的样子。cli-chat 只读查看这类任务时用。
|
|
246
|
+
* prompt / opener_prompt:指定了 ?task= 时看的那个子任务才有(opener_prompt = 开出它所在节点对话的那个子任务的原文)。
|
|
247
|
+
*/
|
|
248
|
+
export interface BatchStreamTask {
|
|
249
|
+
id: string;
|
|
250
|
+
seq: number;
|
|
251
|
+
task_key: string | null;
|
|
252
|
+
title: string;
|
|
253
|
+
status: string;
|
|
254
|
+
round: number;
|
|
255
|
+
attempt: number;
|
|
256
|
+
remote_task_id: string | null;
|
|
257
|
+
continue_from_task_id: string | null;
|
|
258
|
+
last_error: string | null;
|
|
259
|
+
prompt?: string;
|
|
260
|
+
opener_prompt?: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** 一次性任务(含续派,mode 不是 conversation)的实时流首个事件:没有轮次,只有子任务。 */
|
|
264
|
+
export interface BatchStreamSnapshot {
|
|
265
|
+
job: { id: string; status: JobStatus; node_id: string | null; round: number; terminal_revision: number; mode: string; title: string };
|
|
266
|
+
tasks: BatchStreamTask[];
|
|
267
|
+
/** 指定了 ?task= 时:看的那个子任务。 */
|
|
268
|
+
task?: BatchStreamTask;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export const isBatchSnapshot = (snapshot: StreamSnapshot | BatchStreamSnapshot): snapshot is BatchStreamSnapshot =>
|
|
272
|
+
!Array.isArray((snapshot as StreamSnapshot).turns) && Array.isArray((snapshot as BatchStreamSnapshot).tasks);
|
|
273
|
+
|
|
212
274
|
export interface StreamEnd {
|
|
275
|
+
/** 对话任务的结束原因;只是这个对话被关闭时为 conversation_closed。 */
|
|
213
276
|
reason: string;
|
|
277
|
+
task_id?: string;
|
|
214
278
|
continued_by_job_id?: string | null;
|
|
279
|
+
/** 接续这个对话的新对话(新对话任务里的子任务);还没有为 null。 */
|
|
280
|
+
continued_by_task_id?: string | null;
|
|
215
281
|
}
|
|
216
282
|
|
|
217
283
|
// ── center 回调体(与中枢 conversation.ts turnCallbackPayload / job-callback.ts 的字段一一对应) ──
|
|
@@ -264,6 +330,8 @@ export interface ConversationClosedCallback {
|
|
|
264
330
|
/** 投递顺序号:最后一轮的 seq + 1,永远排在这个对话所有 conversation_turn 之后。 */
|
|
265
331
|
callback_seq: number;
|
|
266
332
|
job_id: string;
|
|
333
|
+
/** 只关掉了对话任务里的这一个对话时才有(前端事件;center 只在整个对话任务结束时回调)。 */
|
|
334
|
+
task_id?: string;
|
|
267
335
|
/** succeeded(关闭 / 过期 / 换线)/ cancelled / failed。 */
|
|
268
336
|
status: JobStatus;
|
|
269
337
|
/** closed / cancelled / expired / node_reassigned / node_lost。 */
|
|
@@ -276,3 +344,73 @@ export interface ConversationClosedCallback {
|
|
|
276
344
|
}
|
|
277
345
|
|
|
278
346
|
export type ConversationCallback = ConversationTurnCallback | ConversationClosedCallback;
|
|
347
|
+
|
|
348
|
+
/** 一轮结束时 onTurnEnd 收到的载荷(字段与 center 的 conversation_turn 回调体一致)。 */
|
|
349
|
+
export type TurnEndPayload = ConversationTurnCallback;
|
|
350
|
+
/** 对话结束时 onConversationEnd 收到的载荷(字段与 center 的 conversation_closed 回调体一致)。 */
|
|
351
|
+
export type ConversationEndPayload = ConversationClosedCallback;
|
|
352
|
+
|
|
353
|
+
// ── 右侧看板(cli-board):center 的 /api/jobs/:id/board、files、diff、browser ──
|
|
354
|
+
|
|
355
|
+
export interface BoardInfo {
|
|
356
|
+
job_id: string;
|
|
357
|
+
/** false = 还没派发到执行节点(文件和浏览器都还没有)。 */
|
|
358
|
+
dispatched: boolean;
|
|
359
|
+
detail?: string;
|
|
360
|
+
task_id?: string;
|
|
361
|
+
remote_task_id?: string;
|
|
362
|
+
node?: { id: string; health: string };
|
|
363
|
+
files: boolean;
|
|
364
|
+
/** 节点开了浏览器功能。 */
|
|
365
|
+
browser: boolean;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export interface FileEntry {
|
|
369
|
+
name: string;
|
|
370
|
+
/** 相对工作目录的路径,用 / 分隔。 */
|
|
371
|
+
path: string;
|
|
372
|
+
type: "dir" | "file";
|
|
373
|
+
size: number;
|
|
374
|
+
mtime: number;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export interface DirListing { type: "dir"; path: string; entries: FileEntry[]; truncated: boolean }
|
|
378
|
+
export interface FileInfo { type: "file"; path: string; name: string; size: number; mtime: number; contentType: string; inline: boolean }
|
|
379
|
+
|
|
380
|
+
export interface DiffFile { path: string; status: string; additions: number; deletions: number; binary: boolean; patch: string; truncated?: boolean }
|
|
381
|
+
/** Operator 的 /api/tasks/:id/diff(经 center 原样转发)。 */
|
|
382
|
+
export interface DiffResult {
|
|
383
|
+
isolated?: boolean;
|
|
384
|
+
reason?: string;
|
|
385
|
+
branch?: string;
|
|
386
|
+
baseLabel?: string;
|
|
387
|
+
files?: DiffFile[];
|
|
388
|
+
isDirty?: boolean;
|
|
389
|
+
ahead?: number;
|
|
390
|
+
alreadyMerged?: boolean;
|
|
391
|
+
error?: string;
|
|
392
|
+
/** 有的节点只给一整段 unified diff。 */
|
|
393
|
+
diff?: string;
|
|
394
|
+
patch?: string;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Operator 的任务浏览器状态(/api/tasks/:id/browser)。 */
|
|
398
|
+
export interface BrowserStatus {
|
|
399
|
+
feature: boolean;
|
|
400
|
+
state: "none" | "opening" | "running" | "closing";
|
|
401
|
+
origin: "ai" | "manual" | "check" | null;
|
|
402
|
+
control: "ai" | "human";
|
|
403
|
+
handoff: { instruction: string; by: "ai" | "user" | "browser" | "kept" } | null;
|
|
404
|
+
capability: "config" | "manual" | "ai" | null;
|
|
405
|
+
viewers: number;
|
|
406
|
+
spaceName: string;
|
|
407
|
+
instance: { running: boolean; spaces: number };
|
|
408
|
+
shownLocally: boolean;
|
|
409
|
+
error: string | null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** 浏览器画面流里的一条消息(节点原样转发)。 */
|
|
413
|
+
export type BrowserMessage =
|
|
414
|
+
| { type: "frame"; data: string; metadata?: { deviceWidth: number; deviceHeight: number } }
|
|
415
|
+
| { type: "page"; url?: string; title?: string }
|
|
416
|
+
| { type: "error"; message?: string };
|
package/src/elements.tsx
CHANGED
|
@@ -7,18 +7,27 @@
|
|
|
7
7
|
*
|
|
8
8
|
* <cli-msglist group="a" base-url="/api/center" title="战略"></cli-msglist>
|
|
9
9
|
* <cli-chat group="a"></cli-chat>
|
|
10
|
+
* <cli-board group="a"></cli-board> (右侧看板:文件 / 改动 / 浏览器;对话里点文件会在这里打开)
|
|
11
|
+
* <cli-file-preview job-id="job_x" path="outputs/报告.md"></cli-file-preview> (单独的文件预览 + 下载)
|
|
12
|
+
* <cli-browser job-id="job_x"></cli-browser> (单独的浏览器实时画面)
|
|
10
13
|
*
|
|
11
14
|
* 同一个 group 的元素共享一个仓库:列表点哪个,对话就显示哪个;不写 group 各自独立。
|
|
12
|
-
* 属性:base-url、token、line、namespace、group、job-id、
|
|
15
|
+
* 属性:base-url、token、line、namespace、group、job-id、task-id(对话任务里的哪个对话,"new" = 新开一个;对话 / 看板)、
|
|
16
|
+
* title、color、theme、context、read-only、
|
|
17
|
+
* tabs(看板页签,逗号分隔:files,changes,browser)、default-tab、file-open(对话:auto / board / modal / link)、
|
|
13
18
|
* config(界面配置 JSON)、new-conversation(新建对话参数 JSON:title / businessId / workspace / callback / agent …)。
|
|
14
19
|
* 界面配置也可以直接赋对象:document.querySelector("cli-chat").config = { header: { export: false } }。
|
|
15
|
-
*
|
|
16
|
-
* cli-
|
|
20
|
+
* 事件(全在浏览器里从实时流算出,和 center 的 callback.url 无关):
|
|
21
|
+
* cli-turn-end(一轮结束)、cli-conversation-end(对话结束),detail 字段与 center 回调体一致;
|
|
22
|
+
* cli-event(全部事件:select / conversation_turn / conversation_closed / job_change / open / error);
|
|
23
|
+
* cli-callback 是旧名字,等同于前两个合在一起,保留兼容。
|
|
17
24
|
* 渲染在 light DOM 里(不用 Shadow DOM),样式靠页面引入 styles.css,作用域都在 .cli-ui 内。
|
|
18
25
|
*/
|
|
19
26
|
import { createRoot, type Root } from "react-dom/client";
|
|
20
27
|
import { CliMsgList } from "./msglist/CliMsgList.js";
|
|
21
28
|
import { CliChat } from "./chat/CliChat.js";
|
|
29
|
+
import { CliBoard, type BoardTab } from "./board/CliBoard.js";
|
|
30
|
+
import { CliBrowserView, CliFilePreview } from "./board/blocks.js";
|
|
22
31
|
import { createCliStore, type CliStore } from "./core/store.js";
|
|
23
32
|
|
|
24
33
|
const groups = new Map<string, CliStore>();
|
|
@@ -36,7 +45,7 @@ function storeFor(el: HTMLElement): CliStore | undefined {
|
|
|
36
45
|
return store;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
const ATTRS = ["base-url", "token", "line", "namespace", "group", "job-id", "title", "color", "theme", "context", "read-only", "config", "new-conversation"];
|
|
48
|
+
const ATTRS = ["base-url", "token", "line", "namespace", "group", "job-id", "task-id", "title", "color", "theme", "context", "read-only", "config", "new-conversation", "tabs", "default-tab", "file-open", "path"];
|
|
40
49
|
|
|
41
50
|
function jsonAttr(el: HTMLElement, name: string): object | undefined {
|
|
42
51
|
const raw = el.getAttribute(name);
|
|
@@ -91,6 +100,8 @@ abstract class CliElement extends HTMLElement {
|
|
|
91
100
|
color: this.getAttribute("color") ?? undefined,
|
|
92
101
|
theme: (this.getAttribute("theme") as "light" | "dark" | null) ?? undefined,
|
|
93
102
|
onEvent: (event: unknown) => this.dispatchEvent(new CustomEvent("cli-event", { detail: event, bubbles: true })),
|
|
103
|
+
onTurnEnd: (payload: unknown) => this.dispatchEvent(new CustomEvent("cli-turn-end", { detail: payload, bubbles: true })),
|
|
104
|
+
onConversationEnd: (payload: unknown) => this.dispatchEvent(new CustomEvent("cli-conversation-end", { detail: payload, bubbles: true })),
|
|
94
105
|
onCallback: (payload: unknown) => this.dispatchEvent(new CustomEvent("cli-callback", { detail: payload, bubbles: true })),
|
|
95
106
|
style: { height: "100%" },
|
|
96
107
|
config: this.config as never,
|
|
@@ -103,8 +114,8 @@ abstract class CliElement extends HTMLElement {
|
|
|
103
114
|
class CliMsgListElement extends CliElement {
|
|
104
115
|
protected element() {
|
|
105
116
|
const context = this.getAttribute("context");
|
|
106
|
-
//
|
|
107
|
-
const { onCallback:
|
|
117
|
+
// 结果事件只由 <cli-chat> 发:同一个 group 里列表和对话共用一个仓库,两边都发的话 document 上会收到两遍。
|
|
118
|
+
const { onTurnEnd: _a, onConversationEnd: _b, onCallback: _c, ...common } = this.common();
|
|
108
119
|
return <CliMsgList {...common} {...(context !== null ? { context } : {})} />;
|
|
109
120
|
}
|
|
110
121
|
}
|
|
@@ -112,11 +123,44 @@ class CliMsgListElement extends CliElement {
|
|
|
112
123
|
class CliChatElement extends CliElement {
|
|
113
124
|
protected element() {
|
|
114
125
|
const jobId = this.getAttribute("job-id");
|
|
115
|
-
|
|
126
|
+
const taskId = this.getAttribute("task-id");
|
|
127
|
+
const fileOpen = this.getAttribute("file-open") as "auto" | "board" | "modal" | "link" | null;
|
|
128
|
+
return <CliChat {...this.common()} {...(jobId !== null ? { jobId: jobId || null } : {})} {...(taskId ? { taskId } : {})} readOnly={this.hasAttribute("read-only")} {...(fileOpen ? { fileOpen } : {})} />;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
class CliBoardElement extends CliElement {
|
|
133
|
+
protected element() {
|
|
134
|
+
const jobId = this.getAttribute("job-id");
|
|
135
|
+
const tabs = this.getAttribute("tabs")?.split(",").map((t) => t.trim()).filter(Boolean) as BoardTab[] | undefined;
|
|
136
|
+
const defaultTab = (this.getAttribute("default-tab") as BoardTab | null) ?? undefined;
|
|
137
|
+
// 结果事件只由 <cli-chat> 发(同上);看板只发 cli-event。
|
|
138
|
+
const { onTurnEnd: _a, onConversationEnd: _b, onCallback: _c, title: _t, color: _k, config: _g, ...common } = this.common();
|
|
139
|
+
const taskId = this.getAttribute("task-id");
|
|
140
|
+
return <CliBoard {...common} {...(jobId !== null ? { jobId: jobId || null } : {})} {...(taskId ? { taskId } : {})} tabs={tabs?.length ? tabs : undefined} defaultTab={defaultTab} readOnly={this.hasAttribute("read-only")} />;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
class CliFilePreviewElement extends CliElement {
|
|
145
|
+
protected element() {
|
|
146
|
+
const jobId = this.getAttribute("job-id");
|
|
147
|
+
const { onTurnEnd: _a, onConversationEnd: _b, onCallback: _c, title: _t, color: _k, config: _g, ...common } = this.common();
|
|
148
|
+
return <CliFilePreview {...common} {...(jobId !== null ? { jobId: jobId || null } : {})} path={this.getAttribute("path") ?? ""} />;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class CliBrowserElement extends CliElement {
|
|
153
|
+
protected element() {
|
|
154
|
+
const jobId = this.getAttribute("job-id");
|
|
155
|
+
const { onTurnEnd: _a, onConversationEnd: _b, onCallback: _c, title: _t, color: _k, config: _g, ...common } = this.common();
|
|
156
|
+
return <CliBrowserView {...common} {...(jobId !== null ? { jobId: jobId || null } : {})} readOnly={this.hasAttribute("read-only")} />;
|
|
116
157
|
}
|
|
117
158
|
}
|
|
118
159
|
|
|
119
160
|
export function defineCliElements() {
|
|
120
161
|
if (!customElements.get("cli-msglist")) customElements.define("cli-msglist", CliMsgListElement);
|
|
121
162
|
if (!customElements.get("cli-chat")) customElements.define("cli-chat", CliChatElement);
|
|
163
|
+
if (!customElements.get("cli-board")) customElements.define("cli-board", CliBoardElement);
|
|
164
|
+
if (!customElements.get("cli-file-preview")) customElements.define("cli-file-preview", CliFilePreviewElement);
|
|
165
|
+
if (!customElements.get("cli-browser")) customElements.define("cli-browser", CliBrowserElement);
|
|
122
166
|
}
|
package/src/embed.ts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
* <script type="module" src="/static/cli-ui/cli-ui.js"></script>
|
|
6
6
|
* <cli-chat base-url="/api/center" job-id="job_xxx"></cli-chat>
|
|
7
7
|
*
|
|
8
|
-
* 引入后自动注册 <cli-chat> / <cli-msglist>;想用 JS 控制(传回调函数、随时改参数)就用
|
|
8
|
+
* 引入后自动注册 <cli-chat> / <cli-msglist> / <cli-board>;想用 JS 控制(传回调函数、随时改参数)就用
|
|
9
|
+
* window.CliUI.mountChat / mountMsgList / mountBoard。几个 mount 要联动(对话里点文件在看板打开)时传同一个 store:
|
|
10
|
+
* const store = CliUI.createCliStore({ baseUrl: "/api/center" });
|
|
11
|
+
* CliUI.mountChat(a, { store }); CliUI.mountBoard(b, { store });
|
|
9
12
|
* 产物由 `npm run build:embed -w @spzxg/cli-ui` 生成在 dist/。
|
|
10
13
|
*/
|
|
11
14
|
import "./styles.css";
|
|
@@ -14,6 +17,8 @@ import { createRoot } from "react-dom/client";
|
|
|
14
17
|
import { defineCliElements } from "./elements.js";
|
|
15
18
|
import { CliChat, type CliChatProps } from "./chat/CliChat.js";
|
|
16
19
|
import { CliMsgList, type CliMsgListProps } from "./msglist/CliMsgList.js";
|
|
20
|
+
import { CliBoard, type CliBoardProps } from "./board/CliBoard.js";
|
|
21
|
+
import { CliBrowserView, CliFilePreview, type CliBrowserViewProps, type CliFilePreviewProps } from "./board/blocks.js";
|
|
17
22
|
import { CliCenterProvider } from "./core/provider.js";
|
|
18
23
|
import { createCliStore } from "./core/store.js";
|
|
19
24
|
import { createCenterClient } from "./core/client.js";
|
|
@@ -35,13 +40,19 @@ function mount<P extends object>(Component: (props: P) => unknown, el: Element,
|
|
|
35
40
|
};
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
/** 在一个元素里挂对话组件。参数与 React 的 <CliChat> 相同(jobId、newConversation、
|
|
43
|
+
/** 在一个元素里挂对话组件。参数与 React 的 <CliChat> 相同(jobId、newConversation、onTurnEnd、config……)。 */
|
|
39
44
|
export const mountChat = (el: Element, props: CliChatProps = {}) => mount(CliChat, el, props);
|
|
40
45
|
/** 在一个元素里挂列表组件。参数与 React 的 <CliMsgList> 相同。 */
|
|
41
46
|
export const mountMsgList = (el: Element, props: CliMsgListProps = {}) => mount(CliMsgList, el, props);
|
|
47
|
+
/** 在一个元素里挂右侧看板。参数与 React 的 <CliBoard> 相同(jobId、tabs、fileActions……)。 */
|
|
48
|
+
export const mountBoard = (el: Element, props: CliBoardProps = {}) => mount(CliBoard, el, props);
|
|
49
|
+
/** 单独的文件预览 + 下载:{ jobId, path, baseUrl… }。 */
|
|
50
|
+
export const mountFilePreview = (el: Element, props: CliFilePreviewProps) => mount(CliFilePreview, el, props);
|
|
51
|
+
/** 单独的浏览器实时画面:{ jobId, readOnly, baseUrl… }。 */
|
|
52
|
+
export const mountBrowser = (el: Element, props: CliBrowserViewProps = {}) => mount(CliBrowserView, el, props);
|
|
42
53
|
|
|
43
|
-
export { defineCliElements, CliChat, CliMsgList, CliCenterProvider, createCliStore, createCenterClient };
|
|
54
|
+
export { defineCliElements, CliChat, CliMsgList, CliBoard, CliFilePreview, CliBrowserView, CliCenterProvider, createCliStore, createCenterClient };
|
|
44
55
|
|
|
45
56
|
defineCliElements();
|
|
46
|
-
const api = { mountChat, mountMsgList, createCliStore, createCenterClient, defineCliElements };
|
|
57
|
+
const api = { mountChat, mountMsgList, mountBoard, mountFilePreview, mountBrowser, createCliStore, createCenterClient, defineCliElements };
|
|
47
58
|
(globalThis as { CliUI?: typeof api }).CliUI = api;
|
package/src/index.ts
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* cli-msglist 消息对话列表(CliMsgList)
|
|
5
5
|
* cli-chat 对话(CliChat)
|
|
6
|
-
* cli-board
|
|
6
|
+
* cli-board 右侧看板:工作区文件(预览 / 下载)、改动、任务浏览器(CliBoard)
|
|
7
|
+
* 其中两个核心也能单独用:CliFilePreview(文件预览 + 下载)、CliBrowserView(浏览器实时画面)
|
|
7
8
|
*
|
|
8
9
|
* 单独用:
|
|
9
10
|
* <CliChat baseUrl="/api/center" jobId="job_xxx" />
|
|
11
|
+
* <CliChat baseUrl="/api/center" jobId="job_xxx" taskId="task_yyy" /> (对话任务里的某个对话;taskId="new" 新开一个)
|
|
10
12
|
* 组合用(同一个 Provider 里共享选中和实时流):
|
|
11
13
|
* <CliCenterProvider baseUrl="/api/center" namespace="strategy">
|
|
12
14
|
* <CliMsgList title="战略" />
|
|
@@ -18,15 +20,19 @@
|
|
|
18
20
|
*/
|
|
19
21
|
export { CliMsgList, type CliMsgListProps } from "./msglist/CliMsgList.js";
|
|
20
22
|
export { CliChat, type CliChatProps } from "./chat/CliChat.js";
|
|
21
|
-
export {
|
|
23
|
+
export { CliBoard, type CliBoardProps, type BoardTab } from "./board/CliBoard.js";
|
|
24
|
+
export { CliFilePreview, CliBrowserView, type CliFilePreviewProps, type CliBrowserViewProps } from "./board/blocks.js";
|
|
25
|
+
export { FileViewer, downloadFile, type FileViewerProps, type FileAction } from "./board/FileViewer.js";
|
|
26
|
+
export { BrowserPane } from "./board/BrowserPane.js";
|
|
27
|
+
export { CliCenterProvider, useCliStore, useCliState, type CliCenterProviderProps, type ResultHandlers } from "./core/provider.js";
|
|
22
28
|
|
|
23
29
|
export {
|
|
24
30
|
resolveChatConfig, resolveMsgListConfig, DEFAULT_CHAT_CONFIG, DEFAULT_MSGLIST_CONFIG,
|
|
25
31
|
type CliChatConfig, type CliMsgListConfig, type ResolvedChatConfig, type ResolvedMsgListConfig,
|
|
26
32
|
} from "./core/config.js";
|
|
27
|
-
export { createCliStore, type CliStore, type CliStoreOptions, type CliState, type CliEvent, type JobEntry } from "./core/store.js";
|
|
28
|
-
export { createCenterClient, CenterError, type CenterClient, type CenterClientOptions, type StreamHandlers } from "./core/client.js";
|
|
29
|
-
export { viewMessages, isRunning, runningTurn, type ConvState } from "./core/conversation.js";
|
|
33
|
+
export { createCliStore, type CliStore, type CliStoreOptions, type CliState, type CliEvent, type JobEntry, type OpenTarget } from "./core/store.js";
|
|
34
|
+
export { createCenterClient, CenterError, boardFileUrl, type CenterClient, type CenterClientOptions, type StreamHandlers, type BoardBackend, type ConversationBackend } from "./core/client.js";
|
|
35
|
+
export { viewMessages, isRunning, runningTurn, convKey, parseConvKey, NEW_TASK, type ConvState } from "./core/conversation.js";
|
|
30
36
|
export {
|
|
31
37
|
composeMessage, splitMessage, serializeBlock, readFileText, isLongText, ACCEPTED_EXTENSIONS, MAX_MESSAGE_CHARS,
|
|
32
38
|
type TextBlock, type MessagePart,
|