@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 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 10
38
- lines, auto-following the newest content, chat stays clean:
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):模型思考时浮在左上角,显示最近 10 行,跟随最新内容,
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 10 lines, follows newest) │ │
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 10 lines in a bordered panel above the input editor; ctrl+o opens a wider full-text view (80% width).
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
- > - 捕获模型思考内容,在输入框上方带边框面板显示最近 10 行;ctrl+o 打开更宽的全文视图(80% 宽)。
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 10s after
20
- * thinking ends / the turn settles (EMPTY_THINK_MODE "hide", unless manually opened via
21
- * ctrl+o). If hideThinkingBlock is not enabled in settings, the title shows
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
- const MAX_LINES = 10;
42
- // Auto-hide delay (ms) after thinking ends / the turn settles.
43
- const CLOSE_DELAY_MS = 10000;
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 10s after the turn
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
- border("") +
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(border("│") + pad(titleLine(theme, " ⌃O 收起")) + border("│"));
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
- border("") +
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.1.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",