@ai-zen/cli 1.0.0-alpha.2 → 1.0.0-alpha.20
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 +356 -0
- package/README.md +99 -28
- package/README.zh.md +99 -28
- package/dist/agent-creator.d.ts +11 -0
- package/dist/agent-creator.d.ts.map +1 -1
- package/dist/agent-creator.js +20 -11
- package/dist/agent-creator.js.map +1 -1
- package/dist/agents-store.d.ts.map +1 -1
- package/dist/agents-store.js +40 -3
- package/dist/agents-store.js.map +1 -1
- package/dist/auto-migrate-confirm.d.ts +43 -0
- package/dist/auto-migrate-confirm.d.ts.map +1 -0
- package/dist/auto-migrate-confirm.js +46 -0
- package/dist/auto-migrate-confirm.js.map +1 -0
- package/dist/config-editor.d.ts +4 -4
- package/dist/config-editor.d.ts.map +1 -1
- package/dist/config-editor.js +6 -8
- package/dist/config-editor.js.map +1 -1
- package/dist/config.d.ts +23 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +26 -2
- package/dist/config.js.map +1 -1
- package/dist/conversation-commands/registry.d.ts.map +1 -1
- package/dist/conversation-commands/registry.js +4 -1
- package/dist/conversation-commands/registry.js.map +1 -1
- package/dist/conversation-persist-plugin.d.ts +0 -1
- package/dist/conversation-persist-plugin.d.ts.map +1 -1
- package/dist/conversation-persist-plugin.js +2 -3
- package/dist/conversation-persist-plugin.js.map +1 -1
- package/dist/conversation-repository.d.ts +5 -1
- package/dist/conversation-repository.d.ts.map +1 -1
- package/dist/conversation-repository.js +1 -2
- package/dist/conversation-repository.js.map +1 -1
- package/dist/handoff-service.d.ts +19 -0
- package/dist/handoff-service.d.ts.map +1 -0
- package/dist/handoff-service.js +86 -0
- package/dist/handoff-service.js.map +1 -0
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/model-switch.d.ts +58 -0
- package/dist/model-switch.d.ts.map +1 -0
- package/dist/model-switch.js +61 -0
- package/dist/model-switch.js.map +1 -0
- package/dist/reasoning-effort.d.ts +77 -0
- package/dist/reasoning-effort.d.ts.map +1 -0
- package/dist/reasoning-effort.js +137 -0
- package/dist/reasoning-effort.js.map +1 -0
- package/dist/reasoning-presets.d.ts +34 -0
- package/dist/reasoning-presets.d.ts.map +1 -0
- package/dist/reasoning-presets.js +85 -0
- package/dist/reasoning-presets.js.map +1 -0
- package/dist/sdk-log.d.ts +48 -0
- package/dist/sdk-log.d.ts.map +1 -0
- package/dist/sdk-log.js +67 -0
- package/dist/sdk-log.js.map +1 -0
- 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/stdio-runner.d.ts.map +1 -1
- package/dist/stdio-runner.js +15 -13
- package/dist/stdio-runner.js.map +1 -1
- package/dist/sub-agent-guard-plugin.d.ts +5 -5
- package/dist/sub-agent-guard-plugin.d.ts.map +1 -1
- package/dist/sub-agent-guard-plugin.js +7 -7
- package/dist/sub-agent-guard-plugin.js.map +1 -1
- package/dist/tui/chat-session.d.ts +83 -59
- package/dist/tui/chat-session.d.ts.map +1 -1
- package/dist/tui/chat-session.js +161 -105
- package/dist/tui/chat-session.js.map +1 -1
- package/dist/tui/components.d.ts +7 -2
- package/dist/tui/components.d.ts.map +1 -1
- package/dist/tui/components.js +3 -3
- package/dist/tui/components.js.map +1 -1
- package/dist/tui/config-wizard/engine.js +1 -1
- package/dist/tui/config-wizard/engine.js.map +1 -1
- package/dist/tui/config-wizard/fields.d.ts +3 -1
- package/dist/tui/config-wizard/fields.d.ts.map +1 -1
- package/dist/tui/config-wizard/fields.js +58 -6
- package/dist/tui/config-wizard/fields.js.map +1 -1
- package/dist/tui/config-wizard/menu.d.ts.map +1 -1
- package/dist/tui/config-wizard/menu.js +2 -3
- package/dist/tui/config-wizard/menu.js.map +1 -1
- package/dist/tui/config-wizard/step-defaults.d.ts.map +1 -1
- package/dist/tui/config-wizard/step-defaults.js +4 -11
- package/dist/tui/config-wizard/step-defaults.js.map +1 -1
- package/dist/tui/config-wizard/types.d.ts +18 -3
- 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 +87 -40
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/input-editing.d.ts +49 -0
- package/dist/tui/input-editing.d.ts.map +1 -0
- package/dist/tui/input-editing.js +87 -0
- package/dist/tui/input-editing.js.map +1 -0
- package/dist/tui/key-debug.d.ts +62 -0
- package/dist/tui/key-debug.d.ts.map +1 -0
- package/dist/tui/key-debug.js +92 -0
- package/dist/tui/key-debug.js.map +1 -0
- package/dist/tui/mouse.d.ts +55 -0
- package/dist/tui/mouse.d.ts.map +1 -0
- package/dist/tui/mouse.js +74 -0
- package/dist/tui/mouse.js.map +1 -0
- package/dist/tui/screens/chat-commands.d.ts +12 -2
- package/dist/tui/screens/chat-commands.d.ts.map +1 -1
- package/dist/tui/screens/chat-commands.js +204 -17
- package/dist/tui/screens/chat-commands.js.map +1 -1
- package/dist/tui/screens/chat.d.ts +2 -5
- package/dist/tui/screens/chat.d.ts.map +1 -1
- package/dist/tui/screens/chat.js +460 -98
- 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/handoff-choice.d.ts +37 -0
- package/dist/tui/screens/handoff-choice.d.ts.map +1 -0
- package/dist/tui/screens/handoff-choice.js +41 -0
- package/dist/tui/screens/handoff-choice.js.map +1 -0
- package/dist/tui/screens/index.d.ts +8 -8
- package/dist/tui/screens/index.d.ts.map +1 -1
- package/dist/tui/screens/index.js +7 -7
- package/dist/tui/screens/index.js.map +1 -1
- package/dist/tui/screens/input.d.ts +35 -0
- package/dist/tui/screens/input.d.ts.map +1 -1
- package/dist/tui/screens/input.js +66 -0
- package/dist/tui/screens/input.js.map +1 -1
- package/dist/tui/screens/layout.d.ts +13 -3
- package/dist/tui/screens/layout.d.ts.map +1 -1
- package/dist/tui/screens/layout.js +15 -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/screens/splash.d.ts.map +1 -1
- package/dist/tui/screens/splash.js +7 -1
- package/dist/tui/screens/splash.js.map +1 -1
- package/dist/tui/text.d.ts +12 -2
- package/dist/tui/text.d.ts.map +1 -1
- package/dist/tui/text.js +17 -3
- 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/dist/types.d.ts +7 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/auto-migrate-confirm-plugin.d.ts +0 -58
- package/dist/auto-migrate-confirm-plugin.d.ts.map +0 -1
- package/dist/auto-migrate-confirm-plugin.js +0 -61
- package/dist/auto-migrate-confirm-plugin.js.map +0 -1
- package/dist/migration-service.d.ts +0 -15
- package/dist/migration-service.d.ts.map +0 -1
- package/dist/migration-service.js +0 -59
- package/dist/migration-service.js.map +0 -1
- 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
|
@@ -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;
|
|
@@ -7,6 +5,18 @@ export const CONFIRM_LINES = 6;
|
|
|
7
5
|
export function pickerLines(itemCount) {
|
|
8
6
|
return 7 + itemCount;
|
|
9
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* 把「想要的视口顶部」钳制成合法值:下限 0、上限 `totalLines - budget`。
|
|
10
|
+
*
|
|
11
|
+
* 返回 `null` 表示**已贴底**(视口顶恰好等于最大顶,或内容不足一屏)—— 调用方据此
|
|
12
|
+
* 清掉滚动状态,让视口重新跟随最新内容。所有滚动动作(翻页 / 按行 / 鼠标滚轮)都
|
|
13
|
+
* 走这一个函数,越界方向自然被吃掉,不会出现「滚过头回不来」。
|
|
14
|
+
*/
|
|
15
|
+
export function clampViewTop(totalLines, budget, nextTop) {
|
|
16
|
+
const maxTop = Math.max(0, totalLines - budget);
|
|
17
|
+
const clamped = Math.max(0, Math.min(maxTop, nextTop));
|
|
18
|
+
return clamped >= maxTop ? null : clamped;
|
|
19
|
+
}
|
|
10
20
|
/**
|
|
11
21
|
* 计算对话屏布局:底部区域高度固定后,把「对话行」按视口截取到可用高度,顶部用空行
|
|
12
22
|
* 补齐 —— 使整帧高度恒等于传入的 `rows`(调用方以 `usableFrameRows()` 预留末行),
|
|
@@ -16,19 +26,16 @@ export function pickerLines(itemCount) {
|
|
|
16
26
|
* 传入具体值时按 [0, totalLines - budget] 钳制,用于「应用内翻页」回看更早的历史。
|
|
17
27
|
*/
|
|
18
28
|
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));
|
|
29
|
+
const transcript = input.transcript;
|
|
24
30
|
const inputHeight = input.busy ? 1 : inputLines(input.input, input.columns).length;
|
|
25
31
|
const overlayHeight = input.overlay === "confirm"
|
|
26
32
|
? CONFIRM_LINES
|
|
27
33
|
: input.overlay === "picker"
|
|
28
34
|
? pickerLines(input.pickerItems)
|
|
29
35
|
: 0;
|
|
36
|
+
const noticeCount = input.noticeCount ?? 0;
|
|
30
37
|
const bottomLines = input.overlay === "none"
|
|
31
|
-
? 1 /* 分隔线 */ + input.hintCount + inputHeight + 1 /* 状态栏 */
|
|
38
|
+
? noticeCount /* 通知栏 */ + 1 /* 分隔线 */ + input.hintCount + inputHeight + 1 /* 状态栏 */
|
|
32
39
|
: overlayHeight + 1 /* 状态栏 */;
|
|
33
40
|
const budget = Math.max(0, input.rows - bottomLines);
|
|
34
41
|
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":"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"}
|
|
@@ -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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// 渲染层 —— 直接以 SDK 的 `AgentNS.Message[]` 为唯一数据源产出 RLine(不经过任何自定义中间模型)
|
|
2
|
+
//
|
|
3
|
+
// 顺序即真相:遍历 messages 数组(跨消息天然有序);单条 assistant 内恒为
|
|
4
|
+
// `reasoning_content → tool_calls → content`。因此 **不需要** 自定义 Block / LiveAssistant,
|
|
5
|
+
// 也不需要把流式 delta 翻译成中间事件 —— 流式中 SDK 会实时更新「当前那条 message」,
|
|
6
|
+
// 直接读它即可。
|
|
7
|
+
import { AgentNS } from "@ai-zen/agents-core";
|
|
8
|
+
import { contentLines, indentLines, clipSpans, toolSpans, spanLine, innerWidth } from "./lines.js";
|
|
9
|
+
import { stripCwdNote } from "../text.js";
|
|
10
|
+
/** 从消息内容提取纯文本;非文本块(图片 / 文件)以占位文本表示 */
|
|
11
|
+
export function messageText(content) {
|
|
12
|
+
if (content == null)
|
|
13
|
+
return "";
|
|
14
|
+
if (typeof content === "string")
|
|
15
|
+
return content;
|
|
16
|
+
return content
|
|
17
|
+
.map((part) => {
|
|
18
|
+
if (part.type === "text")
|
|
19
|
+
return part.text;
|
|
20
|
+
if (part.type === "image_url")
|
|
21
|
+
return `[图片: ${part.image_url.url}]`;
|
|
22
|
+
if (part.type === "file") {
|
|
23
|
+
const file = part;
|
|
24
|
+
return `[文件: ${file.file_id ?? file.filename ?? "内联文件"}]`;
|
|
25
|
+
}
|
|
26
|
+
return "";
|
|
27
|
+
})
|
|
28
|
+
.join("");
|
|
29
|
+
}
|
|
30
|
+
/** 单条消息 → 预折行(不可渲染的消息返回空数组) */
|
|
31
|
+
export function messageToLines(message, columns) {
|
|
32
|
+
if (message.hidden || message.omit)
|
|
33
|
+
return [];
|
|
34
|
+
const key = `m${message.id}`;
|
|
35
|
+
const width = innerWidth(columns);
|
|
36
|
+
if (message.role === AgentNS.Role.User) {
|
|
37
|
+
const text = stripCwdNote(messageText(message.content));
|
|
38
|
+
if (!text.trim())
|
|
39
|
+
return [];
|
|
40
|
+
return [
|
|
41
|
+
{ key: `${key}-gap`, kind: "gap", text: " " },
|
|
42
|
+
{ key: `${key}-h`, kind: "user-header", text: "❯ 你" },
|
|
43
|
+
...indentLines("user-text", key, text, width),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
if (message.role === AgentNS.Role.Assistant) {
|
|
47
|
+
const reasoning = (message.reasoning_content ?? "").trim();
|
|
48
|
+
const content = messageText(message.content).trim();
|
|
49
|
+
const tools = (message.tool_calls ?? []).filter((t) => t?.function?.name);
|
|
50
|
+
// 占位(流式尚未产出任何内容):不渲染,避免空标题
|
|
51
|
+
if (!reasoning && !content && tools.length === 0)
|
|
52
|
+
return [];
|
|
53
|
+
const lines = [
|
|
54
|
+
{ key: `${key}-gap`, kind: "gap", text: " " },
|
|
55
|
+
{ key: `${key}-h`, kind: "ai-header", text: "✦ AI" },
|
|
56
|
+
];
|
|
57
|
+
if (reasoning) {
|
|
58
|
+
lines.push(...contentLines("reasoning", `${key}-r`, reasoning, width));
|
|
59
|
+
}
|
|
60
|
+
if (reasoning && tools.length > 0) {
|
|
61
|
+
// 思考与工具调用之间留一个空行,区分「过程」与「动作」
|
|
62
|
+
lines.push({ key: `${key}-tg`, kind: "gap", text: " " });
|
|
63
|
+
}
|
|
64
|
+
tools.forEach((tool, i) => {
|
|
65
|
+
const name = tool.function?.name ?? "";
|
|
66
|
+
const args = tool.function?.arguments ?? "";
|
|
67
|
+
lines.push(spanLine(`${key}-t${i}`, "tool", clipSpans(toolSpans(name, args), columns - 2)));
|
|
68
|
+
});
|
|
69
|
+
if (content) {
|
|
70
|
+
// 思考 / 工具与正文之间留一个空行,便于区分「过程」与「结论」
|
|
71
|
+
if (lines.length > 2)
|
|
72
|
+
lines.push({ key: `${key}-cgap`, kind: "gap", text: " " });
|
|
73
|
+
lines.push(...contentLines("content", `${key}-c`, content, width));
|
|
74
|
+
}
|
|
75
|
+
return lines;
|
|
76
|
+
}
|
|
77
|
+
// system / tool(工具结果)/ function:不进转录
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
/** 内容签名:流式中同一条消息会持续增长,用它判断是否需要重算 */
|
|
81
|
+
function signature(message) {
|
|
82
|
+
const reasoning = message.reasoning_content?.length ?? 0;
|
|
83
|
+
const content = typeof message.content === "string"
|
|
84
|
+
? message.content.length
|
|
85
|
+
: Array.isArray(message.content)
|
|
86
|
+
? message.content.length
|
|
87
|
+
: 0;
|
|
88
|
+
const tools = message.tool_calls?.reduce((sum, t) => sum + (t.function?.name?.length ?? 0) + (t.function?.arguments?.length ?? 0), 0) ?? 0;
|
|
89
|
+
return `${reasoning}:${content}:${tools}:${message.status ?? ""}`;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* `messageToLines` 的缓存包装。
|
|
93
|
+
*
|
|
94
|
+
* 已完成消息的渲染结果按「消息对象 + 列宽 + 内容签名」缓存:流式期间每帧都要重排整个转录,
|
|
95
|
+
* 命中缓存后历史消息的折行 / 高亮全部免算。用签名(而非仅对象身份)兜住「流式中那条正在
|
|
96
|
+
* 增长的消息」——签名变了就重算,避免拿到陈旧结果。
|
|
97
|
+
*/
|
|
98
|
+
const messageLineCache = new WeakMap();
|
|
99
|
+
export function cachedMessageToLines(message, columns) {
|
|
100
|
+
const sig = signature(message);
|
|
101
|
+
const hit = messageLineCache.get(message);
|
|
102
|
+
if (hit && hit.columns === columns && hit.sig === sig)
|
|
103
|
+
return hit.lines;
|
|
104
|
+
const lines = messageToLines(message, columns);
|
|
105
|
+
messageLineCache.set(message, { columns, sig, lines });
|
|
106
|
+
return lines;
|
|
107
|
+
}
|
|
108
|
+
/** 整段转录 → 预折行 */
|
|
109
|
+
export function transcriptLines(messages, columns) {
|
|
110
|
+
const out = [];
|
|
111
|
+
for (const message of messages)
|
|
112
|
+
out.push(...cachedMessageToLines(message, columns));
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=messages-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages-view.js","sourceRoot":"","sources":["../../../src/tui/screens/messages-view.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通知通道 —— 系统级提示(notice / error)的旁路,与对话转录严格分离。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不能塞进 `agent.messages`:那份数组会被 `formatHistory()` **原样发给模型**,
|
|
5
|
+
* 把「配置已更新 / 已中止 / 保存失败」这类 UI 提示写进去,等于往上下文里灌噪声,
|
|
6
|
+
* 甚至带偏模型行为。所以 notice / error 走这条独立通道,由对话屏订阅后渲染到
|
|
7
|
+
* 输入框上方的独立栏位,**绝不进入 messages**。
|
|
8
|
+
*
|
|
9
|
+
* 纯逻辑、不依赖 React,可独立单测。
|
|
10
|
+
*/
|
|
11
|
+
export interface Notice {
|
|
12
|
+
text: string;
|
|
13
|
+
tone: "info" | "error";
|
|
14
|
+
/** 递增序号:同一文本重复推送也能被 React 依赖感知为「新通知」 */
|
|
15
|
+
seq: number;
|
|
16
|
+
}
|
|
17
|
+
export type NoticeListener = (notice: Notice) => void;
|
|
18
|
+
/** 通知通道:`push` 广播,屏订阅。 */
|
|
19
|
+
export declare class NoticeChannel {
|
|
20
|
+
private seq;
|
|
21
|
+
private readonly listeners;
|
|
22
|
+
push(text: string, tone?: "info" | "error"): void;
|
|
23
|
+
subscribe(listener: NoticeListener): () => void;
|
|
24
|
+
}
|
|
25
|
+
/** 通知栏最多占用的行数(超出以 … 收尾),避免长输出把对话区挤没 */
|
|
26
|
+
export declare const MAX_NOTICE_LINES = 8;
|
|
27
|
+
/**
|
|
28
|
+
* 通知 → 栏位文本行(按显示宽度折行、封顶)。
|
|
29
|
+
* 首行带前缀(info `· ` / error `✖ `),其余行对齐缩进。
|
|
30
|
+
*/
|
|
31
|
+
export declare function noticeLines(notice: Notice, columns: number): string[];
|
|
32
|
+
//# sourceMappingURL=notice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notice.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAEtD,0BAA0B;AAC1B,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IAEvD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,GAAG,OAAgB,GAAG,IAAI;IAKzD,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI;CAMhD;AAED,uCAAuC;AACvC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAUrE"}
|