@runminton/pi-tidy-tools 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -12
- package/README.zh-CN.md +48 -12
- package/package.json +2 -3
- package/tidy-tools.ts +204 -47
- package/CHANGELOG.md +0 -16
package/README.md
CHANGED
|
@@ -2,31 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
A tidy tool-rendering extension for [Pi](https://github.com/earendil-works/pi-mono) (the pi-coding-agent TUI). It overrides the rendering of `bash`, `edit` and `write`: compact summaries by default, full official rendering when you need it.
|
|
4
4
|
|
|
5
|
+
> Display layer only. Tool execution is fully delegated to Pi's official implementations — parameters, behavior and results are untouched; the extension only decides how many rows to draw and in which colors.
|
|
6
|
+
|
|
5
7
|
[中文版](README.zh-CN.md)
|
|
6
8
|
|
|
7
9
|
## Default view
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
All three tools start **Collapsed**:
|
|
10
12
|
|
|
11
|
-
| Tool |
|
|
13
|
+
| Tool | Collapsed summary | Expanded |
|
|
12
14
|
|---|---|---|
|
|
13
|
-
| `bash` |
|
|
14
|
-
| `edit` |
|
|
15
|
-
| `write` |
|
|
15
|
+
| `bash` | `$ command` plus one output line, with an inline `... (N more lines)` suffix when truncated | complete output |
|
|
16
|
+
| `edit` | `edit path (N blocks)` + `applied +a -b` | Pi's official renderer: path, block count, live diff preview, native layout |
|
|
17
|
+
| `write` | `write path (N lines)` + `done` | Pi's official renderer: path, line count, syntax-highlighted content |
|
|
18
|
+
|
|
19
|
+
Collapsed summaries are single-line per slot: one command line plus one output line (the count suffix is inline at the end of the output line).
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
In Pi's fullscreen mode, left-click any tool block to toggle that row between its compact summary and full rendering. Click it again to collapse it. Each tool has only these two states; `bash` and `write` skip Pi's intermediate previews.
|
|
22
|
+
|
|
23
|
+
## Modes
|
|
24
|
+
|
|
25
|
+
| Mode | Behavior |
|
|
26
|
+
|---|---|
|
|
27
|
+
| compact (default) | all three tools collapsed |
|
|
28
|
+
| markdown (`/tidy-markdown` toggles) | `edit`/`write` on Markdown files (`.md`, `.mdx`, `.markdown`) expand to full content; everything else stays collapsed |
|
|
29
|
+
|
|
30
|
+
Switching modes clears shortcut state and row-local click state, then re-renders existing rows. The mode is not persisted across restarts.
|
|
18
31
|
|
|
19
32
|
## Shortcuts
|
|
20
33
|
|
|
21
|
-
|
|
34
|
+
On Mac press `Command+Option+letter` (verified; maps to the registered `Ctrl+Alt+letter`. Other platforms press `Ctrl+Alt+letter`):
|
|
35
|
+
|
|
36
|
+
| Shortcut (Mac) | Tool | Toggles between |
|
|
22
37
|
|---|---|---|
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
38
|
+
| `Command+Option+B` | `bash` | collapsed summary ↔ **one-key full output** |
|
|
39
|
+
| `Command+Option+E` | `edit` | official rendering ↔ collapsed summary |
|
|
40
|
+
| `Command+Option+W` | `write` | official rendering ↔ collapsed summary |
|
|
26
41
|
|
|
27
|
-
A notification shows the
|
|
42
|
+
A shortcut toggles all existing rows of that tool type and clears their prior row-local click state. A notification shows the new state; press again to switch back.
|
|
28
43
|
|
|
29
|
-
`bash`
|
|
44
|
+
`bash` and `write` expand to the **complete output** in one keypress — they skip Pi's built-in previews (bash's 5-line tail preview, write's 10-line preview). `edit` expands to Pi's **official rendering** (it has no preview state). The global `Ctrl+O` tool expansion does not affect these three tools.
|
|
30
45
|
|
|
31
46
|
## Commands
|
|
32
47
|
|
|
@@ -37,6 +52,7 @@ Use these commands when terminal shortcuts conflict or are not delivered:
|
|
|
37
52
|
| `/tidy-bash` | Toggle `bash` output |
|
|
38
53
|
| `/tidy-edit` | Toggle `edit` output |
|
|
39
54
|
| `/tidy-write` | Toggle `write` output |
|
|
55
|
+
| `/tidy-markdown` | Toggle markdown mode (edit/write expand for Markdown files) |
|
|
40
56
|
|
|
41
57
|
## Usage
|
|
42
58
|
|
|
@@ -73,6 +89,26 @@ If a shortcut still does nothing on your machine, check:
|
|
|
73
89
|
3. **Custom keys.** The bindings are registered in `tidy-tools.ts` via `pi.registerShortcut(...)` — edit the key strings there to rebind. Pi's built-in global expansion (`Ctrl+O`, `app.tools.expand`) is configured separately via `~/.pi/agent/keybindings.json`.
|
|
74
90
|
4. **Inspect the input.** Run `/tidy-key-debug`, then press one target shortcut. Pi shows the received bytes and any rewrite. If no notification appears, the terminal or input method consumed the key before Pi received it.
|
|
75
91
|
|
|
92
|
+
## Changelog
|
|
93
|
+
|
|
94
|
+
### 0.2.1
|
|
95
|
+
|
|
96
|
+
- Added per-block fullscreen clicking for bash/edit/write, toggling between compact and full rendering
|
|
97
|
+
- Kept tool-wide shortcuts; a shortcut applies one state to all rows of that tool type
|
|
98
|
+
|
|
99
|
+
### 0.2.0
|
|
100
|
+
|
|
101
|
+
- All three tools collapsed by default (edit/write used to expand)
|
|
102
|
+
- New `/tidy-markdown`: edit/write on Markdown files auto-expand to full content, the rest stays collapsed; switching modes clears manual toggles
|
|
103
|
+
- Write expansion skips the official 10-line preview and shows full content; global `Ctrl+O` no longer affects any of the three tools
|
|
104
|
+
- Bash collapsed output is 2 rows with the count suffix inline, including error output
|
|
105
|
+
- Fixed tab-width overflow wrapping collapsed bash output on narrow terminals; fixed doubled edit padding from a nested render shell
|
|
106
|
+
|
|
107
|
+
### 0.1.3
|
|
108
|
+
|
|
109
|
+
- New `/tidy-bash`, `/tidy-edit`, `/tidy-write`, `/tidy-key-debug` commands
|
|
110
|
+
- Normalized the legacy `ESC` + control-character shortcut form
|
|
111
|
+
|
|
76
112
|
## Development
|
|
77
113
|
|
|
78
114
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -2,31 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
为 [Pi](https://github.com/earendil-works/pi-mono)(pi-coding-agent 的 TUI)打造的工具渲染精简扩展。它覆盖了 `bash`、`edit`、`write` 的渲染:默认呈现紧凑摘要,需要时切换为官方完整渲染。
|
|
4
4
|
|
|
5
|
+
> 只动显示层,不动执行层。三个工具的执行完全委托 Pi 官方实现,参数、行为、结果一字不差;插件只决定屏幕上画几行、用什么颜色。
|
|
6
|
+
|
|
5
7
|
[English](README.md)
|
|
6
8
|
|
|
7
9
|
## 默认视图
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
三个工具默认**全部折叠**:
|
|
10
12
|
|
|
11
|
-
| 工具 |
|
|
13
|
+
| 工具 | 折叠摘要 | 展开 |
|
|
12
14
|
|---|---|---|
|
|
13
|
-
| `bash` |
|
|
14
|
-
| `edit` |
|
|
15
|
-
| `write` |
|
|
15
|
+
| `bash` | `$ command` 加一行输出预览,计数后缀 `... (N more lines)` 内联在行尾 | 完整输出 |
|
|
16
|
+
| `edit` | `edit 路径(N blocks)` + `applied +a -b` | Pi 官方渲染:路径、修改块数、实时 diff 预览、原生布局 |
|
|
17
|
+
| `write` | `write 路径(N lines)` + `done` | Pi 官方渲染:路径、行数、语法高亮内容 |
|
|
18
|
+
|
|
19
|
+
折叠摘要每槽单行:命令 1 行加输出 1 行(计数后缀内联在输出行尾)。
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
在 Pi 的 fullscreen 模式下,左键点击任意一个工具块,可单独在紧凑摘要和完整渲染之间切换。再次点击即收起。三个工具都只有这两种状态;`bash` 和 `write` 不经过官方中间预览。
|
|
22
|
+
|
|
23
|
+
## 模式
|
|
24
|
+
|
|
25
|
+
| 模式 | 行为 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| compact(默认) | 三工具全折叠 |
|
|
28
|
+
| markdown(`/tidy-markdown` 切换) | Markdown 文件(`.md`、`.mdx`、`.markdown`)的 `edit`/`write` 展开全文,其余照样折叠 |
|
|
29
|
+
|
|
30
|
+
切换模式会清空快捷键状态和各工具块的点击状态,并重渲染已有行。模式不持久化,重启后回到 compact。
|
|
18
31
|
|
|
19
32
|
## 快捷键
|
|
20
33
|
|
|
21
|
-
|
|
34
|
+
Mac 上按 `Command+Option+字母`(实测有效,对应插件注册的 `Ctrl+Alt+字母`;其他平台按 `Ctrl+Alt+字母`):
|
|
35
|
+
|
|
36
|
+
| 快捷键(Mac) | 工具 | 切换内容 |
|
|
22
37
|
|---|---|---|
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
38
|
+
| `Command+Option+B` | `bash` | 折叠摘要 ↔ **一键完整输出** |
|
|
39
|
+
| `Command+Option+E` | `edit` | 官方渲染 ↔ 折叠摘要 |
|
|
40
|
+
| `Command+Option+W` | `write` | 官方渲染 ↔ 折叠摘要 |
|
|
26
41
|
|
|
27
|
-
|
|
42
|
+
快捷键按工具类型切换所有已有工具块,并清除该工具之前的逐块点击状态。每次切换会弹出通知;再按一次切回。
|
|
28
43
|
|
|
29
|
-
`bash` 展开时**一次按键直接显示完整输出**——跳过 Pi
|
|
44
|
+
`bash` 和 `write` 展开时**一次按键直接显示完整输出**——跳过 Pi 内置预览(bash 的 5 行尾部预览、write 的 10 行预览)。`edit` 展开时使用 Pi **官方渲染**(它没有预览态)。全局 `Ctrl+O` 对这三个工具均无效。
|
|
30
45
|
|
|
31
46
|
## 命令
|
|
32
47
|
|
|
@@ -37,6 +52,7 @@
|
|
|
37
52
|
| `/tidy-bash` | 切换 `bash` 输出 |
|
|
38
53
|
| `/tidy-edit` | 切换 `edit` 输出 |
|
|
39
54
|
| `/tidy-write` | 切换 `write` 输出 |
|
|
55
|
+
| `/tidy-markdown` | 切换 Markdown 模式(Markdown 文件的 edit/write 展开) |
|
|
40
56
|
|
|
41
57
|
## 使用方法
|
|
42
58
|
|
|
@@ -73,6 +89,26 @@ Pi 会在终端支持时协商 [Kitty 键盘协议](https://sw.kovidgoyal.net/ki
|
|
|
73
89
|
3. **自定义按键。** 绑定在 `tidy-tools.ts` 里通过 `pi.registerShortcut(...)` 注册——改那里的按键字符串即可重新绑定。Pi 内置的全局展开(`Ctrl+O`,即 `app.tools.expand`)则单独通过 `~/.pi/agent/keybindings.json` 配置。
|
|
74
90
|
4. **检查输入。** 执行 `/tidy-key-debug`,再按一次目标快捷键。Pi 会显示收到的字节和改写结果。若没有通知,终端或输入法在 Pi 收到按键前已拦截它。
|
|
75
91
|
|
|
92
|
+
## 更新日志
|
|
93
|
+
|
|
94
|
+
### 0.2.1
|
|
95
|
+
|
|
96
|
+
- 支持在 Pi fullscreen 模式下点击单个 bash/edit/write 工具块,在紧凑摘要和完整渲染之间切换
|
|
97
|
+
- 保留工具级快捷键;快捷键会统一设置同类工具块
|
|
98
|
+
|
|
99
|
+
### 0.2.0 - 2026-09-05
|
|
100
|
+
|
|
101
|
+
- 默认三工具全折叠(edit/write 原默认展开)
|
|
102
|
+
- 新增 `/tidy-markdown`:Markdown 文件的 edit/write 自动展开全文,其余照样折叠;切模式清空手动钉选
|
|
103
|
+
- write 展开跳过官方 10 行预览,直接全文;全局 `Ctrl+O` 对三工具失效
|
|
104
|
+
- bash 折叠压成 2 行:计数后缀内联到输出行尾,失败输出(红色)同样单行
|
|
105
|
+
- 修 tab 宽度导致的窄终端折行;修 edit 双层外框多出的 padding
|
|
106
|
+
|
|
107
|
+
### 0.1.3
|
|
108
|
+
|
|
109
|
+
- 新增 `/tidy-bash`、`/tidy-edit`、`/tidy-write`、`/tidy-key-debug` 命令
|
|
110
|
+
- 修传统 `ESC` + 控制字符形式的快捷键归一化
|
|
111
|
+
|
|
76
112
|
## 开发
|
|
77
113
|
|
|
78
114
|
```bash
|
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runminton/pi-tidy-tools",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Collapse bash/edit/write into one-line summaries in the pi TUI; expand the official rendering per tool with Ctrl+Alt+B/W/E.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
8
|
"tidy-tools.ts",
|
|
9
9
|
"README.md",
|
|
10
|
-
"README.zh-CN.md"
|
|
11
|
-
"CHANGELOG.md"
|
|
10
|
+
"README.zh-CN.md"
|
|
12
11
|
],
|
|
13
12
|
"pi": {
|
|
14
13
|
"extensions": [
|
package/tidy-tools.ts
CHANGED
|
@@ -4,9 +4,13 @@
|
|
|
4
4
|
* Overrides the TUI rendering of bash, edit and write while delegating
|
|
5
5
|
* execution to the official implementations:
|
|
6
6
|
* - Collapsed: show a small summary with an ellipsis for long content.
|
|
7
|
-
* - Expanded:
|
|
8
|
-
*
|
|
7
|
+
* - Expanded: click an individual tool block in fullscreen mode, or use
|
|
8
|
+
* Ctrl+Alt+E (edit), Ctrl+Alt+W (write), Ctrl+Alt+B (bash) for all rows
|
|
9
|
+
* of that tool, to toggle collapsed and fully expanded rendering.
|
|
9
10
|
* - The global Ctrl+O tool expansion does not affect these three tools.
|
|
11
|
+
* - Modes: compact (default, all three collapsed) and markdown
|
|
12
|
+
* (`/tidy-markdown` toggles; edit/write on Markdown files expand,
|
|
13
|
+
* the rest stays collapsed). Not persisted across restarts.
|
|
10
14
|
*
|
|
11
15
|
* Usage:
|
|
12
16
|
* pi -e ./tidy-tools.ts
|
|
@@ -23,6 +27,7 @@ import type { EditToolDetails } from "@earendil-works/pi-coding-agent";
|
|
|
23
27
|
import {
|
|
24
28
|
Box,
|
|
25
29
|
Container,
|
|
30
|
+
MouseRegion,
|
|
26
31
|
Text,
|
|
27
32
|
type Component,
|
|
28
33
|
sliceByColumn,
|
|
@@ -32,8 +37,8 @@ import {
|
|
|
32
37
|
|
|
33
38
|
const MAX_COLLAPSED_COMMAND_LINES = 1;
|
|
34
39
|
const MAX_COLLAPSED_CONTENT_LINES = 3;
|
|
35
|
-
// bash
|
|
36
|
-
const MAX_COLLAPSED_BASH_OUTPUT_LINES =
|
|
40
|
+
// bash 折叠输出:预览与计数后缀合并为 1 行(命令 1 行 + 输出 1 行)。
|
|
41
|
+
const MAX_COLLAPSED_BASH_OUTPUT_LINES = 1;
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
44
|
* Truncate rendered rows to at most maxLines, keeping the beginning.
|
|
@@ -65,6 +70,14 @@ function firstLine(text: string | undefined): string {
|
|
|
65
70
|
return line.length > 80 ? `${line.slice(0, 77)}...` : line;
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
// 终端 tab 按 8 的倍数对齐,pi-tui 按 3 列计算。不展开 tab 会导致
|
|
74
|
+
// 行宽估算小于终端实际渲染,窄终端下折成两行。与官方 replaceTabs
|
|
75
|
+
// 保持一致:tab 展开为 3 个空格。
|
|
76
|
+
|
|
77
|
+
function expandTabs(text: string): string {
|
|
78
|
+
return text.replace(/\t/g, " ");
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
function errorText(result: { content: { type: string; text?: string }[] }): string {
|
|
69
82
|
return result.content
|
|
70
83
|
.filter((c) => c.type === "text")
|
|
@@ -119,6 +132,46 @@ class LimitedLinesText implements Component {
|
|
|
119
132
|
}
|
|
120
133
|
}
|
|
121
134
|
|
|
135
|
+
class CollapsedBashOutput implements Component {
|
|
136
|
+
// 首行预览 + 计数后缀合并为 1 行,后缀常显。预览放不下时截断并加省略标记。
|
|
137
|
+
constructor(
|
|
138
|
+
private readonly preview: string,
|
|
139
|
+
private readonly suffix: string,
|
|
140
|
+
private readonly truncatedMarker: string,
|
|
141
|
+
) {}
|
|
142
|
+
|
|
143
|
+
render(width: number): string[] {
|
|
144
|
+
if (!this.suffix) {
|
|
145
|
+
return truncateRows(
|
|
146
|
+
wrapTextWithAnsi(this.preview, Math.max(1, width)),
|
|
147
|
+
MAX_COLLAPSED_BASH_OUTPUT_LINES,
|
|
148
|
+
this.truncatedMarker,
|
|
149
|
+
width,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
const suffixWidth = visibleWidth(this.suffix);
|
|
153
|
+
if (suffixWidth >= width) {
|
|
154
|
+
return [sliceByColumn(this.suffix, 0, Math.max(1, width), true)];
|
|
155
|
+
}
|
|
156
|
+
const previewBudget = width - suffixWidth - 1;
|
|
157
|
+
if (visibleWidth(this.preview) <= previewBudget) {
|
|
158
|
+
return [`${this.preview} ${this.suffix}`];
|
|
159
|
+
}
|
|
160
|
+
const markerWidth = visibleWidth(this.truncatedMarker);
|
|
161
|
+
const preview = sliceByColumn(
|
|
162
|
+
this.preview,
|
|
163
|
+
0,
|
|
164
|
+
Math.max(0, previewBudget - markerWidth),
|
|
165
|
+
true,
|
|
166
|
+
);
|
|
167
|
+
const row = `${preview}${this.truncatedMarker} ${this.suffix}`;
|
|
168
|
+
// 极窄宽度下省略标记本身都放不下时做最终钳制,保证恒为 1 行。
|
|
169
|
+
return [visibleWidth(row) > width ? sliceByColumn(row, 0, width, true) : row];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
invalidate(): void {}
|
|
173
|
+
}
|
|
174
|
+
|
|
122
175
|
class CollapsedToolShell extends Box {
|
|
123
176
|
private callComponent?: Component;
|
|
124
177
|
private resultComponent?: Component;
|
|
@@ -160,13 +213,66 @@ function getCollapsedBackground(isPartial: boolean, isError: boolean): string {
|
|
|
160
213
|
return isError ? "toolErrorBg" : "toolSuccessBg";
|
|
161
214
|
}
|
|
162
215
|
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
216
|
+
// 显示模式:compact 下三工具默认全折叠;markdown 模式下 edit/write 遇到
|
|
217
|
+
// Markdown 文件默认展开全文,其余照样折叠。默认 compact,不持久化。
|
|
218
|
+
let markdownMode = false;
|
|
219
|
+
// 手动钉选:Ctrl+Alt+? 或 /tidy-<tool> 设置,优先于模式默认;切换模式时清空。
|
|
220
|
+
const manualExpanded = new Map<string, boolean>();
|
|
221
|
+
// Per-tool revisions let a mode or tool-wide toggle replace row-local clicks
|
|
222
|
+
// without disturbing clicked rows belonging to the other tools.
|
|
223
|
+
const expansionRevisions = new Map<string, number>();
|
|
224
|
+
|
|
225
|
+
function isMarkdownPath(value: unknown): boolean {
|
|
226
|
+
if (typeof value !== "string") return false;
|
|
227
|
+
return /\.(md|mdx|markdown)$/i.test(value.trim());
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function toolArgsMarkdown(args: any): boolean {
|
|
231
|
+
return isMarkdownPath(args?.path) || isMarkdownPath(args?.file_path);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 不带文件上下文的工具级基准(用于翻转手动钉选):compact 下全 false;
|
|
235
|
+
// markdown 模式下 edit/write 为 true(具体到文件时再按后缀细化)。
|
|
236
|
+
function modeBaseExpanded(name: string): boolean {
|
|
237
|
+
if (!markdownMode) return false;
|
|
238
|
+
return name === "edit" || name === "write";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function modeDefaultExpanded(name: string, args: any): boolean {
|
|
242
|
+
if (!markdownMode) return false;
|
|
243
|
+
if (name === "edit" || name === "write") return toolArgsMarkdown(args);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
interface RowExpansionState {
|
|
248
|
+
revision: number;
|
|
249
|
+
override?: boolean;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function getRowExpansionState(name: string, context: any): RowExpansionState {
|
|
253
|
+
const revision = expansionRevisions.get(name) ?? 0;
|
|
254
|
+
let state = context.state.tidyExpansion as RowExpansionState | undefined;
|
|
255
|
+
if (!state || state.revision !== revision) {
|
|
256
|
+
state = { revision };
|
|
257
|
+
context.state.tidyExpansion = state;
|
|
258
|
+
}
|
|
259
|
+
return state;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function isToolExpanded(name: string, args: any, context: any): boolean {
|
|
263
|
+
const rowState = getRowExpansionState(name, context);
|
|
264
|
+
return rowState.override ?? manualExpanded.get(name) ?? modeDefaultExpanded(name, args);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function makeToolClickable(component: Component, name: string, args: any, context: any): Component {
|
|
268
|
+
return new MouseRegion(component, (event) => {
|
|
269
|
+
if (event.type !== "click" || event.button !== "left") return undefined;
|
|
270
|
+
const rowState = getRowExpansionState(name, context);
|
|
271
|
+
rowState.override = !isToolExpanded(name, args, context);
|
|
272
|
+
context.invalidate();
|
|
273
|
+
return { handled: true };
|
|
274
|
+
});
|
|
275
|
+
}
|
|
170
276
|
|
|
171
277
|
// Some terminals (Kitty keyboard protocol flag-1 mode) encode Ctrl+letter as
|
|
172
278
|
// a CSI-u sequence using the control character code without a Ctrl modifier
|
|
@@ -201,14 +307,19 @@ function convertCtrlAltSequence(data: string): string | undefined {
|
|
|
201
307
|
return `\x1b[${key.charCodeAt(0)};7${eventSuffix}u`; // 7 = Ctrl|Alt (1-indexed)
|
|
202
308
|
}
|
|
203
309
|
|
|
204
|
-
function
|
|
205
|
-
toolExpanded.set(name, !(toolExpanded.get(name) ?? false));
|
|
310
|
+
function refreshToolRows(ctx: ExtensionContext): void {
|
|
206
311
|
const globalExpanded = ctx.ui.getToolsExpanded();
|
|
207
312
|
// Force every tool row to re-render. The global value is restored
|
|
208
313
|
// immediately, so only the rows' renderers refresh, not the global state.
|
|
209
314
|
ctx.ui.setToolsExpanded(!globalExpanded);
|
|
210
315
|
ctx.ui.setToolsExpanded(globalExpanded);
|
|
211
|
-
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function toggleToolExpanded(ctx: ExtensionContext, name: string): void {
|
|
319
|
+
manualExpanded.set(name, !(manualExpanded.get(name) ?? modeBaseExpanded(name)));
|
|
320
|
+
expansionRevisions.set(name, (expansionRevisions.get(name) ?? 0) + 1);
|
|
321
|
+
refreshToolRows(ctx);
|
|
322
|
+
ctx.ui.notify(`${name}: ${manualExpanded.get(name) ? "expanded" : "collapsed"}`, "info");
|
|
212
323
|
}
|
|
213
324
|
|
|
214
325
|
// Cache the official tool implementation by working directory, as pi may run
|
|
@@ -244,6 +355,9 @@ function registerCollapsibleTool(
|
|
|
244
355
|
getDef: (cwd: string) => ToolDefinition<any, any, any>,
|
|
245
356
|
collapsedCall: (args: any, theme: any) => Component,
|
|
246
357
|
collapsedResult: (result: any, options: any, theme: any, context: any) => Component,
|
|
358
|
+
// 跳过官方预览态,展开即全文(bash 行为)。为 false 时保留官方默认视图,
|
|
359
|
+
// 其内部预览 / 全文仍由全局 Ctrl+O 决定。
|
|
360
|
+
forceFullExpansion = false,
|
|
247
361
|
) {
|
|
248
362
|
const def = getDef(process.cwd());
|
|
249
363
|
const usesSelfShell = def.renderShell === "self";
|
|
@@ -256,8 +370,11 @@ function registerCollapsibleTool(
|
|
|
256
370
|
parameters: def.parameters,
|
|
257
371
|
prepareArguments: def.prepareArguments,
|
|
258
372
|
constrainedSampling: def.constrainedSampling,
|
|
259
|
-
//
|
|
260
|
-
//
|
|
373
|
+
// Mirror the official renderShell. Omitting it falls back to
|
|
374
|
+
// "default", which double-wraps "self" tools like edit in an
|
|
375
|
+
// extra Box: one extra padding line on top and bottom when
|
|
376
|
+
// expanded, and a Box-in-Box when collapsed.
|
|
377
|
+
...(usesSelfShell ? { renderShell: "self" as const } : {}),
|
|
261
378
|
|
|
262
379
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
263
380
|
return getDef(ctx.cwd).execute(toolCallId, params, signal, onUpdate, ctx);
|
|
@@ -265,15 +382,22 @@ function registerCollapsibleTool(
|
|
|
265
382
|
|
|
266
383
|
renderCall(args, theme, context) {
|
|
267
384
|
const def = getDef(context.cwd);
|
|
268
|
-
if (
|
|
269
|
-
|
|
385
|
+
if (isToolExpanded(def.name, args, context) && def.renderCall) {
|
|
386
|
+
const component = def.renderCall(
|
|
270
387
|
args as never,
|
|
271
388
|
theme as never,
|
|
272
|
-
|
|
389
|
+
// One-key full expansion: skip the official preview so
|
|
390
|
+
// the per-tool shortcut always shows complete output.
|
|
391
|
+
{
|
|
392
|
+
...context,
|
|
393
|
+
...(forceFullExpansion ? { expanded: true } : {}),
|
|
394
|
+
lastComponent: undefined,
|
|
395
|
+
} as never,
|
|
273
396
|
);
|
|
397
|
+
return makeToolClickable(component, def.name, args, context);
|
|
274
398
|
}
|
|
275
399
|
const component = collapsedCall(args, theme);
|
|
276
|
-
if (!usesSelfShell) return component;
|
|
400
|
+
if (!usesSelfShell) return makeToolClickable(component, def.name, args, context);
|
|
277
401
|
|
|
278
402
|
const shell = getCollapsedToolShell(
|
|
279
403
|
context.state,
|
|
@@ -281,21 +405,22 @@ function registerCollapsibleTool(
|
|
|
281
405
|
getCollapsedBackground(context.isPartial, context.isError),
|
|
282
406
|
);
|
|
283
407
|
shell.setCall(component);
|
|
284
|
-
return shell;
|
|
408
|
+
return makeToolClickable(shell, def.name, args, context);
|
|
285
409
|
},
|
|
286
410
|
|
|
287
411
|
renderResult(result, options, theme, context) {
|
|
288
412
|
const def = getDef(context.cwd);
|
|
289
|
-
if (
|
|
290
|
-
|
|
413
|
+
if (isToolExpanded(def.name, context.args, context) && def.renderResult) {
|
|
414
|
+
const component = def.renderResult(
|
|
291
415
|
result as never,
|
|
292
|
-
options as never,
|
|
416
|
+
{ ...options, ...(forceFullExpansion ? { expanded: true } : {}) } as never,
|
|
293
417
|
theme as never,
|
|
294
418
|
{ ...context, lastComponent: undefined } as never,
|
|
295
419
|
);
|
|
420
|
+
return makeToolClickable(component, def.name, context.args, context);
|
|
296
421
|
}
|
|
297
422
|
const component = collapsedResult(result, options, theme, context);
|
|
298
|
-
if (!usesSelfShell) return component;
|
|
423
|
+
if (!usesSelfShell) return makeToolClickable(component, def.name, context.args, context);
|
|
299
424
|
|
|
300
425
|
const shell = getCollapsedToolShell(
|
|
301
426
|
context.state,
|
|
@@ -345,36 +470,38 @@ export default function (pi: ExtensionAPI) {
|
|
|
345
470
|
},
|
|
346
471
|
|
|
347
472
|
renderCall(args, theme, context) {
|
|
348
|
-
if (
|
|
473
|
+
if (isToolExpanded("bash", args, context)) {
|
|
349
474
|
const def = getBashDef(context.cwd);
|
|
350
475
|
if (def.renderCall) {
|
|
351
|
-
|
|
476
|
+
const component = def.renderCall(
|
|
352
477
|
args as never,
|
|
353
478
|
theme as never,
|
|
354
479
|
// One-key full expansion for bash: skip the official
|
|
355
480
|
// preview so Ctrl+Alt+B shows the complete output.
|
|
356
481
|
{ ...context, expanded: true, lastComponent: undefined } as never,
|
|
357
482
|
);
|
|
483
|
+
return makeToolClickable(component, "bash", args, context);
|
|
358
484
|
}
|
|
359
485
|
}
|
|
360
486
|
|
|
361
|
-
const
|
|
487
|
+
const rawCommand = args.command || "...";
|
|
488
|
+
const command = typeof rawCommand === "string" ? expandTabs(rawCommand) : "...";
|
|
362
489
|
const timeout = args.timeout as number | undefined;
|
|
363
490
|
const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
|
|
364
491
|
const renderedCommand = theme.fg("toolTitle", theme.bold(`$ ${command}`)) + timeoutSuffix;
|
|
365
492
|
|
|
366
|
-
return new LimitedLinesText(
|
|
493
|
+
return makeToolClickable(new LimitedLinesText(
|
|
367
494
|
renderedCommand,
|
|
368
495
|
MAX_COLLAPSED_COMMAND_LINES,
|
|
369
496
|
theme.fg("muted", "..."),
|
|
370
|
-
);
|
|
497
|
+
), "bash", args, context);
|
|
371
498
|
},
|
|
372
499
|
|
|
373
500
|
renderResult(result, options, theme, context) {
|
|
374
|
-
if (
|
|
501
|
+
if (isToolExpanded("bash", context.args, context)) {
|
|
375
502
|
const def = getBashDef(context.cwd);
|
|
376
503
|
if (def.renderResult) {
|
|
377
|
-
|
|
504
|
+
const component = def.renderResult(
|
|
378
505
|
result as never,
|
|
379
506
|
// One-key full expansion for bash: skip the official
|
|
380
507
|
// preview so Ctrl+Alt+B shows the complete output.
|
|
@@ -382,31 +509,47 @@ export default function (pi: ExtensionAPI) {
|
|
|
382
509
|
theme as never,
|
|
383
510
|
{ ...context, lastComponent: undefined } as never,
|
|
384
511
|
);
|
|
512
|
+
return makeToolClickable(component, "bash", context.args, context);
|
|
385
513
|
}
|
|
386
514
|
}
|
|
387
515
|
|
|
388
|
-
const
|
|
516
|
+
const clickable = (component: Component) => makeToolClickable(component, "bash", context.args, context);
|
|
517
|
+
const output = expandTabs(errorText(result).trim().replace(/\r/g, ""));
|
|
518
|
+
const allLines = output.split("\n");
|
|
519
|
+
const first = allLines[0] ?? "";
|
|
520
|
+
const moreSuffix =
|
|
521
|
+
allLines.length > 1
|
|
522
|
+
? theme.fg("muted", `... (${allLines.length - 1} more lines)`)
|
|
523
|
+
: "";
|
|
524
|
+
// 失败输出同样压成 1 行:红色首行 + 内联计数,与成功态同形。
|
|
389
525
|
if (context.isError) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
526
|
+
if (!first) {
|
|
527
|
+
return clickable(new CollapsedBashOutput(
|
|
528
|
+
theme.fg("error", "failed"),
|
|
529
|
+
"",
|
|
530
|
+
theme.fg("muted", "..."),
|
|
531
|
+
));
|
|
532
|
+
}
|
|
533
|
+
return clickable(new CollapsedBashOutput(
|
|
534
|
+
theme.fg("error", first),
|
|
535
|
+
moreSuffix,
|
|
393
536
|
theme.fg("muted", "..."),
|
|
394
|
-
);
|
|
537
|
+
));
|
|
395
538
|
}
|
|
396
539
|
// Preview only the first line. Wrapping the full output on every
|
|
397
540
|
// frame is what made the TUI lag with large outputs.
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
theme.fg("muted",
|
|
403
|
-
);
|
|
541
|
+
if (!first) {
|
|
542
|
+
return clickable(new CollapsedBashOutput(
|
|
543
|
+
theme.fg("muted", "(no output)"),
|
|
544
|
+
"",
|
|
545
|
+
theme.fg("muted", "..."),
|
|
546
|
+
));
|
|
404
547
|
}
|
|
405
|
-
return new
|
|
406
|
-
|
|
407
|
-
|
|
548
|
+
return clickable(new CollapsedBashOutput(
|
|
549
|
+
theme.fg("toolOutput", first),
|
|
550
|
+
moreSuffix,
|
|
408
551
|
theme.fg("muted", "..."),
|
|
409
|
-
);
|
|
552
|
+
));
|
|
410
553
|
},
|
|
411
554
|
});
|
|
412
555
|
|
|
@@ -462,6 +605,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
462
605
|
}
|
|
463
606
|
return new Text(theme.fg("success", "done"), 0, 0);
|
|
464
607
|
},
|
|
608
|
+
// 展开即全文,跳过官方 10 行预览:三工具统一两档,Ctrl+O 彻底失效。
|
|
609
|
+
true,
|
|
465
610
|
);
|
|
466
611
|
|
|
467
612
|
pi.registerCommand("tidy-bash", {
|
|
@@ -476,6 +621,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
476
621
|
description: "Toggle write output expansion",
|
|
477
622
|
handler: async (_args, ctx) => toggleToolExpanded(ctx, "write"),
|
|
478
623
|
});
|
|
624
|
+
pi.registerCommand("tidy-markdown", {
|
|
625
|
+
description: "Toggle markdown mode (edit/write expand for Markdown files)",
|
|
626
|
+
handler: async (_args, ctx) => {
|
|
627
|
+
markdownMode = !markdownMode;
|
|
628
|
+
manualExpanded.clear();
|
|
629
|
+
for (const name of ["bash", "edit", "write"]) {
|
|
630
|
+
expansionRevisions.set(name, (expansionRevisions.get(name) ?? 0) + 1);
|
|
631
|
+
}
|
|
632
|
+
refreshToolRows(ctx);
|
|
633
|
+
ctx.ui.notify(`markdown mode: ${markdownMode ? "on" : "off"}`, "info");
|
|
634
|
+
},
|
|
635
|
+
});
|
|
479
636
|
pi.registerCommand("tidy-key-debug", {
|
|
480
637
|
description: "Show the next raw terminal key sequence",
|
|
481
638
|
handler: async (_args, ctx) => {
|
package/CHANGELOG.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## [0.1.3] - 2026-08-27
|
|
4
|
-
|
|
5
|
-
### Added
|
|
6
|
-
|
|
7
|
-
- Added `/tidy-bash`, `/tidy-edit`, and `/tidy-write` commands for toggling tool output.
|
|
8
|
-
- Added `/tidy-key-debug` to inspect the next terminal key sequence.
|
|
9
|
-
|
|
10
|
-
### Fixed
|
|
11
|
-
|
|
12
|
-
- Normalized the legacy `ESC` + control-character form of `Ctrl+Alt+letter` shortcuts.
|
|
13
|
-
|
|
14
|
-
### Documentation
|
|
15
|
-
|
|
16
|
-
- Documented the new commands and keyboard input debugging steps.
|