@aiwayds/pi-think-panel 0.1.0 → 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 +36 -7
- package/extensions/pi-think-panel.ts +91 -31
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -34,18 +34,19 @@ quietly hides itself again.
|
|
|
34
34
|
|
|
35
35
|
## Layout / 布局
|
|
36
36
|
|
|
37
|
-
Small panel (overlay A) floats top-left while the model is thinking: last
|
|
38
|
-
lines
|
|
37
|
+
Small panel (overlay A) floats top-left while the model is thinking: last 5
|
|
38
|
+
lines by default (configurable via `PI_THINK_PANEL_MAX_LINES`), auto-following
|
|
39
|
+
the newest content, chat stays clean:
|
|
39
40
|
|
|
40
|
-
> 小面板(overlay A
|
|
41
|
-
>
|
|
41
|
+
> 小面板(overlay A):模型思考时浮在左上角,默认显示最近 5 行(可通过
|
|
42
|
+
> `PI_THINK_PANEL_MAX_LINES` 配置),跟随最新内容,聊天区保持干净。
|
|
42
43
|
|
|
43
44
|
```text
|
|
44
45
|
┌──────────────────────────────────────────────────────────────┐
|
|
45
46
|
│ ╭─ Thinking… ⌃O 展开 · ⌃H 隐藏 ────────────────────────╮ │
|
|
46
47
|
│ │ reasoning line 1 │ │
|
|
47
48
|
│ │ reasoning line 2 │ │
|
|
48
|
-
│ │ … (last
|
|
49
|
+
│ │ … (last 5 lines, follows newest) │ │
|
|
49
50
|
│ ╰───────────────────────────────────────────────────────╯ │
|
|
50
51
|
│ │
|
|
51
52
|
│ chat history stays clean — think text never floods it │
|
|
@@ -116,13 +117,13 @@ Note: ctrl+o is normally reserved for the tools panel (app.tools.expand); this e
|
|
|
116
117
|
|
|
117
118
|
## What it does / 功能
|
|
118
119
|
|
|
119
|
-
- Captures the model's thinking and shows the last
|
|
120
|
+
- Captures the model's thinking and shows the last N lines (default 5, configurable via `PI_THINK_PANEL_MAX_LINES`) in a bordered panel above the input editor; ctrl+o opens a wider full-text view (80% width).
|
|
120
121
|
- Auto-shows while the model is thinking (when thinking is enabled); when no thinking is happening it either hides (default `hide` mode) or keeps showing the last think text (`last` mode).
|
|
121
122
|
- ctrl+h toggles the small panel on/off.
|
|
122
123
|
- When `hideThinkingBlock` is not enabled in settings, the panel title row reminds you that think text is also visible in chat (press Ctrl+T to hide it there).
|
|
123
124
|
- Completed think blocks are separated by a `------` divider in both views — including a trailing divider after the just-finished current block.
|
|
124
125
|
|
|
125
|
-
> - 捕获模型思考内容,在输入框上方带边框面板显示最近
|
|
126
|
+
> - 捕获模型思考内容,在输入框上方带边框面板显示最近 N 行(默认 5 行,可配置 `PI_THINK_PANEL_MAX_LINES`);ctrl+o 打开更宽的全文视图(80% 宽)。
|
|
126
127
|
> - 模型思考时自动显示(思考开启时);无思考时按 `EMPTY_THINK_MODE` 隐藏(默认 `hide`)或保留最后内容(`last`)。
|
|
127
128
|
> - ctrl+h 切换小面板显示/隐藏。
|
|
128
129
|
> - 设置中 `hideThinkingBlock` 未开启时,标题行提示"聊天区也显示 think,可按 Ctrl+T 隐藏"。
|
|
@@ -142,6 +143,34 @@ const EMPTY_THINK_MODE: "last" | "hide" = "hide";
|
|
|
142
143
|
|
|
143
144
|
Change it and run `/reload` in pi.
|
|
144
145
|
|
|
146
|
+
Auto-hide delay and panel background are configurable via env vars:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
export PI_THINK_PANEL_CLOSE_DELAY_MS=5000 # hide 5s after thinking ends (default 1000)
|
|
150
|
+
export PI_THINK_PANEL_BG=toolSuccessBg # panel background (theme bg color key)
|
|
151
|
+
export PI_THINK_PANEL_MAX_LINES=5 # small-panel line count (default 5)
|
|
152
|
+
export PI_THINK_PANEL_MIN_TERM_ROWS=12 # hide small panel below this terminal height (default MAX_LINES+6)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`PI_THINK_PANEL_MAX_LINES` sets how many of the latest think lines the small
|
|
156
|
+
panel shows (positive integer, default 5).
|
|
157
|
+
|
|
158
|
+
`PI_THINK_PANEL_MIN_TERM_ROWS` is the minimum terminal height (rows) for the
|
|
159
|
+
small panel: below it the panel hides automatically (and reappears when the
|
|
160
|
+
terminal grows again) — handy when the window is dragged short or the font is
|
|
161
|
+
zoomed in. Default is MAX_LINES + 6.
|
|
162
|
+
|
|
163
|
+
> `PI_THINK_PANEL_MAX_LINES` 设置小面板显示的最新思考行数(正整数,默认 5)。
|
|
164
|
+
>
|
|
165
|
+
> `PI_THINK_PANEL_MIN_TERM_ROWS` 是小面板可见所需的最小终端高度(行数):低于
|
|
166
|
+
> 它面板自动隐藏(终端恢复后自动重现)——窗口拖矮或字体放大时很有用。默认
|
|
167
|
+
> MAX_LINES + 6。
|
|
168
|
+
|
|
169
|
+
`PI_THINK_PANEL_BG` accepts one of pi's theme bg color keys: `selectedBg` /
|
|
170
|
+
`userMessageBg` / `customMessageBg` / `toolPendingBg` / `toolSuccessBg`
|
|
171
|
+
(default) / `toolErrorBg`. Set env vars before starting pi (or before
|
|
172
|
+
`/reload`), then `/reload` to apply.
|
|
173
|
+
|
|
145
174
|
## Install / update / 安装与更新
|
|
146
175
|
|
|
147
176
|
```bash
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* auto-following new content as it streams in (a live "details" view);
|
|
10
10
|
* anchored left so it stays clear of a right-side terminal sidebar.
|
|
11
11
|
* Both overlays are nonCapturing so keyboard focus stays in the editor.
|
|
12
|
+
* Panel background color is configurable via PI_THINK_PANEL_BG (one of pi's
|
|
13
|
+
* theme bg keys, default toolSuccessBg).
|
|
12
14
|
*
|
|
13
15
|
* Keys (ctx.ui.onTerminalInput — ctrl+o / escape / x are reserved keys):
|
|
14
16
|
* ctrl+o toggle between overlay A (small) and overlay B (full text);
|
|
@@ -16,9 +18,10 @@
|
|
|
16
18
|
* ctrl+h toggle overlay A (small panel) on/off (consumed while B is closed)
|
|
17
19
|
* Closing B is another ctrl+o press (back to A); esc and x are never
|
|
18
20
|
* consumed, so typing and stream-abort keep working.
|
|
19
|
-
* Overlay A is visible while the agent is thinking and auto-hides
|
|
20
|
-
* thinking ends / the turn settles
|
|
21
|
-
* ctrl+o). If
|
|
21
|
+
* Overlay A is visible while the agent is thinking and auto-hides
|
|
22
|
+
* CLOSE_DELAY_MS (default 1s) after thinking ends / the turn settles
|
|
23
|
+
* (EMPTY_THINK_MODE "hide", unless manually opened via ctrl+o). If
|
|
24
|
+
* hideThinkingBlock is not enabled in settings, the title shows
|
|
22
25
|
* a reminder that think text is also visible in chat (Ctrl+T hides it).
|
|
23
26
|
* Never writes user settings.
|
|
24
27
|
*/
|
|
@@ -38,13 +41,71 @@ import * as path from "path";
|
|
|
38
41
|
// whitespace; tweak then /reload.
|
|
39
42
|
const PANEL_WIDTH_PCT = "90%";
|
|
40
43
|
// How many lines of think text overlay A shows (history tail + current block).
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
const
|
|
44
|
+
// Configurable via PI_THINK_PANEL_MAX_LINES (lines, default 5). Invalid
|
|
45
|
+
// values fall back to the default. Read at module load — /reload to apply.
|
|
46
|
+
const MAX_LINES = readMaxLines();
|
|
47
|
+
|
|
48
|
+
function readMaxLines(): number {
|
|
49
|
+
const raw = process.env.PI_THINK_PANEL_MAX_LINES;
|
|
50
|
+
if (raw === undefined) return 5;
|
|
51
|
+
const n = Number(raw);
|
|
52
|
+
return Number.isInteger(n) && n > 0 ? n : 5;
|
|
53
|
+
}
|
|
54
|
+
// Min terminal rows for overlay A. When the terminal is resized shorter
|
|
55
|
+
// than this (window dragged down / font zoomed in), the TUI's visible()
|
|
56
|
+
// callback hides the small panel; it reappears automatically once the
|
|
57
|
+
// terminal is tall enough again. Defaults to panel height + margin
|
|
58
|
+
// (MAX_LINES + 6); override via PI_THINK_PANEL_MIN_TERM_ROWS.
|
|
59
|
+
const MIN_TERM_ROWS = readMinTermRows();
|
|
60
|
+
|
|
61
|
+
function readMinTermRows(): number {
|
|
62
|
+
const raw = process.env.PI_THINK_PANEL_MIN_TERM_ROWS;
|
|
63
|
+
if (raw === undefined) return MAX_LINES + 6;
|
|
64
|
+
const n = Number(raw);
|
|
65
|
+
return Number.isInteger(n) && n > 0 ? n : MAX_LINES + 6;
|
|
66
|
+
}
|
|
67
|
+
// Auto-hide delay (ms) after thinking ends / the turn settles. Configurable
|
|
68
|
+
// via PI_THINK_PANEL_CLOSE_DELAY_MS (milliseconds, default 1000). Invalid
|
|
69
|
+
// values fall back to the default. Read at module load — /reload to apply.
|
|
70
|
+
const CLOSE_DELAY_MS = readCloseDelayMs();
|
|
71
|
+
|
|
72
|
+
function readCloseDelayMs(): number {
|
|
73
|
+
const raw = process.env.PI_THINK_PANEL_CLOSE_DELAY_MS;
|
|
74
|
+
if (raw === undefined) return 1000;
|
|
75
|
+
const ms = Number(raw);
|
|
76
|
+
return Number.isFinite(ms) && ms >= 0 ? Math.floor(ms) : 1000;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Background color for both overlays — one of pi's theme bg color keys
|
|
80
|
+
// (see ThemeBg in pi-coding-agent: selectedBg / userMessageBg /
|
|
81
|
+
// customMessageBg / toolPendingBg / toolSuccessBg / toolErrorBg).
|
|
82
|
+
// Configurable via PI_THINK_PANEL_BG; default "toolSuccessBg". Read at
|
|
83
|
+
// module load — /reload to apply.
|
|
84
|
+
const PANEL_BG = readPanelBg();
|
|
85
|
+
|
|
86
|
+
const THEME_BG_COLORS = [
|
|
87
|
+
"selectedBg",
|
|
88
|
+
"userMessageBg",
|
|
89
|
+
"customMessageBg",
|
|
90
|
+
"toolPendingBg",
|
|
91
|
+
"toolSuccessBg",
|
|
92
|
+
"toolErrorBg",
|
|
93
|
+
] as const;
|
|
94
|
+
type PanelBg = (typeof THEME_BG_COLORS)[number];
|
|
95
|
+
|
|
96
|
+
function readPanelBg(): PanelBg {
|
|
97
|
+
const raw = process.env.PI_THINK_PANEL_BG;
|
|
98
|
+
if (
|
|
99
|
+
raw !== undefined &&
|
|
100
|
+
(THEME_BG_COLORS as readonly string[]).includes(raw)
|
|
101
|
+
)
|
|
102
|
+
return raw as PanelBg;
|
|
103
|
+
return "toolSuccessBg";
|
|
104
|
+
}
|
|
44
105
|
|
|
45
106
|
// What to do when no thinking is happening: "last" keeps overlay A visible
|
|
46
|
-
// with the last think text; "hide" (default) auto-hides it
|
|
47
|
-
// settles. Change this and /reload to apply.
|
|
107
|
+
// with the last think text; "hide" (default) auto-hides it CLOSE_DELAY_MS
|
|
108
|
+
// after the turn settles. Change this and /reload to apply.
|
|
48
109
|
const EMPTY_THINK_MODE: "last" | "hide" = "hide";
|
|
49
110
|
|
|
50
111
|
// Layout published by pi-sidebar-panel via globalThis (same process, jiti's
|
|
@@ -182,51 +243,51 @@ function titleLine(theme: Theme, hint: string): string {
|
|
|
182
243
|
/** Overlay A (top-center panel): last MAX_LINES lines, no inner separator. */
|
|
183
244
|
function renderTopPanel(theme: Theme, width: number): string[] {
|
|
184
245
|
const innerW = Math.max(1, width - 2);
|
|
246
|
+
const bg = (s: string) => theme.bg(PANEL_BG, s);
|
|
185
247
|
const border = (s: string) => theme.fg("border", s);
|
|
186
248
|
const pad = (s: string) => truncateToWidth(s, innerW, "...", true);
|
|
249
|
+
// Rows are exactly `width` visible chars (pad=true + 2 border cols), so bg
|
|
250
|
+
// covers the full panel area including padding and borders.
|
|
251
|
+
const row = (s: string) => bg(border("│") + pad(s) + border("│"));
|
|
187
252
|
// Think lines use the chat code-block color (mdCodeBlock) + 2-space indent,
|
|
188
253
|
// matching pi's markdown code-block idiom so think text reads as code.
|
|
189
254
|
const code = (s: string) => theme.fg("mdCodeBlock", s);
|
|
190
255
|
const rows: string[] = [];
|
|
191
|
-
rows.push(border("┌" + "─".repeat(innerW) + "┐"));
|
|
192
|
-
rows.push(
|
|
193
|
-
border("│") + pad(titleLine(theme, " ⌃O 展开 · ⌃H 隐藏")) + border("│"),
|
|
194
|
-
);
|
|
256
|
+
rows.push(bg(border("┌" + "─".repeat(innerW) + "┐")));
|
|
257
|
+
rows.push(row(titleLine(theme, " ⌃O 展开 · ⌃H 隐藏")));
|
|
195
258
|
const text = fullThinkText();
|
|
196
259
|
const lines = text ? text.split(/\r?\n/).slice(-MAX_LINES) : [];
|
|
197
260
|
if (lines.length === 0) {
|
|
198
|
-
rows.push(
|
|
199
|
-
border("│") + pad(theme.fg("dim", " (no thinking yet)")) + border("│"),
|
|
200
|
-
);
|
|
261
|
+
rows.push(row(theme.fg("dim", " (no thinking yet)")));
|
|
201
262
|
} else {
|
|
202
263
|
for (const l of lines)
|
|
203
264
|
rows.push(
|
|
204
|
-
|
|
205
|
-
pad(l === "------" ? theme.fg("dim", " ------") : code(" " + l)) +
|
|
206
|
-
border("│"),
|
|
265
|
+
row(l === "------" ? theme.fg("dim", " ------") : code(" " + l)),
|
|
207
266
|
);
|
|
208
267
|
}
|
|
209
|
-
rows.push(border("└" + "─".repeat(innerW) + "┘"));
|
|
268
|
+
rows.push(bg(border("└" + "─".repeat(innerW) + "┘")));
|
|
210
269
|
return rows;
|
|
211
270
|
}
|
|
212
271
|
|
|
213
272
|
/** Overlay B (centered full-text): every line, capped so the hint stays visible. */
|
|
214
273
|
function renderFullPanel(theme: Theme, width: number): string[] {
|
|
215
274
|
const innerW = Math.max(1, width - 2);
|
|
275
|
+
const bg = (s: string) => theme.bg(PANEL_BG, s);
|
|
216
276
|
const border = (s: string) => theme.fg("border", s);
|
|
217
277
|
const pad = (s: string) => truncateToWidth(s, innerW, "...", true);
|
|
278
|
+
// Rows are exactly `width` visible chars (pad=true + 2 border cols), so bg
|
|
279
|
+
// covers the full panel area including padding and borders.
|
|
280
|
+
const row = (s: string) => bg(border("│") + pad(s) + border("│"));
|
|
218
281
|
// Think lines use the chat code-block color (mdCodeBlock) + 2-space indent,
|
|
219
282
|
// matching pi's markdown code-block idiom so think text reads as code.
|
|
220
283
|
const code = (s: string) => theme.fg("mdCodeBlock", s);
|
|
221
284
|
const rows: string[] = [];
|
|
222
|
-
rows.push(border("┌" + "─".repeat(innerW) + "┐"));
|
|
223
|
-
rows.push(
|
|
285
|
+
rows.push(bg(border("┌" + "─".repeat(innerW) + "┐")));
|
|
286
|
+
rows.push(row(titleLine(theme, " ⌃O 收起")));
|
|
224
287
|
const text = fullThinkText();
|
|
225
288
|
const lines = text ? text.split(/\r?\n/) : [];
|
|
226
289
|
if (lines.length === 0) {
|
|
227
|
-
rows.push(
|
|
228
|
-
border("│") + pad(theme.fg("dim", " (no thinking yet)")) + border("│"),
|
|
229
|
-
);
|
|
290
|
+
rows.push(row(theme.fg("dim", " (no thinking yet)")));
|
|
230
291
|
} else {
|
|
231
292
|
// maxHeight is 90% of terminal rows — cap the body so the hint (and the
|
|
232
293
|
// bottom border) are not hard-truncated by the TUI. Show the TAIL so the
|
|
@@ -236,18 +297,14 @@ function renderFullPanel(theme: Theme, width: number): string[] {
|
|
|
236
297
|
const maxBody = Math.max(2, Math.floor(termRows * 0.9) - 4);
|
|
237
298
|
const shown = lines.length > maxBody ? lines.slice(-maxBody) : lines;
|
|
238
299
|
if (lines.length > maxBody) {
|
|
239
|
-
rows.push(
|
|
240
|
-
border("│") + pad(theme.fg("dim", " …(更早内容已省略)")) + border("│"),
|
|
241
|
-
);
|
|
300
|
+
rows.push(row(theme.fg("dim", " …(更早内容已省略)")));
|
|
242
301
|
}
|
|
243
302
|
for (const l of shown)
|
|
244
303
|
rows.push(
|
|
245
|
-
|
|
246
|
-
pad(l === "------" ? theme.fg("dim", " ------") : code(" " + l)) +
|
|
247
|
-
border("│"),
|
|
304
|
+
row(l === "------" ? theme.fg("dim", " ------") : code(" " + l)),
|
|
248
305
|
);
|
|
249
306
|
}
|
|
250
|
-
rows.push(border("└" + "─".repeat(innerW) + "┘"));
|
|
307
|
+
rows.push(bg(border("└" + "─".repeat(innerW) + "┘")));
|
|
251
308
|
return rows;
|
|
252
309
|
}
|
|
253
310
|
|
|
@@ -459,6 +516,8 @@ export default function (pi: ExtensionAPI): void {
|
|
|
459
516
|
anchor: "top-left",
|
|
460
517
|
offsetX: 1,
|
|
461
518
|
offsetY: 1,
|
|
519
|
+
// Terminal too short → hide the panel (auto-restores on resize).
|
|
520
|
+
visible: (_cols, rows) => rows >= MIN_TERM_ROWS,
|
|
462
521
|
width: mountedAWidth,
|
|
463
522
|
nonCapturing: true,
|
|
464
523
|
},
|
|
@@ -538,6 +597,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
538
597
|
anchor: "top-left",
|
|
539
598
|
offsetX: 1,
|
|
540
599
|
offsetY: 1,
|
|
600
|
+
visible: (_cols, rows) => rows >= MIN_TERM_ROWS,
|
|
541
601
|
width,
|
|
542
602
|
nonCapturing: true,
|
|
543
603
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwayds/pi-think-panel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "pi-coding-agent extension — toggleable floating 'think' content panel showing live reasoning, ctrl+o for full-text view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "fan56",
|
|
15
15
|
"main": "extensions/pi-think-panel.ts",
|
|
16
|
+
"pi": {
|
|
17
|
+
"extensions": ["extensions/pi-think-panel.ts"]
|
|
18
|
+
},
|
|
16
19
|
"files": [
|
|
17
20
|
"extensions",
|
|
18
21
|
"README.md",
|