@ai-zen/cli 1.0.0-alpha.2 → 1.0.0-alpha.4
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/CHANGELOG.md +47 -0
- package/README.md +12 -12
- package/README.zh.md +12 -12
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/conversation-persist-plugin.js +1 -1
- package/dist/conversation-persist-plugin.js.map +1 -1
- package/dist/conversation-repository.js +1 -1
- package/dist/conversation-repository.js.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/migration-service.js +1 -1
- package/dist/migration-service.js.map +1 -1
- package/dist/session-pointer.d.ts +22 -9
- package/dist/session-pointer.d.ts.map +1 -1
- package/dist/session-pointer.js +55 -23
- package/dist/session-pointer.js.map +1 -1
- package/dist/tui/chat-session.d.ts +34 -47
- package/dist/tui/chat-session.d.ts.map +1 -1
- package/dist/tui/chat-session.js +62 -66
- package/dist/tui/chat-session.js.map +1 -1
- package/dist/tui/config-wizard/types.d.ts +9 -0
- package/dist/tui/config-wizard/types.d.ts.map +1 -1
- package/dist/tui/config-wizard/wizard.d.ts.map +1 -1
- package/dist/tui/config-wizard/wizard.js +4 -3
- package/dist/tui/config-wizard/wizard.js.map +1 -1
- package/dist/tui/highlight.d.ts +3 -2
- package/dist/tui/highlight.d.ts.map +1 -1
- package/dist/tui/highlight.js +3 -2
- package/dist/tui/highlight.js.map +1 -1
- package/dist/tui/index.d.ts +1 -0
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +5 -5
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/screens/chat-commands.d.ts +4 -2
- package/dist/tui/screens/chat-commands.d.ts.map +1 -1
- package/dist/tui/screens/chat-commands.js +10 -11
- package/dist/tui/screens/chat-commands.js.map +1 -1
- package/dist/tui/screens/chat.d.ts +0 -4
- package/dist/tui/screens/chat.d.ts.map +1 -1
- package/dist/tui/screens/chat.js +81 -60
- package/dist/tui/screens/chat.js.map +1 -1
- package/dist/tui/screens/frame-buffer.d.ts +47 -0
- package/dist/tui/screens/frame-buffer.d.ts.map +1 -0
- package/dist/tui/screens/frame-buffer.js +78 -0
- package/dist/tui/screens/frame-buffer.js.map +1 -0
- package/dist/tui/screens/index.d.ts +6 -6
- package/dist/tui/screens/index.d.ts.map +1 -1
- package/dist/tui/screens/index.js +5 -5
- package/dist/tui/screens/index.js.map +1 -1
- package/dist/tui/screens/layout.d.ts +5 -3
- package/dist/tui/screens/layout.d.ts.map +1 -1
- package/dist/tui/screens/layout.js +3 -8
- package/dist/tui/screens/layout.js.map +1 -1
- package/dist/tui/screens/lines.d.ts +12 -41
- package/dist/tui/screens/lines.d.ts.map +1 -1
- package/dist/tui/screens/lines.js +87 -95
- package/dist/tui/screens/lines.js.map +1 -1
- package/dist/tui/screens/messages-view.d.ts +10 -0
- package/dist/tui/screens/messages-view.d.ts.map +1 -0
- package/dist/tui/screens/messages-view.js +115 -0
- package/dist/tui/screens/messages-view.js.map +1 -0
- package/dist/tui/screens/notice.d.ts +32 -0
- package/dist/tui/screens/notice.d.ts.map +1 -0
- package/dist/tui/screens/notice.js +46 -0
- package/dist/tui/screens/notice.js.map +1 -0
- package/dist/tui/screens/render.d.ts +2 -4
- package/dist/tui/screens/render.d.ts.map +1 -1
- package/dist/tui/screens/render.js +7 -43
- package/dist/tui/screens/render.js.map +1 -1
- package/dist/tui/text.d.ts +5 -0
- package/dist/tui/text.d.ts.map +1 -1
- package/dist/tui/text.js +7 -0
- package/dist/tui/text.js.map +1 -1
- package/dist/tui/theme.d.ts +7 -1
- package/dist/tui/theme.d.ts.map +1 -1
- package/dist/tui/theme.js +15 -3
- package/dist/tui/theme.js.map +1 -1
- package/package.json +3 -3
- package/dist/tui/screens/state.d.ts +0 -13
- package/dist/tui/screens/state.d.ts.map +0 -1
- package/dist/tui/screens/state.js +0 -75
- package/dist/tui/screens/state.js.map +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 流式事件合帧缓冲 —— 把高频的流式 delta 合并为低频「帧」再交给 React 上屏。
|
|
3
|
+
*
|
|
4
|
+
* 背景:AI 流式响应每秒可产生数十到上百个 delta。若每个 delta 都触发一次 React
|
|
5
|
+
* 更新,等价于每个 delta 一次「全量布局重算(含语法高亮 / 折行)+ 整屏重绘」。
|
|
6
|
+
* Ink 每帧输出的是**整屏**(先擦除旧帧再写新帧,见 `ink/build/ink.js`),并不做
|
|
7
|
+
* 行级差分;在 SSH 弱网(低带宽 / 高延迟)下,这会迅速填满 pty 缓冲、阻塞
|
|
8
|
+
* stdout(Linux 上 TTY 写是同步阻塞的),表现为界面卡死,甚至把连接拖断。
|
|
9
|
+
*
|
|
10
|
+
* 本模块把 delta 先攒进缓冲,按固定间隔(默认 {@link STREAM_FRAME_MS} 毫秒,
|
|
11
|
+
* 约 16fps)批量吐出;`user` / `done` / `error` 这类需要即时反馈或收尾的事件则
|
|
12
|
+
* 立即上屏(flush),避免「按下回车半天不显示」或「回答完了还在转圈」。
|
|
13
|
+
*
|
|
14
|
+
* 纯逻辑、不依赖 React,可独立单测。
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* 合帧间隔(毫秒):约 16fps。
|
|
18
|
+
*
|
|
19
|
+
* 刻意**低于** Ink 默认的 30fps 输出节流,让「主动合帧」成为上屏频率的主控,
|
|
20
|
+
* 而不是叠加在 Ink 节流之上。
|
|
21
|
+
*/
|
|
22
|
+
export const STREAM_FRAME_MS = 60;
|
|
23
|
+
/**
|
|
24
|
+
* 需要立即上屏(不等下一帧)的事件类型:
|
|
25
|
+
* - `user`:回车即回显,交互反馈不能延迟;
|
|
26
|
+
* - `done` / `error`:本轮收尾 / 报错,立刻落定,避免「结束还挂着 spinner」。
|
|
27
|
+
*/
|
|
28
|
+
export function isImmediateEvent(type) {
|
|
29
|
+
return type === "user" || type === "done" || type === "error";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 通用合帧缓冲:`push` 入队,按 `delayMs` 节流批量交给 `onFlush`。
|
|
33
|
+
*
|
|
34
|
+
* `immediate=true` 时立即吐出(连同已积压项),用于需要即时反馈的事件。
|
|
35
|
+
*/
|
|
36
|
+
export class FrameBuffer {
|
|
37
|
+
onFlush;
|
|
38
|
+
delayMs;
|
|
39
|
+
pending = [];
|
|
40
|
+
timer = null;
|
|
41
|
+
constructor(onFlush, delayMs = STREAM_FRAME_MS) {
|
|
42
|
+
this.onFlush = onFlush;
|
|
43
|
+
this.delayMs = delayMs;
|
|
44
|
+
}
|
|
45
|
+
/** 入队一个事件;`immediate` 为真时立刻吐出(连同已积压的) */
|
|
46
|
+
push(item, immediate = false) {
|
|
47
|
+
this.pending.push(item);
|
|
48
|
+
if (immediate) {
|
|
49
|
+
this.flushNow();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (this.timer == null) {
|
|
53
|
+
this.timer = setTimeout(() => this.flushNow(), this.delayMs);
|
|
54
|
+
// 不因合帧定时器拖住进程退出(测试环境的假定时器可能没有 unref,故可选调用)
|
|
55
|
+
this.timer.unref?.();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** 立即吐出缓冲(清定时器 + 排空队列);空队列时不做任何事 */
|
|
59
|
+
flushNow() {
|
|
60
|
+
if (this.timer != null) {
|
|
61
|
+
clearTimeout(this.timer);
|
|
62
|
+
this.timer = null;
|
|
63
|
+
}
|
|
64
|
+
if (this.pending.length === 0)
|
|
65
|
+
return;
|
|
66
|
+
const items = this.pending.splice(0, this.pending.length);
|
|
67
|
+
this.onFlush(items);
|
|
68
|
+
}
|
|
69
|
+
/** 丢弃缓冲并清定时器(组件卸载 / 会话重建时调用) */
|
|
70
|
+
dispose() {
|
|
71
|
+
if (this.timer != null) {
|
|
72
|
+
clearTimeout(this.timer);
|
|
73
|
+
this.timer = null;
|
|
74
|
+
}
|
|
75
|
+
this.pending.length = 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=frame-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-buffer.js","sourceRoot":"","sources":["../../../src/tui/screens/frame-buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,WAAW;IAKH;IACA;IALX,OAAO,GAAQ,EAAE,CAAC;IAClB,KAAK,GAA0B,IAAI,CAAC;IAE5C,YACmB,OAA6B,EAC7B,UAAkB,eAAe;QADjC,YAAO,GAAP,OAAO,CAAsB;QAC7B,YAAO,GAAP,OAAO,CAA0B;IACjD,CAAC;IAEJ,yCAAyC;IACzC,IAAI,CAAC,IAAO,EAAE,SAAS,GAAG,KAAK;QAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7D,2CAA2C;YAC3C,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;IAED,oCAAoC;IACpC,QAAQ;QACN,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1D,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IAED,gCAAgC;IAChC,OAAO;QACL,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export { displayWidth, wrapText, layoutInput, INPUT_PREFIX_WIDTH, usableFrameRows } from "../text.js";
|
|
2
|
-
export {
|
|
3
|
-
export type { LineKind, RLine, Span
|
|
1
|
+
export { displayWidth, wrapText, layoutInput, INPUT_PREFIX_WIDTH, usableFrameRows, stripCwdNote } from "../text.js";
|
|
2
|
+
export { contentLines, toolSpans, clipSpans, spanLine, innerWidth } from "./lines.js";
|
|
3
|
+
export type { LineKind, RLine, Span } from "./lines.js";
|
|
4
|
+
export { messageToLines, cachedMessageToLines, transcriptLines, messageText, } from "./messages-view.js";
|
|
4
5
|
export { inputLines, computeInputCursorPosition, isWordChar, wordLeft, wordRight } from "./input.js";
|
|
5
6
|
export { CONFIRM_LINES, pickerLines, computeChatLayout } from "./layout.js";
|
|
6
7
|
export type { ChatLayoutInput, ChatLayout } from "./layout.js";
|
|
7
8
|
export { Splash } from "./splash.js";
|
|
8
|
-
export {
|
|
9
|
-
export type {
|
|
10
|
-
export { messagesToBlocks } from "./render.js";
|
|
9
|
+
export { NoticeChannel, noticeLines, MAX_NOTICE_LINES } from "./notice.js";
|
|
10
|
+
export type { Notice } from "./notice.js";
|
|
11
11
|
export { Chat, resolveSubmitText } from "./chat.js";
|
|
12
12
|
export type { ChatScreenProps } from "./chat.js";
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpH,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtF,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAErG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// 对话屏(screens)—— 对外统一出口
|
|
2
2
|
//
|
|
3
3
|
// 原 src/tui/screens.tsx(1316 行)已按职责拆分为本目录下的多个模块,
|
|
4
|
-
// 此处仅做 re-export,保持既有引用路径 `./screens/index.js
|
|
4
|
+
// 此处仅做 re-export,保持既有引用路径 `./screens/index.js`。
|
|
5
5
|
// 文本度量 / 折行 / 整帧高度:仍位于 ../text.js,此处转发(历史引用与测试依赖)。
|
|
6
|
-
export { displayWidth, wrapText, layoutInput, INPUT_PREFIX_WIDTH, usableFrameRows } from "../text.js";
|
|
7
|
-
export {
|
|
6
|
+
export { displayWidth, wrapText, layoutInput, INPUT_PREFIX_WIDTH, usableFrameRows, stripCwdNote } from "../text.js";
|
|
7
|
+
export { contentLines, toolSpans, clipSpans, spanLine, innerWidth } from "./lines.js";
|
|
8
|
+
export { messageToLines, cachedMessageToLines, transcriptLines, messageText, } from "./messages-view.js";
|
|
8
9
|
export { inputLines, computeInputCursorPosition, isWordChar, wordLeft, wordRight } from "./input.js";
|
|
9
10
|
export { CONFIRM_LINES, pickerLines, computeChatLayout } from "./layout.js";
|
|
10
11
|
export { Splash } from "./splash.js";
|
|
11
|
-
export {
|
|
12
|
-
export { messagesToBlocks } from "./render.js";
|
|
12
|
+
export { NoticeChannel, noticeLines, MAX_NOTICE_LINES } from "./notice.js";
|
|
13
13
|
export { Chat, resolveSubmitText } from "./chat.js";
|
|
14
14
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tui/screens/index.ts"],"names":[],"mappings":"AAAA,wBAAwB;AACxB,EAAE;AACF,iDAAiD;AACjD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tui/screens/index.ts"],"names":[],"mappings":"AAAA,wBAAwB;AACxB,EAAE;AACF,iDAAiD;AACjD,gDAAgD;AAEhD,mDAAmD;AACnD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEpH,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGtF,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAErG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG5E,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG3E,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RLine
|
|
1
|
+
import { RLine } from "./lines.js";
|
|
2
2
|
/** 确认框固定高度(外 marginTop 1 + 上下边框 2 + 问题 1 + marginTop 1 + 选项 1) */
|
|
3
3
|
export declare const CONFIRM_LINES = 6;
|
|
4
4
|
/** 选择覆盖层高度(相对条目数) */
|
|
@@ -6,11 +6,13 @@ export declare function pickerLines(itemCount: number): number;
|
|
|
6
6
|
export interface ChatLayoutInput {
|
|
7
7
|
columns: number;
|
|
8
8
|
rows: number;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/** 已折行的对话行(由 messages-view 依 agent.messages 产出) */
|
|
10
|
+
transcript: RLine[];
|
|
11
11
|
input: string;
|
|
12
12
|
busy: boolean;
|
|
13
13
|
hintCount: number;
|
|
14
|
+
/** 通知栏行数(notice / error 独立栏位),缺省 0 */
|
|
15
|
+
noticeCount?: number;
|
|
14
16
|
overlay: "none" | "confirm" | "picker";
|
|
15
17
|
pickerItems: number;
|
|
16
18
|
/** 视口顶部行索引(相对整个对话行);null / undefined = 贴底(自动跟随最新) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/layout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/layout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,kEAAkE;AAClE,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,qBAAqB;AACrB,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,UAAU,EAAE,KAAK,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,CA2BpE"}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// 由 src/tui/screens.tsx 拆分而来 —— 吸底布局 computeChatLayout + 覆盖层高度
|
|
2
|
-
import { blockToLines, liveToLines } from "./lines.js";
|
|
3
1
|
import { inputLines } from "./input.js";
|
|
4
2
|
/** 确认框固定高度(外 marginTop 1 + 上下边框 2 + 问题 1 + marginTop 1 + 选项 1) */
|
|
5
3
|
export const CONFIRM_LINES = 6;
|
|
@@ -16,19 +14,16 @@ export function pickerLines(itemCount) {
|
|
|
16
14
|
* 传入具体值时按 [0, totalLines - budget] 钳制,用于「应用内翻页」回看更早的历史。
|
|
17
15
|
*/
|
|
18
16
|
export function computeChatLayout(input) {
|
|
19
|
-
const transcript =
|
|
20
|
-
for (const block of input.blocks)
|
|
21
|
-
transcript.push(...blockToLines(block, input.columns));
|
|
22
|
-
if (input.live)
|
|
23
|
-
transcript.push(...liveToLines(input.live, input.columns));
|
|
17
|
+
const transcript = input.transcript;
|
|
24
18
|
const inputHeight = input.busy ? 1 : inputLines(input.input, input.columns).length;
|
|
25
19
|
const overlayHeight = input.overlay === "confirm"
|
|
26
20
|
? CONFIRM_LINES
|
|
27
21
|
: input.overlay === "picker"
|
|
28
22
|
? pickerLines(input.pickerItems)
|
|
29
23
|
: 0;
|
|
24
|
+
const noticeCount = input.noticeCount ?? 0;
|
|
30
25
|
const bottomLines = input.overlay === "none"
|
|
31
|
-
? 1 /* 分隔线 */ + input.hintCount + inputHeight + 1 /* 状态栏 */
|
|
26
|
+
? noticeCount /* 通知栏 */ + 1 /* 分隔线 */ + input.hintCount + inputHeight + 1 /* 状态栏 */
|
|
32
27
|
: overlayHeight + 1 /* 状态栏 */;
|
|
33
28
|
const budget = Math.max(0, input.rows - bottomLines);
|
|
34
29
|
const totalLines = transcript.length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/tui/screens/layout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/tui/screens/layout.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,kEAAkE;AAClE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAC/B,qBAAqB;AACrB,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO,CAAC,GAAG,SAAS,CAAC;AACvB,CAAC;AAiCD;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAsB;IACtD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IAEpC,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACnF,MAAM,aAAa,GACjB,KAAK,CAAC,OAAO,KAAK,SAAS;QACzB,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,QAAQ;YAC1B,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;YAChC,CAAC,CAAC,CAAC,CAAC;IACV,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;IAC3C,MAAM,WAAW,GACf,KAAK,CAAC,OAAO,KAAK,MAAM;QACtB,CAAC,CAAC,WAAW,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,GAAG,WAAW,GAAG,CAAC,CAAC,SAAS;QACnF,CAAC,CAAC,aAAa,GAAG,CAAC,CAAC,SAAS,CAAC;IAElC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IAC1F,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,KAAK,GAAY;QACrB,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,KAAc,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACnG,GAAG,SAAS;KACb,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC;AAC1F,CAAC"}
|
|
@@ -14,58 +14,29 @@ export interface RLine {
|
|
|
14
14
|
/** 着色分段;`text` 恒为这些分段的纯文本拼接(便于测试与裁剪) */
|
|
15
15
|
spans?: Span[];
|
|
16
16
|
}
|
|
17
|
-
export interface ToolLine {
|
|
18
|
-
name: string;
|
|
19
|
-
args: string;
|
|
20
|
-
}
|
|
21
|
-
export type Block = {
|
|
22
|
-
id: number;
|
|
23
|
-
kind: "user";
|
|
24
|
-
text: string;
|
|
25
|
-
} | {
|
|
26
|
-
id: number;
|
|
27
|
-
kind: "assistant";
|
|
28
|
-
reasoning: string;
|
|
29
|
-
content: string;
|
|
30
|
-
tools: ToolLine[];
|
|
31
|
-
} | {
|
|
32
|
-
id: number;
|
|
33
|
-
kind: "notice";
|
|
34
|
-
tone: "info" | "error";
|
|
35
|
-
text: string;
|
|
36
|
-
};
|
|
37
|
-
export interface LiveAssistant {
|
|
38
|
-
reasoning: string;
|
|
39
|
-
content: string;
|
|
40
|
-
tools: ToolLine[];
|
|
41
|
-
sub: string | null;
|
|
42
|
-
/**
|
|
43
|
-
* 本轮(一次 API 请求)工具调用在 `tools` 中的起始下标。
|
|
44
|
-
*
|
|
45
|
-
* AI 一轮回复里可并行调用多个工具,且**工具执行后会在同一轮对话内继续下一轮**
|
|
46
|
-
* (每次请求 SDK 都 `emit("open")`);每轮的 `tool_calls.index` 都会从 `0` 重新
|
|
47
|
-
* 计数。用它分段,避免新一轮与上一轮同 index 的工具调用被拼到同一行
|
|
48
|
-
* (如 `read` + `edit` → `readedit`)。
|
|
49
|
-
*/
|
|
50
|
-
roundBase?: number;
|
|
51
|
-
}
|
|
52
17
|
/** 缩进 2 列后的可用文本宽度 */
|
|
53
18
|
export declare function innerWidth(columns: number): number;
|
|
54
19
|
export declare function indentLines(kind: LineKind, key: string, text: string, width: number): RLine[];
|
|
55
20
|
/** 按显示宽度把分段裁成**严格单行**,超出补省略号(用于工具调用行) */
|
|
56
21
|
export declare function clipSpans(spans: Span[], max: number): Span[];
|
|
22
|
+
/** 由分段拼出 RLine(`text` = 分段纯文本拼接) */
|
|
23
|
+
export declare function spanLine(key: string, kind: LineKind, spans: Span[]): RLine;
|
|
57
24
|
/**
|
|
58
25
|
* 工具调用行的分段:` ⚙ name(args)`,参数按 **JSON** 高亮。
|
|
59
26
|
* 无参数时省略括号;调用方用 `clipSpans` 裁成严格单行。
|
|
60
27
|
*/
|
|
61
28
|
export declare function toolSpans(name: string, args: string): Span[];
|
|
62
29
|
/**
|
|
63
|
-
* 正文 / 思考内容 →
|
|
64
|
-
*
|
|
30
|
+
* 正文 / 思考内容 → 预折行。
|
|
31
|
+
*
|
|
32
|
+
* - **普通文本段**按 **markdown 源码着色**(`highlightCode(text, "markdown")`):
|
|
33
|
+
* AI 回答本身就是 markdown,交给 highlight.js 的 markdown 词法上色,标记符(`#`/`**`/`` ` `` 等)
|
|
34
|
+
* 原样保留、只染色(不做语义渲染);
|
|
35
|
+
* - **围栏代码块**按标注语言做语法高亮,并**保留围栏行**(` ```lang ` 与 ` ``` `,
|
|
36
|
+
* 以低调的暗灰渲染),不丢弃 —— 便于一眼看出代码块边界与语言。
|
|
37
|
+
* 未标注 / 未知语言则不着色。
|
|
38
|
+
*
|
|
39
|
+
* 正文段用**空格优先**折行,代码段逐字符硬断。
|
|
65
40
|
*/
|
|
66
41
|
export declare function contentLines(kind: LineKind, key: string, text: string, width: number): RLine[];
|
|
67
|
-
/** 单个对话块 → 若干预折行 */
|
|
68
|
-
export declare function blockToLines(block: Block, columns: number): RLine[];
|
|
69
|
-
/** 流式中的 AI 块 → 若干预折行(与完成态一致:前置一个空行隔开上文) */
|
|
70
|
-
export declare function liveToLines(live: LiveAssistant, columns: number): RLine[];
|
|
71
42
|
//# sourceMappingURL=lines.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/lines.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAChB,KAAK,GACL,aAAa,GACb,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;AAEZ,gDAAgD;AAChD,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;CAChB;AAED,
|
|
1
|
+
{"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/lines.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAChB,KAAK,GACL,aAAa,GACb,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;AAEZ,gDAAgD;AAChD,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;CAChB;AAED,qBAAqB;AACrB,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAE7F;AAgHD,yCAAyC;AACzC,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,CAsB5D;AAED,oCAAoC;AACpC,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAE1E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,CAS5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAsB9F"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 行渲染原语 —— 折行 / 语法高亮 / 裁剪(与「渲染来源」解耦,纯函数)
|
|
2
2
|
import { wrapText, displayWidth } from "../text.js";
|
|
3
3
|
import { theme, syntaxStyle } from "../theme.js";
|
|
4
4
|
import { highlightCode, highlightJson, splitFences } from "../highlight.js";
|
|
@@ -30,39 +30,82 @@ function splitTokenLines(tokens) {
|
|
|
30
30
|
}
|
|
31
31
|
return rows;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
function toCells(spans) {
|
|
34
|
+
const cells = [];
|
|
35
|
+
for (const s of spans) {
|
|
36
|
+
for (const ch of s.text) {
|
|
37
|
+
cells.push({ ch, color: s.color, dim: s.dim, bold: s.bold, italic: s.italic });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return cells;
|
|
41
|
+
}
|
|
42
|
+
/** 折行单元序列 → 分段(合并相邻同样式,减少渲染节点) */
|
|
43
|
+
function cellsToSpans(cells) {
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const cell of cells) {
|
|
46
|
+
const last = out[out.length - 1];
|
|
47
|
+
if (last &&
|
|
48
|
+
last.color === cell.color &&
|
|
49
|
+
last.dim === cell.dim &&
|
|
50
|
+
last.bold === cell.bold &&
|
|
51
|
+
last.italic === cell.italic) {
|
|
52
|
+
last.text += cell.ch;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
out.push({ text: cell.ch, color: cell.color, dim: cell.dim, bold: cell.bold, italic: cell.italic });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 按显示宽度把分段折行,并合并相邻同样式分段。
|
|
62
|
+
*
|
|
63
|
+
* - `preferSpace=false`(默认,代码块):逐字符硬断,保留缩进语义;
|
|
64
|
+
* - `preferSpace=true`(散文 / markdown 正文):优先在**最近的空格处**断行,
|
|
65
|
+
* 避免把英文单词从中间切断。
|
|
66
|
+
*/
|
|
67
|
+
function wrapSpans(spans, limit, preferSpace = false) {
|
|
35
68
|
const max = Math.max(1, limit);
|
|
69
|
+
const cells = toCells(spans);
|
|
36
70
|
const rows = [];
|
|
37
71
|
let row = [];
|
|
38
72
|
let width = 0;
|
|
39
|
-
|
|
40
|
-
|
|
73
|
+
let lastSpace = -1; // 当前行内最后一个空格的下标(用于空格优先断行)
|
|
74
|
+
const reset = () => {
|
|
41
75
|
row = [];
|
|
42
76
|
width = 0;
|
|
77
|
+
lastSpace = -1;
|
|
43
78
|
};
|
|
44
|
-
for (const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
79
|
+
for (const cell of cells) {
|
|
80
|
+
const w = displayWidth(cell.ch);
|
|
81
|
+
if (row.length && width + w > max) {
|
|
82
|
+
if (preferSpace && lastSpace > 0) {
|
|
83
|
+
// 在最近的空格处断开:空格及其右侧内容折到下一行
|
|
84
|
+
rows.push(row.slice(0, lastSpace));
|
|
85
|
+
const tail = row.slice(lastSpace + 1);
|
|
86
|
+
reset();
|
|
87
|
+
row = tail;
|
|
88
|
+
width = tail.reduce((sum, c) => sum + displayWidth(c.ch), 0);
|
|
89
|
+
for (let i = tail.length - 1; i >= 0; i -= 1) {
|
|
90
|
+
if (tail[i].ch === " ") {
|
|
91
|
+
lastSpace = i;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
56
95
|
}
|
|
57
96
|
else {
|
|
58
|
-
|
|
97
|
+
rows.push(row);
|
|
98
|
+
reset();
|
|
59
99
|
}
|
|
60
|
-
width += w;
|
|
61
100
|
}
|
|
101
|
+
row.push(cell);
|
|
102
|
+
width += w;
|
|
103
|
+
if (cell.ch === " ")
|
|
104
|
+
lastSpace = row.length - 1;
|
|
62
105
|
}
|
|
63
106
|
if (row.length || rows.length === 0)
|
|
64
107
|
rows.push(row);
|
|
65
|
-
return rows;
|
|
108
|
+
return rows.map(cellsToSpans);
|
|
66
109
|
}
|
|
67
110
|
/** 按显示宽度把分段裁成**严格单行**,超出补省略号(用于工具调用行) */
|
|
68
111
|
export function clipSpans(spans, max) {
|
|
@@ -92,7 +135,7 @@ export function clipSpans(spans, max) {
|
|
|
92
135
|
return out;
|
|
93
136
|
}
|
|
94
137
|
/** 由分段拼出 RLine(`text` = 分段纯文本拼接) */
|
|
95
|
-
function spanLine(key, kind, spans) {
|
|
138
|
+
export function spanLine(key, kind, spans) {
|
|
96
139
|
return { key, kind, text: spans.map((s) => s.text).join(""), spans };
|
|
97
140
|
}
|
|
98
141
|
/**
|
|
@@ -110,89 +153,38 @@ export function toolSpans(name, args) {
|
|
|
110
153
|
return spans;
|
|
111
154
|
}
|
|
112
155
|
/**
|
|
113
|
-
* 正文 / 思考内容 →
|
|
114
|
-
*
|
|
156
|
+
* 正文 / 思考内容 → 预折行。
|
|
157
|
+
*
|
|
158
|
+
* - **普通文本段**按 **markdown 源码着色**(`highlightCode(text, "markdown")`):
|
|
159
|
+
* AI 回答本身就是 markdown,交给 highlight.js 的 markdown 词法上色,标记符(`#`/`**`/`` ` `` 等)
|
|
160
|
+
* 原样保留、只染色(不做语义渲染);
|
|
161
|
+
* - **围栏代码块**按标注语言做语法高亮,并**保留围栏行**(` ```lang ` 与 ` ``` `,
|
|
162
|
+
* 以低调的暗灰渲染),不丢弃 —— 便于一眼看出代码块边界与语言。
|
|
163
|
+
* 未标注 / 未知语言则不着色。
|
|
164
|
+
*
|
|
165
|
+
* 正文段用**空格优先**折行,代码段逐字符硬断。
|
|
115
166
|
*/
|
|
116
167
|
export function contentLines(kind, key, text, width) {
|
|
117
168
|
const out = [];
|
|
118
169
|
let n = 0;
|
|
119
170
|
for (const seg of splitFences(text)) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
171
|
+
const isCode = seg.type === "code";
|
|
172
|
+
if (isCode) {
|
|
173
|
+
// 开围栏(保留语言标注)
|
|
174
|
+
const open = seg.lang ? `\`\`\`${seg.lang}` : "```";
|
|
175
|
+
out.push(spanLine(`${key}-${n++}`, kind, [{ text: " " }, { text: open, color: theme.dim }]));
|
|
124
176
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
177
|
+
const lang = isCode ? seg.lang : "markdown";
|
|
178
|
+
for (const rowTokens of splitTokenLines(highlightCode(seg.text, lang))) {
|
|
179
|
+
for (const rowSpans of wrapSpans(tokensToSpans(rowTokens), width, !isCode)) {
|
|
180
|
+
out.push(spanLine(`${key}-${n++}`, kind, [{ text: " " }, ...rowSpans]));
|
|
130
181
|
}
|
|
131
182
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
/** 单个对话块 → 若干预折行 */
|
|
136
|
-
export function blockToLines(block, columns) {
|
|
137
|
-
const width = innerWidth(columns);
|
|
138
|
-
const id = `b${block.id}`;
|
|
139
|
-
if (block.kind === "user") {
|
|
140
|
-
return [
|
|
141
|
-
{ key: `${id}-gap`, kind: "gap", text: " " },
|
|
142
|
-
{ key: `${id}-h`, kind: "user-header", text: "❯ 你" },
|
|
143
|
-
...indentLines("user-text", id, block.text, width),
|
|
144
|
-
];
|
|
145
|
-
}
|
|
146
|
-
if (block.kind === "notice") {
|
|
147
|
-
const prefix = block.tone === "error" ? "✖ " : "· ";
|
|
148
|
-
return wrapText(block.text, width).map((line, i) => ({
|
|
149
|
-
key: `${id}-${i}`,
|
|
150
|
-
kind: block.tone === "error" ? "error" : "notice",
|
|
151
|
-
text: ` ${i === 0 ? prefix : " "}${line}`,
|
|
152
|
-
}));
|
|
153
|
-
}
|
|
154
|
-
const lines = [
|
|
155
|
-
{ key: `${id}-gap`, kind: "gap", text: " " },
|
|
156
|
-
{ key: `${id}-h`, kind: "ai-header", text: "✦ AI" },
|
|
157
|
-
];
|
|
158
|
-
if (block.reasoning.trim()) {
|
|
159
|
-
lines.push(...contentLines("reasoning", `${id}-r`, block.reasoning.trim(), width));
|
|
160
|
-
}
|
|
161
|
-
for (const tool of block.tools) {
|
|
162
|
-
if (tool.name) {
|
|
163
|
-
lines.push(spanLine(`${id}-t${lines.length}`, "tool", clipSpans(toolSpans(tool.name, tool.args), columns - 2)));
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (block.content.trim()) {
|
|
167
|
-
// 思考 / 工具与正文之间留一个空行,便于区分「过程」与「结论」
|
|
168
|
-
if (lines.length > 2)
|
|
169
|
-
lines.push({ key: `${id}-cgap`, kind: "gap", text: " " });
|
|
170
|
-
lines.push(...contentLines("content", `${id}-c`, block.content.trim(), width));
|
|
171
|
-
}
|
|
172
|
-
return lines;
|
|
173
|
-
}
|
|
174
|
-
/** 流式中的 AI 块 → 若干预折行(与完成态一致:前置一个空行隔开上文) */
|
|
175
|
-
export function liveToLines(live, columns) {
|
|
176
|
-
const width = innerWidth(columns);
|
|
177
|
-
const id = "live";
|
|
178
|
-
const lines = [
|
|
179
|
-
{ key: `${id}-gap`, kind: "gap", text: " " },
|
|
180
|
-
{ key: `${id}-h`, kind: "ai-header", text: "✦ AI" },
|
|
181
|
-
];
|
|
182
|
-
if (live.reasoning.trim()) {
|
|
183
|
-
lines.push(...contentLines("reasoning", `${id}-r`, live.reasoning.trim(), width));
|
|
184
|
-
}
|
|
185
|
-
for (const tool of live.tools) {
|
|
186
|
-
if (tool.name) {
|
|
187
|
-
lines.push(spanLine(`${id}-t${lines.length}`, "tool", clipSpans(toolSpans(tool.name, tool.args), columns - 2)));
|
|
183
|
+
if (isCode) {
|
|
184
|
+
// 闭围栏
|
|
185
|
+
out.push(spanLine(`${key}-${n++}`, kind, [{ text: " " }, { text: "```", color: theme.dim }]));
|
|
188
186
|
}
|
|
189
187
|
}
|
|
190
|
-
|
|
191
|
-
// 思考 / 工具与正文之间留一个空行,便于区分「过程」与「结论」
|
|
192
|
-
if (lines.length > 2)
|
|
193
|
-
lines.push({ key: `${id}-cgap`, kind: "gap", text: " " });
|
|
194
|
-
lines.push(...contentLines("content", `${id}-c`, live.content.trim(), width));
|
|
195
|
-
}
|
|
196
|
-
return lines;
|
|
188
|
+
return out;
|
|
197
189
|
}
|
|
198
190
|
//# sourceMappingURL=lines.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lines.js","sourceRoot":"","sources":["../../../src/tui/screens/lines.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"lines.js","sourceRoot":"","sources":["../../../src/tui/screens/lines.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AgentNS } from "@ai-zen/agents-core";
|
|
2
|
+
import { RLine } from "./lines.js";
|
|
3
|
+
/** 从消息内容提取纯文本;非文本块(图片 / 文件)以占位文本表示 */
|
|
4
|
+
export declare function messageText(content: AgentNS.MessageContent | undefined): string;
|
|
5
|
+
/** 单条消息 → 预折行(不可渲染的消息返回空数组) */
|
|
6
|
+
export declare function messageToLines(message: AgentNS.Message, columns: number): RLine[];
|
|
7
|
+
export declare function cachedMessageToLines(message: AgentNS.Message, columns: number): RLine[];
|
|
8
|
+
/** 整段转录 → 预折行 */
|
|
9
|
+
export declare function transcriptLines(messages: AgentNS.Message[], columns: number): RLine[];
|
|
10
|
+
//# sourceMappingURL=messages-view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages-view.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/messages-view.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAyE,MAAM,YAAY,CAAC;AAG1G,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,GAAG,SAAS,GAAG,MAAM,CAc/E;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,CAgDjF;AA4BD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,CAOvF;AAED,iBAAiB;AACjB,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,CAIrF"}
|