@autopilot-agent/cli 0.0.0-stage → 0.2.0
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 +52 -2
- package/dist/activity-animation.d.ts +59 -0
- package/dist/activity-animation.js +148 -0
- package/dist/animation-clock.d.ts +22 -0
- package/dist/animation-clock.js +76 -0
- package/dist/composition-root.d.ts +105 -0
- package/dist/composition-root.js +600 -0
- package/dist/context-report.d.ts +20 -0
- package/dist/context-report.js +173 -0
- package/dist/controller.d.ts +79 -0
- package/dist/controller.js +741 -0
- package/dist/design-tokens.d.ts +38 -0
- package/dist/design-tokens.js +38 -0
- package/dist/error-diagnostics.d.ts +8 -0
- package/dist/error-diagnostics.js +62 -0
- package/dist/event-projector.d.ts +26 -0
- package/dist/event-projector.js +335 -0
- package/dist/file-mention-context.d.ts +34 -0
- package/dist/file-mention-context.js +173 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +14 -0
- package/dist/main.d.ts +40 -0
- package/dist/main.js +543 -0
- package/dist/parser.d.ts +17 -0
- package/dist/parser.js +400 -0
- package/dist/renderer.d.ts +9 -0
- package/dist/renderer.js +62 -0
- package/dist/scroll-viewport.d.ts +22 -0
- package/dist/scroll-viewport.js +62 -0
- package/dist/termination-format.d.ts +4 -0
- package/dist/termination-format.js +18 -0
- package/dist/timeline-lines.d.ts +38 -0
- package/dist/timeline-lines.js +104 -0
- package/dist/tui/app.d.ts +30 -0
- package/dist/tui/app.js +413 -0
- package/dist/tui/approval.d.ts +19 -0
- package/dist/tui/approval.js +41 -0
- package/dist/tui/brand/brand-header.d.ts +20 -0
- package/dist/tui/brand/brand-header.js +22 -0
- package/dist/tui/brand/intro.d.ts +12 -0
- package/dist/tui/brand/intro.js +88 -0
- package/dist/tui/brand/pixel.d.ts +8 -0
- package/dist/tui/brand/pixel.js +50 -0
- package/dist/tui/constants.d.ts +56 -0
- package/dist/tui/constants.js +57 -0
- package/dist/tui/context.d.ts +11 -0
- package/dist/tui/context.js +12 -0
- package/dist/tui/diff-stats.d.ts +8 -0
- package/dist/tui/diff-stats.js +7 -0
- package/dist/tui/format.d.ts +75 -0
- package/dist/tui/format.js +178 -0
- package/dist/tui/hooks/use-file-mention-index.d.ts +15 -0
- package/dist/tui/hooks/use-file-mention-index.js +59 -0
- package/dist/tui/hooks/use-pickers.d.ts +47 -0
- package/dist/tui/hooks/use-pickers.js +288 -0
- package/dist/tui/hooks/use-prompt-input.d.ts +54 -0
- package/dist/tui/hooks/use-prompt-input.js +258 -0
- package/dist/tui/hooks/use-status-view.d.ts +8 -0
- package/dist/tui/hooks/use-status-view.js +13 -0
- package/dist/tui/hooks/use-viewport.d.ts +22 -0
- package/dist/tui/hooks/use-viewport.js +117 -0
- package/dist/tui/input/file-mention.d.ts +33 -0
- package/dist/tui/input/file-mention.js +112 -0
- package/dist/tui/input/graphemes.d.ts +5 -0
- package/dist/tui/input/graphemes.js +10 -0
- package/dist/tui/input/keys.d.ts +16 -0
- package/dist/tui/input/keys.js +58 -0
- package/dist/tui/input/prompt-layout.d.ts +27 -0
- package/dist/tui/input/prompt-layout.js +84 -0
- package/dist/tui/input-area.d.ts +53 -0
- package/dist/tui/input-area.js +89 -0
- package/dist/tui/jobs.d.ts +10 -0
- package/dist/tui/jobs.js +24 -0
- package/dist/tui/lifecycle.d.ts +23 -0
- package/dist/tui/lifecycle.js +114 -0
- package/dist/tui/markdown/diff.d.ts +8 -0
- package/dist/tui/markdown/diff.js +19 -0
- package/dist/tui/markdown/inline.d.ts +4 -0
- package/dist/tui/markdown/inline.js +32 -0
- package/dist/tui/markdown/markdown.d.ts +17 -0
- package/dist/tui/markdown/markdown.js +117 -0
- package/dist/tui/markdown/table.d.ts +19 -0
- package/dist/tui/markdown/table.js +74 -0
- package/dist/tui/performance-diagnostics.d.ts +7 -0
- package/dist/tui/performance-diagnostics.js +57 -0
- package/dist/tui/pickers/effort.d.ts +10 -0
- package/dist/tui/pickers/effort.js +12 -0
- package/dist/tui/pickers/file-mention.d.ts +11 -0
- package/dist/tui/pickers/file-mention.js +22 -0
- package/dist/tui/pickers/model.d.ts +10 -0
- package/dist/tui/pickers/model.js +12 -0
- package/dist/tui/pickers/permission.d.ts +10 -0
- package/dist/tui/pickers/permission.js +13 -0
- package/dist/tui/pickers/skill.d.ts +13 -0
- package/dist/tui/pickers/skill.js +25 -0
- package/dist/tui/pickers/slash.d.ts +10 -0
- package/dist/tui/pickers/slash.js +11 -0
- package/dist/tui/prompt-input.d.ts +11 -0
- package/dist/tui/prompt-input.js +73 -0
- package/dist/tui/render-mode.d.ts +20 -0
- package/dist/tui/render-mode.js +31 -0
- package/dist/tui/run.d.ts +6 -0
- package/dist/tui/run.js +237 -0
- package/dist/tui/status-line.d.ts +16 -0
- package/dist/tui/status-line.js +37 -0
- package/dist/tui/store.d.ts +4 -0
- package/dist/tui/store.js +380 -0
- package/dist/tui/timeline/live.d.ts +16 -0
- package/dist/tui/timeline/live.js +153 -0
- package/dist/tui/timeline/presentation-timeline.d.ts +15 -0
- package/dist/tui/timeline/presentation-timeline.js +128 -0
- package/dist/tui/timeline/progress.d.ts +12 -0
- package/dist/tui/timeline/progress.js +43 -0
- package/dist/tui/timeline/static-timeline.d.ts +13 -0
- package/dist/tui/timeline/static-timeline.js +131 -0
- package/dist/tui/timeline/tool-tree.d.ts +7 -0
- package/dist/tui/timeline/tool-tree.js +25 -0
- package/dist/tui/timeline/turn-items.d.ts +12 -0
- package/dist/tui/timeline/turn-items.js +28 -0
- package/dist/tui/timeline/turn.d.ts +19 -0
- package/dist/tui/timeline/turn.js +21 -0
- package/dist/tui/todo-list.d.ts +50 -0
- package/dist/tui/todo-list.js +120 -0
- package/dist/tui/transcript.d.ts +24 -0
- package/dist/tui/transcript.js +34 -0
- package/dist/tui/trust-dialog.d.ts +51 -0
- package/dist/tui/trust-dialog.js +127 -0
- package/dist/tui/types.d.ts +189 -0
- package/dist/tui/types.js +5 -0
- package/dist/tui/viewport/selection.d.ts +27 -0
- package/dist/tui/viewport/selection.js +79 -0
- package/dist/tui/viewport/sources.d.ts +29 -0
- package/dist/tui/viewport/sources.js +592 -0
- package/dist/tui/viewport/viewport.d.ts +12 -0
- package/dist/tui/viewport/viewport.js +91 -0
- package/dist/tui-input-history.d.ts +23 -0
- package/dist/tui-input-history.js +42 -0
- package/dist/tui-input-router.d.ts +24 -0
- package/dist/tui-input-router.js +115 -0
- package/dist/tui.d.ts +15 -0
- package/dist/tui.js +13 -0
- package/dist/types.d.ts +467 -0
- package/dist/types.js +7 -0
- package/package.json +58 -4
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/** 备用屏视口:按应用层视口裁剪时间线并渲染自绘滚动条。 */
|
|
3
|
+
import { memo } from "react";
|
|
4
|
+
import { Box, Text } from "ink";
|
|
5
|
+
import { CLI_TUI_COLORS, TUI_ACCENT_BG_COLOR, TUI_ACCENT_COLOR, TUI_CHIP_TEXT_COLOR, TUI_DIFF_ADDED_COLOR, TUI_DIFF_REMOVED_COLOR, TUI_TOOL_ERROR_BG_COLOR, TUI_TOOL_ERROR_COLOR, TUI_TOOL_ERROR_TEXT_COLOR } from "../../design-tokens.js";
|
|
6
|
+
import { getTuiVisualLineSelectionRange } from "./selection.js";
|
|
7
|
+
/** 将备用屏时间线的品牌样式映射回 Design.md 规定的 ANSI token。 */
|
|
8
|
+
function getTuiViewportSpanColor(style, useColor) {
|
|
9
|
+
if (!useColor)
|
|
10
|
+
return undefined;
|
|
11
|
+
if (style === "accent")
|
|
12
|
+
return CLI_TUI_COLORS.accent;
|
|
13
|
+
if (style === "brand-blue")
|
|
14
|
+
return CLI_TUI_COLORS.copilotBlue;
|
|
15
|
+
if (style === "brand-ink")
|
|
16
|
+
return CLI_TUI_COLORS.copilotInk;
|
|
17
|
+
if (style === "brand-muted")
|
|
18
|
+
return CLI_TUI_COLORS.copilotMuted;
|
|
19
|
+
if (style === "muted")
|
|
20
|
+
return CLI_TUI_COLORS.copilotMuted;
|
|
21
|
+
if (style === "diff-border" || style === "diff-meta")
|
|
22
|
+
return TUI_ACCENT_COLOR;
|
|
23
|
+
if (style === "diff-added")
|
|
24
|
+
return TUI_DIFF_ADDED_COLOR;
|
|
25
|
+
if (style === "diff-removed")
|
|
26
|
+
return TUI_DIFF_REMOVED_COLOR;
|
|
27
|
+
if (style === "error")
|
|
28
|
+
return TUI_TOOL_ERROR_COLOR;
|
|
29
|
+
if (style === "chip-active")
|
|
30
|
+
return TUI_CHIP_TEXT_COLOR;
|
|
31
|
+
if (style === "chip-error")
|
|
32
|
+
return TUI_TOOL_ERROR_TEXT_COLOR;
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
/** 取得备用屏 span 的背景与粗体强调;活动工具徽标 chip 使用强调背景 + 深色粗体文字。 */
|
|
36
|
+
function getTuiViewportSpanEmphasis(style, useColor) {
|
|
37
|
+
if (!useColor)
|
|
38
|
+
return {};
|
|
39
|
+
if (style === "chip-active")
|
|
40
|
+
return { backgroundColor: TUI_ACCENT_BG_COLOR, bold: true };
|
|
41
|
+
if (style === "chip-error")
|
|
42
|
+
return { backgroundColor: TUI_TOOL_ERROR_BG_COLOR, bold: true };
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
/** 在视口的已换行文本中保留来源样式,并与鼠标划选反显组合。 */
|
|
46
|
+
function renderTuiViewportLine(line, selection, useColor) {
|
|
47
|
+
if (line.text.length === 0)
|
|
48
|
+
return _jsx(Text, { children: " " });
|
|
49
|
+
const boundaries = new Set([0, line.text.length]);
|
|
50
|
+
if (selection) {
|
|
51
|
+
boundaries.add(selection.start);
|
|
52
|
+
boundaries.add(selection.end);
|
|
53
|
+
}
|
|
54
|
+
for (const span of line.spans) {
|
|
55
|
+
boundaries.add(span.start);
|
|
56
|
+
boundaries.add(span.end);
|
|
57
|
+
}
|
|
58
|
+
const offsets = [...boundaries].filter(function keepOffset(offset) { return offset >= 0 && offset <= line.text.length; }).sort(function sortOffset(left, right) { return left - right; });
|
|
59
|
+
return offsets.slice(0, -1).map(function renderSegment(start, index) {
|
|
60
|
+
const end = offsets[index + 1] ?? start;
|
|
61
|
+
const style = line.spans.find(function findStyle(span) { return span.start <= start && span.end >= end; })?.style;
|
|
62
|
+
const selected = selection !== undefined && start >= selection.start && end <= selection.end;
|
|
63
|
+
const emphasis = getTuiViewportSpanEmphasis(style, useColor);
|
|
64
|
+
return _jsx(Text, { color: getTuiViewportSpanColor(style, useColor), backgroundColor: emphasis.backgroundColor, bold: emphasis.bold, inverse: selected && useColor, children: line.text.slice(start, end) || " " }, `${start}:${end}`);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** 根据当前窗口比例生成一列纯文本滚动条,拇指至少占一行。 */
|
|
68
|
+
function createTuiScrollbar(lines, state) {
|
|
69
|
+
if (lines <= 0)
|
|
70
|
+
return [];
|
|
71
|
+
if (state.totalHeight <= state.viewportHeight || state.viewportHeight <= 0)
|
|
72
|
+
return Array.from({ length: lines }, function emptyScrollbar() { return " "; });
|
|
73
|
+
const thumbLength = Math.max(1, Math.min(lines, Math.round(lines * state.viewportHeight / state.totalHeight)));
|
|
74
|
+
const maximumStart = Math.max(0, lines - thumbLength);
|
|
75
|
+
const maximumTop = Math.max(1, state.totalHeight - state.viewportHeight);
|
|
76
|
+
const thumbStart = Math.min(maximumStart, Math.round(maximumStart * state.scrollTop / maximumTop));
|
|
77
|
+
return Array.from({ length: lines }, function renderScrollbar(_value, index) {
|
|
78
|
+
return index >= thumbStart && index < thumbStart + thumbLength ? "█" : "░";
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/** 在备用屏中按应用层视口裁剪时间线,并把最右一列留给自绘滚动条。 */
|
|
82
|
+
export const TuiViewportTimeline = memo(function TuiViewportTimeline(props) {
|
|
83
|
+
const visibleLines = props.lines.slice(props.state.scrollTop, props.state.scrollTop + props.state.viewportHeight);
|
|
84
|
+
const scrollbar = createTuiScrollbar(props.state.viewportHeight, props.state);
|
|
85
|
+
return (_jsxs(Box, { height: props.state.viewportHeight, width: props.columns, flexDirection: "row", overflow: "hidden", "aria-role": "list", children: [_jsx(Box, { width: Math.max(1, props.columns - 1), flexDirection: "column", overflow: "hidden", children: visibleLines.map(function renderViewportLine(line, index) {
|
|
86
|
+
const range = getTuiVisualLineSelectionRange(props.lines, props.selection, props.state.scrollTop + index);
|
|
87
|
+
return _jsx(Box, { width: Math.max(1, props.columns - 1), flexDirection: "row", overflow: "hidden", children: renderTuiViewportLine(line, range, props.useColor) }, `${line.itemId}:${line.sourceStart}:${index}`);
|
|
88
|
+
}) }), _jsx(Box, { width: 1, flexDirection: "column", overflow: "hidden", children: scrollbar.map(function renderScrollbarCell(cell, index) {
|
|
89
|
+
return _jsx(Text, { color: props.useColor ? CLI_TUI_COLORS.copilotMuted : undefined, children: cell }, index);
|
|
90
|
+
}) })] }));
|
|
91
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** TUI 会话内输入历史状态;纯函数维护草稿、FIFO 队列和上下边界召回语义。 */
|
|
2
|
+
/** 会话内输入历史的最小状态。 */
|
|
3
|
+
export interface TuiInputHistoryState {
|
|
4
|
+
entries: string[];
|
|
5
|
+
index?: number;
|
|
6
|
+
draft: string;
|
|
7
|
+
}
|
|
8
|
+
/** 历史导航动作的稳定结果。 */
|
|
9
|
+
export interface TuiInputHistoryNavigation {
|
|
10
|
+
state: TuiInputHistoryState;
|
|
11
|
+
input: string;
|
|
12
|
+
changed: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** 创建空的会话内输入历史。 */
|
|
15
|
+
export declare function createTuiInputHistoryState(): TuiInputHistoryState;
|
|
16
|
+
/** 成功提交输入后写入 FIFO,并退出历史浏览。 */
|
|
17
|
+
export declare function recordTuiInputHistory(state: TuiInputHistoryState, input: string, maximumEntries?: number): TuiInputHistoryState;
|
|
18
|
+
/** 判断上下箭头是否位于允许切换历史的视觉行边界。 */
|
|
19
|
+
export declare function canNavigateTuiInputHistory(direction: "up" | "down", cursorLine: number, lineCount: number, browsingHistory: boolean, pickerActive: boolean): boolean;
|
|
20
|
+
/** 在输入历史中向上或向下移动,并在首次进入时保存草稿。 */
|
|
21
|
+
export declare function navigateTuiInputHistory(state: TuiInputHistoryState, direction: "up" | "down", currentInput: string, enabled?: boolean): TuiInputHistoryNavigation;
|
|
22
|
+
/** 普通编辑立即退出历史浏览,并把编辑后的内容作为新的草稿。 */
|
|
23
|
+
export declare function updateTuiInputHistoryDraft(state: TuiInputHistoryState, input: string): TuiInputHistoryState;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** TUI 会话内输入历史状态;纯函数维护草稿、FIFO 队列和上下边界召回语义。 */
|
|
2
|
+
/** 创建空的会话内输入历史。 */
|
|
3
|
+
export function createTuiInputHistoryState() {
|
|
4
|
+
return { entries: [], draft: "" };
|
|
5
|
+
}
|
|
6
|
+
/** 成功提交输入后写入 FIFO,并退出历史浏览。 */
|
|
7
|
+
export function recordTuiInputHistory(state, input, maximumEntries = 100) {
|
|
8
|
+
const limit = Math.max(1, maximumEntries);
|
|
9
|
+
const entries = [...state.entries, input].slice(-limit);
|
|
10
|
+
return { entries, draft: "", index: undefined };
|
|
11
|
+
}
|
|
12
|
+
/** 判断上下箭头是否位于允许切换历史的视觉行边界。 */
|
|
13
|
+
export function canNavigateTuiInputHistory(direction, cursorLine, lineCount, browsingHistory, pickerActive) {
|
|
14
|
+
if (pickerActive)
|
|
15
|
+
return false;
|
|
16
|
+
if (direction === "up")
|
|
17
|
+
return cursorLine <= 0;
|
|
18
|
+
return browsingHistory && cursorLine >= Math.max(0, lineCount - 1);
|
|
19
|
+
}
|
|
20
|
+
/** 在输入历史中向上或向下移动,并在首次进入时保存草稿。 */
|
|
21
|
+
export function navigateTuiInputHistory(state, direction, currentInput, enabled = true) {
|
|
22
|
+
if (!enabled || state.entries.length === 0)
|
|
23
|
+
return { state, input: currentInput, changed: false };
|
|
24
|
+
if (direction === "up") {
|
|
25
|
+
const index = state.index === undefined ? state.entries.length - 1 : Math.max(0, state.index - 1);
|
|
26
|
+
const nextState = { ...state, index, draft: state.index === undefined ? currentInput : state.draft };
|
|
27
|
+
return { state: nextState, input: nextState.entries[index], changed: nextState.entries[index] !== currentInput };
|
|
28
|
+
}
|
|
29
|
+
if (state.index === undefined)
|
|
30
|
+
return { state, input: currentInput, changed: false };
|
|
31
|
+
if (state.index < state.entries.length - 1) {
|
|
32
|
+
const index = state.index + 1;
|
|
33
|
+
const nextState = { ...state, index };
|
|
34
|
+
return { state: nextState, input: nextState.entries[index], changed: nextState.entries[index] !== currentInput };
|
|
35
|
+
}
|
|
36
|
+
const nextState = { ...state, index: undefined };
|
|
37
|
+
return { state: nextState, input: nextState.draft, changed: nextState.draft !== currentInput };
|
|
38
|
+
}
|
|
39
|
+
/** 普通编辑立即退出历史浏览,并把编辑后的内容作为新的草稿。 */
|
|
40
|
+
export function updateTuiInputHistoryDraft(state, input) {
|
|
41
|
+
return { ...state, draft: input, index: undefined };
|
|
42
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** TUI 原始输入分流器;仅解析 SGR 鼠标报文,并完整保留普通键盘字节顺序。 */
|
|
2
|
+
/** 已归一化的 SGR 鼠标报文。 */
|
|
3
|
+
export interface TuiSgrMouseEvent {
|
|
4
|
+
code: number;
|
|
5
|
+
column: number;
|
|
6
|
+
row: number;
|
|
7
|
+
released: boolean;
|
|
8
|
+
kind: "wheel_up" | "wheel_down" | "button" | "drag" | "move";
|
|
9
|
+
}
|
|
10
|
+
/** 一次输入分流的无副作用结果。 */
|
|
11
|
+
export interface TuiInputRouterResult {
|
|
12
|
+
passthrough: string;
|
|
13
|
+
mouseEvents: TuiSgrMouseEvent[];
|
|
14
|
+
diagnostics: Array<"invalid_sgr_mouse" | "sgr_mouse_overflow">;
|
|
15
|
+
}
|
|
16
|
+
/** 提供跨 chunk 缓冲的原始输入分流器。 */
|
|
17
|
+
export interface TuiInputRouter {
|
|
18
|
+
push(chunk: string): TuiInputRouterResult;
|
|
19
|
+
finish(): TuiInputRouterResult;
|
|
20
|
+
}
|
|
21
|
+
/** 根据 SGR 按钮编码归类鼠标行为,供上层状态机消费。 */
|
|
22
|
+
export declare function classifyTuiSgrMouseEvent(code: number): TuiSgrMouseEvent["kind"];
|
|
23
|
+
/** 创建有界的输入分流器,避免残缺鼠标控制序列无限累积。 */
|
|
24
|
+
export declare function createTuiInputRouter(bufferLimit?: number): TuiInputRouter;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** TUI 原始输入分流器;仅解析 SGR 鼠标报文,并完整保留普通键盘字节顺序。 */
|
|
2
|
+
const SGR_MOUSE_PREFIX = "\u001b[<";
|
|
3
|
+
const X10_MOUSE_PREFIX = "\u001b[M";
|
|
4
|
+
const DEFAULT_SGR_BUFFER_LIMIT = 256;
|
|
5
|
+
/** 根据 SGR 按钮编码归类鼠标行为,供上层状态机消费。 */
|
|
6
|
+
export function classifyTuiSgrMouseEvent(code) {
|
|
7
|
+
// SGR 会把 Shift/Alt/Ctrl 叠加到按钮编码;滚轮方向仍由最低位决定。
|
|
8
|
+
// 不能只识别 64/65,否则按住修饰键或部分 Windows Terminal 报文会被误作普通点击。
|
|
9
|
+
if ((code & 64) === 64)
|
|
10
|
+
return (code & 1) === 0 ? "wheel_up" : "wheel_down";
|
|
11
|
+
if ((code & 32) === 32)
|
|
12
|
+
return (code & 3) === 3 ? "move" : "drag";
|
|
13
|
+
return "button";
|
|
14
|
+
}
|
|
15
|
+
/** 创建有界的输入分流器,避免残缺鼠标控制序列无限累积。 */
|
|
16
|
+
export function createTuiInputRouter(bufferLimit = DEFAULT_SGR_BUFFER_LIMIT) {
|
|
17
|
+
const limit = Math.max(1, bufferLimit);
|
|
18
|
+
let bufferedMousePrefix = "";
|
|
19
|
+
/** 构造统一的结果对象,避免调用方猜测未解析数据。 */
|
|
20
|
+
function createResult(passthrough = "", mouseEvents = [], diagnostics = []) {
|
|
21
|
+
return { passthrough, mouseEvents, diagnostics };
|
|
22
|
+
}
|
|
23
|
+
/** 解析一个完整 SGR 鼠标报文;格式不合法时返回 undefined。 */
|
|
24
|
+
function parseMouseSequence(sequence) {
|
|
25
|
+
const match = /^\u001b\[<(\d+);(\d+);(\d+)([Mm])$/u.exec(sequence);
|
|
26
|
+
if (!match)
|
|
27
|
+
return undefined;
|
|
28
|
+
const code = Number(match[1]);
|
|
29
|
+
return {
|
|
30
|
+
code,
|
|
31
|
+
column: Number(match[2]),
|
|
32
|
+
row: Number(match[3]),
|
|
33
|
+
released: match[4] === "m",
|
|
34
|
+
kind: classifyTuiSgrMouseEvent(code),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** 解析 X10 三字节鼠标报文;部分终端忽略 1006 时仍会以该格式回传滚轮和拖拽。 */
|
|
38
|
+
function parseX10MouseSequence(sequence) {
|
|
39
|
+
if (sequence.length !== 6 || !sequence.startsWith(X10_MOUSE_PREFIX))
|
|
40
|
+
return undefined;
|
|
41
|
+
const code = sequence.charCodeAt(3) - 32;
|
|
42
|
+
const column = sequence.charCodeAt(4) - 32;
|
|
43
|
+
const row = sequence.charCodeAt(5) - 32;
|
|
44
|
+
if (code < 0 || column < 1 || row < 1)
|
|
45
|
+
return undefined;
|
|
46
|
+
return {
|
|
47
|
+
code,
|
|
48
|
+
column,
|
|
49
|
+
row,
|
|
50
|
+
released: code === 3,
|
|
51
|
+
kind: classifyTuiSgrMouseEvent(code),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** 处理一个输入 chunk,同时只缓存确认属于 SGR 鼠标前缀的残缺序列。 */
|
|
55
|
+
function push(chunk) {
|
|
56
|
+
let source = bufferedMousePrefix + chunk;
|
|
57
|
+
bufferedMousePrefix = "";
|
|
58
|
+
let passthrough = "";
|
|
59
|
+
const mouseEvents = [];
|
|
60
|
+
const diagnostics = [];
|
|
61
|
+
while (source.length > 0) {
|
|
62
|
+
const sgrPrefixIndex = source.indexOf(SGR_MOUSE_PREFIX);
|
|
63
|
+
const x10PrefixIndex = source.indexOf(X10_MOUSE_PREFIX);
|
|
64
|
+
const prefixIndex = sgrPrefixIndex < 0
|
|
65
|
+
? x10PrefixIndex
|
|
66
|
+
: x10PrefixIndex < 0
|
|
67
|
+
? sgrPrefixIndex
|
|
68
|
+
: Math.min(sgrPrefixIndex, x10PrefixIndex);
|
|
69
|
+
if (prefixIndex < 0) {
|
|
70
|
+
passthrough += source;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
passthrough += source.slice(0, prefixIndex);
|
|
74
|
+
source = source.slice(prefixIndex);
|
|
75
|
+
if (source.startsWith(X10_MOUSE_PREFIX)) {
|
|
76
|
+
if (source.length < 6) {
|
|
77
|
+
bufferedMousePrefix = source;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
const sequence = source.slice(0, 6);
|
|
81
|
+
source = source.slice(6);
|
|
82
|
+
const mouseEvent = parseX10MouseSequence(sequence);
|
|
83
|
+
if (mouseEvent)
|
|
84
|
+
mouseEvents.push(mouseEvent);
|
|
85
|
+
else
|
|
86
|
+
diagnostics.push("invalid_sgr_mouse");
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const terminatorIndex = source.search(/[Mm]/u);
|
|
90
|
+
if (terminatorIndex < 0) {
|
|
91
|
+
if (source.length > limit)
|
|
92
|
+
diagnostics.push("sgr_mouse_overflow");
|
|
93
|
+
else
|
|
94
|
+
bufferedMousePrefix = source;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
const sequence = source.slice(0, terminatorIndex + 1);
|
|
98
|
+
source = source.slice(terminatorIndex + 1);
|
|
99
|
+
const mouseEvent = parseMouseSequence(sequence);
|
|
100
|
+
if (mouseEvent)
|
|
101
|
+
mouseEvents.push(mouseEvent);
|
|
102
|
+
else
|
|
103
|
+
diagnostics.push("invalid_sgr_mouse");
|
|
104
|
+
}
|
|
105
|
+
return createResult(passthrough, mouseEvents, diagnostics);
|
|
106
|
+
}
|
|
107
|
+
/** 在输入结束时丢弃残缺鼠标前缀,绝不把其泄漏给文本输入。 */
|
|
108
|
+
function finish() {
|
|
109
|
+
if (!bufferedMousePrefix)
|
|
110
|
+
return createResult();
|
|
111
|
+
bufferedMousePrefix = "";
|
|
112
|
+
return createResult("", [], ["invalid_sgr_mouse"]);
|
|
113
|
+
}
|
|
114
|
+
return { push, finish };
|
|
115
|
+
}
|
package/dist/tui.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Ink TUI 公开出口(barrel)。
|
|
3
|
+
* 实现已按领域拆分到 tui/ 目录;这里只 re-export 对外契约符号,
|
|
4
|
+
* 保证 index.ts、main.ts 与既有测试的 import 零改动。
|
|
5
|
+
*/
|
|
6
|
+
export type { CliTuiActivePrompt, CliTuiInitialOperation, CliTuiPendingInput, CliTuiRecap, CliTuiSnapshot, CliTuiStore, CliTuiTimelineEvent, CliTuiTranscriptItem, CliTuiTurnMetrics, RunCliTuiOptions, RunCliTuiResult, TuiMouseInputSink, TuiOutputStream, TuiRenderMode, TuiTerminalSizeSource, TuiTerminalLifecycle, } from "./tui/types.js";
|
|
7
|
+
export { createCliTuiStore } from "./tui/store.js";
|
|
8
|
+
export { formatCliTuiRecap, runCliTui } from "./tui/run.js";
|
|
9
|
+
export { createRoutedTuiStdin, createTuiMouseInputSink, createTuiRenderStdout, createTuiTerminalLifecycle } from "./tui/lifecycle.js";
|
|
10
|
+
export { createTuiViewportDiffSource, formatTuiViewportMarkdown } from "./tui/viewport/sources.js";
|
|
11
|
+
export { getTuiRenderModeFallbackNotice, resolveTuiRenderMode, resolveTuiRenderModePolicy } from "./tui/render-mode.js";
|
|
12
|
+
export { parseTuiControlAction, resolveTuiDeleteDirection } from "./tui/input/keys.js";
|
|
13
|
+
export { CliTuiApp } from "./tui/app.js";
|
|
14
|
+
export { resolveTrustDialogNavigation, runCliTrustDialog, TuiTrustDialog, TRUST_DIALOG_OPTIONS } from "./tui/trust-dialog.js";
|
|
15
|
+
export type { CliTrustDecision, CliTrustDialogOptions, CliTrustDialogSignalSource } from "./tui/trust-dialog.js";
|
package/dist/tui.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Ink TUI 公开出口(barrel)。
|
|
3
|
+
* 实现已按领域拆分到 tui/ 目录;这里只 re-export 对外契约符号,
|
|
4
|
+
* 保证 index.ts、main.ts 与既有测试的 import 零改动。
|
|
5
|
+
*/
|
|
6
|
+
export { createCliTuiStore } from "./tui/store.js";
|
|
7
|
+
export { formatCliTuiRecap, runCliTui } from "./tui/run.js";
|
|
8
|
+
export { createRoutedTuiStdin, createTuiMouseInputSink, createTuiRenderStdout, createTuiTerminalLifecycle } from "./tui/lifecycle.js";
|
|
9
|
+
export { createTuiViewportDiffSource, formatTuiViewportMarkdown } from "./tui/viewport/sources.js";
|
|
10
|
+
export { getTuiRenderModeFallbackNotice, resolveTuiRenderMode, resolveTuiRenderModePolicy } from "./tui/render-mode.js";
|
|
11
|
+
export { parseTuiControlAction, resolveTuiDeleteDirection } from "./tui/input/keys.js";
|
|
12
|
+
export { CliTuiApp } from "./tui/app.js";
|
|
13
|
+
export { resolveTrustDialogNavigation, runCliTrustDialog, TuiTrustDialog, TRUST_DIALOG_OPTIONS } from "./tui/trust-dialog.js";
|