pi-pretty-tui 0.2.8 → 0.2.9
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 +5 -1
- package/README.zh-CN.md +5 -1
- package/extensions/index.ts +54 -7
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -37,6 +37,10 @@ Displayed custom messages without their own renderer use one generic accent-colo
|
|
|
37
37
|
|
|
38
38
|
Pi's direct internal `showStatus()` feedback is transient: it appears only in the status line above the editor, a newer status replaces it, and a ten-second timer removes it. It never enters the transcript or activity timeline and is cleared on reload. In clean mode, `Ctrl+T` and `Ctrl+O` only show short hints to click the corresponding disclosure; neither changes visibility or Pi's Thinking setting. In full and compact modes, both shortcuts keep Pi's native behaviour.
|
|
39
39
|
|
|
40
|
+
Visible assistant text remains outside activity trees even when a later update to the same message introduces thinking. Repeated updates do not close a separate, subsequent tool group.
|
|
41
|
+
|
|
42
|
+
`read` range labels accept numeric strings and omitted/null fields without concatenating line numbers; malformed range values omit the label. Expanded `edit` diffs use a fixed, right-aligned line-number gutter followed by a separate `+`/`-` column. Wrapped code aligns with the content column, preserving code indentation and theme diff colors.
|
|
43
|
+
|
|
40
44
|
## Error presentation
|
|
41
45
|
|
|
42
46
|
In clean mode, standalone errors keep their original title and complete text in a compact `✕ Error · reason` layout. Consecutive errors with exactly the same source type, title, and full text share one visible row with a dim `×2` / `×3` count. Other transcript components break the run; errors carrying thought/tool/visible-response content are not folded. This is UI-only: session records and original message components remain unchanged. Long errors wrap without an added preview truncation; full/compact modes keep native presentation.
|
|
@@ -88,7 +92,7 @@ The suite covers live and restored activity timelines, retries and terminal stat
|
|
|
88
92
|
|
|
89
93
|
## Compatibility notice
|
|
90
94
|
|
|
91
|
-
The current development and regression-test baseline is **Pi 1.
|
|
95
|
+
The current development and regression-test baseline is **Pi 1.3.0**. User chat bubbles support its direct Markdown layout, keeping their transparent background and content-sized width. Nested execution requires Pi 0.99.0 or later; this is a feature requirement, not a promise that every older Pi release is tested.
|
|
92
96
|
|
|
93
97
|
Tool rendering uses Pi's documented extension APIs. Active-tool state follows Pi's built-in execution transitions through its exported interactive components. Clean-mode thought details use the activity hierarchy's own disclosure state; full and compact modes retain Pi's built-in Thinking visibility control. Rendering native user messages as adaptive chat bubbles, rounding the native prompt editor, changing unordered-list markers, and enhancing fenced code blocks require reload-safe runtime patches because Pi does not currently expose public renderer hooks for those presentation details. Markdown enhancements are activated only while rendering user and assistant messages in the main transcript; Markdown created by extension overlays keeps Pi's native layout. Markdown parsing, syntax highlighting, and terminal semantic zones remain handled by Pi.
|
|
94
98
|
|
package/README.zh-CN.md
CHANGED
|
@@ -37,6 +37,10 @@ Pi 可能会提示内置工具被覆盖。这是正常现象:pi-pretty-tui 会
|
|
|
37
37
|
|
|
38
38
|
Pi 内部直接调用 `showStatus()` 产生的反馈属于临时 UI 状态:它只显示在编辑器上方,新状态替换旧状态,并由十秒 timer 自动清除;它不会进入 transcript 或活动时间线,reload 时也会清除。在 clean 模式下,`Ctrl+T` 和 `Ctrl+O` 都只显示点击对应节点的简短提示,不改变内容可见性或 Pi 的 Thinking 设置。在 full 和 compact 模式下,两者保留 Pi 的原生行为。
|
|
39
39
|
|
|
40
|
+
即使同一消息的后续更新才加入思考,可见 assistant 正文也保持在活动树之外;重复更新不会关闭后续独立的工具活动组。
|
|
41
|
+
|
|
42
|
+
`read` 行范围标签正确处理数字字符串及省略/null 参数,不再拼接出错误行号;无效范围值不显示范围标签。展开的 `edit` diff 使用固定宽度、右对齐的左侧行号栏,`+`/`-` 独占一列;长代码续行与正文列对齐,保留代码缩进及主题 diff 配色。
|
|
43
|
+
|
|
40
44
|
## 错误展示
|
|
41
45
|
|
|
42
46
|
clean 模式下,独立错误保留原始标题和完整内容,以紧凑的 `✕ Error · 原因` 展示。来源类型、标题及完整内容完全一致的相邻错误合为一条可见行,并使用暗色 `×2` / `×3` 标识次数。其他 transcript 组件会打断计数;包含思考、工具或可见回答的错误不参与合并。合并仅影响 UI,不删除或修改会话记录及原始消息组件。长错误完整换行,不增加预览截断;full/compact 模式保留原生展示。
|
|
@@ -88,7 +92,7 @@ npm test
|
|
|
88
92
|
|
|
89
93
|
## 兼容性说明
|
|
90
94
|
|
|
91
|
-
当前开发与回归测试基线为 **Pi 1.
|
|
95
|
+
当前开发与回归测试基线为 **Pi 1.3.0**。用户气泡已适配该版本直接使用 Markdown 的布局,保留透明背景与按内容自适应的宽度。嵌套执行需要 Pi 0.99.0 及以后;这是功能要求,不代表每个旧版都经过测试。
|
|
92
96
|
|
|
93
97
|
工具渲染使用 Pi 的公开扩展 API。活动工具状态通过 Pi 导出的交互组件跟随其内置执行状态变化;clean 模式的 thought 详情使用活动时间线自身的展开状态,full 和 compact 模式则保留 Pi 内置的 Thinking 可见性控制。由于 Pi 目前没有为这些展示细节提供公开渲染钩子,自适应的原生用户消息气泡、原生输入编辑器圆角、无序列表标记和围栏代码块增强需要使用可安全重载的运行时补丁。Markdown 增强仅在渲染主 transcript 中的用户消息和助手回复时启用;扩展 overlay 创建的 Markdown 保持 Pi 原生布局。Markdown 解析、语法高亮和终端语义区域仍由 Pi 处理。
|
|
94
98
|
|
package/extensions/index.ts
CHANGED
|
@@ -2462,6 +2462,22 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2462
2462
|
const nonEmptyLines = (value: string): number =>
|
|
2463
2463
|
value ? value.split("\n").filter((line) => line.length > 0).length : 0;
|
|
2464
2464
|
|
|
2465
|
+
// Normalize display values only; null means omitted, as in native Pi labels.
|
|
2466
|
+
// Malformed values hide the range rather than inventing line numbers.
|
|
2467
|
+
const readLineRange = (offset: unknown, limit: unknown): string => {
|
|
2468
|
+
if (offset == null && limit == null) return "";
|
|
2469
|
+
const parse = (value: unknown): number | undefined => {
|
|
2470
|
+
if (typeof value === "string" && value.trim() !== "") value = Number(value);
|
|
2471
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
2472
|
+
};
|
|
2473
|
+
const start = offset == null ? 1 : parse(offset);
|
|
2474
|
+
if (start === undefined) return "";
|
|
2475
|
+
if (limit == null) return ` · lines ${start}+`;
|
|
2476
|
+
const count = parse(limit);
|
|
2477
|
+
if (count === undefined) return "";
|
|
2478
|
+
return ` · lines ${start}–${start + count - 1}`;
|
|
2479
|
+
};
|
|
2480
|
+
|
|
2465
2481
|
type DisplayValue = string | (() => string);
|
|
2466
2482
|
type DisplayRow = {
|
|
2467
2483
|
prefix: DisplayValue;
|
|
@@ -2548,6 +2564,28 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2548
2564
|
return block(rows);
|
|
2549
2565
|
};
|
|
2550
2566
|
|
|
2567
|
+
// Pi diffs encode the marker before the padded line number. Move it into
|
|
2568
|
+
// its own column and keep the whole gutter out of content word wrapping.
|
|
2569
|
+
const editDiffLayout = (theme: any, diff: string): ((line: string) => DisplayRow) => {
|
|
2570
|
+
const parse = (line: string) => /^([ +\-]) *(\d+) (.*)$/.exec(line);
|
|
2571
|
+
const numberWidth = diff.split("\n").reduce((width, line) => Math.max(width, parse(line)?.[2].length ?? 0), 1);
|
|
2572
|
+
const gutterWidth = numberWidth + 3;
|
|
2573
|
+
return (line) => {
|
|
2574
|
+
const match = parse(line);
|
|
2575
|
+
const marker = match?.[1] ?? (
|
|
2576
|
+
line.startsWith("+") && !line.startsWith("+++") ? "+" :
|
|
2577
|
+
line.startsWith("-") && !line.startsWith("---") ? "-" : " "
|
|
2578
|
+
);
|
|
2579
|
+
const color = marker === "+" ? "toolDiffAdded" : marker === "-" ? "toolDiffRemoved" : "toolDiffContext";
|
|
2580
|
+
const gutter = match ? `${match[2].padStart(numberWidth)} ${marker} ` : " ".repeat(gutterWidth);
|
|
2581
|
+
return {
|
|
2582
|
+
prefix: theme.fg(color, gutter),
|
|
2583
|
+
continuation: " ".repeat(gutterWidth),
|
|
2584
|
+
content: theme.fg(color, match ? match[3] : /^\s+\.\.\.$/.test(line) ? "..." : line),
|
|
2585
|
+
};
|
|
2586
|
+
};
|
|
2587
|
+
};
|
|
2588
|
+
|
|
2551
2589
|
const result = (
|
|
2552
2590
|
theme: any,
|
|
2553
2591
|
summary: string,
|
|
@@ -2556,6 +2594,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2556
2594
|
error = false,
|
|
2557
2595
|
summaryIsStyled = false,
|
|
2558
2596
|
outputStyle: (line: string) => string = (line) => theme.fg("dim", line),
|
|
2597
|
+
outputLayout?: (line: string) => DisplayRow,
|
|
2559
2598
|
) => {
|
|
2560
2599
|
const rows: DisplayRow[] = [{
|
|
2561
2600
|
prefix: theme.fg("dim", "└ "),
|
|
@@ -2570,10 +2609,11 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2570
2609
|
const omitted = lines.length - shown.length;
|
|
2571
2610
|
for (let index = 0; index < shown.length; index++) {
|
|
2572
2611
|
const isLast = index === shown.length - 1 && omitted === 0;
|
|
2612
|
+
const layout = outputLayout?.(shown[index]);
|
|
2573
2613
|
rows.push({
|
|
2574
|
-
prefix: theme.fg("dim", isLast ? " └ " : " │ "),
|
|
2575
|
-
continuation: isLast ? " " : theme.fg("dim", " │ "),
|
|
2576
|
-
content: outputStyle(shown[index] || " "),
|
|
2614
|
+
prefix: theme.fg("dim", isLast ? " └ " : " │ ") + (layout?.prefix ?? ""),
|
|
2615
|
+
continuation: (isLast ? " " : theme.fg("dim", " │ ")) + (layout?.continuation ?? ""),
|
|
2616
|
+
content: layout?.content ?? outputStyle(shown[index] || " "),
|
|
2577
2617
|
});
|
|
2578
2618
|
}
|
|
2579
2619
|
if (omitted > 0) {
|
|
@@ -3898,7 +3938,15 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
3898
3938
|
|
|
3899
3939
|
const closeAtAssistantBoundary = (message: any, activity: string): boolean => {
|
|
3900
3940
|
const key = assistantMessageKey(message);
|
|
3901
|
-
if (assistantBoundaryKeys.has(key))
|
|
3941
|
+
if (assistantBoundaryKeys.has(key)) {
|
|
3942
|
+
// A later snapshot can introduce thinking after text already sealed this
|
|
3943
|
+
// message. Seal that newly-created thought group too, but do not let
|
|
3944
|
+
// repeated text updates close a different, subsequent tool group.
|
|
3945
|
+
const thought = activityTimeline.memberForThinking(key);
|
|
3946
|
+
const thoughtGroup = thought ? activityTimeline.groupForMember(thought.id) : undefined;
|
|
3947
|
+
if (!thoughtGroup || activityTimeline.currentGroup()?.id !== thoughtGroup.id ||
|
|
3948
|
+
activityTimeline.isSettled(thoughtGroup.id)) return false;
|
|
3949
|
+
}
|
|
3902
3950
|
assistantBoundaryKeys.add(key);
|
|
3903
3951
|
cleanRun.activity = activity;
|
|
3904
3952
|
finishCleanGroup(activity);
|
|
@@ -4181,9 +4229,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4181
4229
|
renderShell: "self",
|
|
4182
4230
|
renderCall(args: any, theme: any, context: any) {
|
|
4183
4231
|
if (hideCleanTool(context.toolCallId, context.expanded, context.executionStarted)) return cleanToolCall(theme, readLabel, context.toolCallId);
|
|
4184
|
-
const range = args.offset
|
|
4185
|
-
? ` · lines ${args.offset ?? 1}${args.limit ? `–${(args.offset ?? 1) + args.limit - 1}` : "+"}`
|
|
4186
|
-
: "";
|
|
4232
|
+
const range = readLineRange(args.offset, args.limit);
|
|
4187
4233
|
return call(theme, readLabel, `${args.path}${range}`, context.state);
|
|
4188
4234
|
},
|
|
4189
4235
|
renderResult(toolResult: any, options: any, theme: any, context: any) {
|
|
@@ -4294,6 +4340,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4294
4340
|
if (line.startsWith("-") && !line.startsWith("---")) return theme.fg("toolDiffRemoved", line);
|
|
4295
4341
|
return theme.fg("toolDiffContext", line);
|
|
4296
4342
|
},
|
|
4343
|
+
!failed && diff ? editDiffLayout(theme, diff) : undefined,
|
|
4297
4344
|
);
|
|
4298
4345
|
},
|
|
4299
4346
|
} as any);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-pretty-tui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "A beautiful, polished TUI for the Pi coding agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Kainan Yang <ykn0309@whu.edu.cn>",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"@earendil-works/pi-tui": "*"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@earendil-works/pi-coding-agent": "1.
|
|
43
|
-
"@earendil-works/pi-tui": "1.
|
|
42
|
+
"@earendil-works/pi-coding-agent": "1.3.0",
|
|
43
|
+
"@earendil-works/pi-tui": "1.3.0",
|
|
44
44
|
"@types/node": "^24.0.0",
|
|
45
45
|
"jiti": "^2.7.0",
|
|
46
46
|
"typescript": "^5.9.0"
|