@runminton/pi-tidy-tools 0.2.0 → 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 CHANGED
@@ -18,6 +18,8 @@ All three tools start **Collapsed**:
18
18
 
19
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).
20
20
 
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
+
21
23
  ## Modes
22
24
 
23
25
  | Mode | Behavior |
@@ -25,7 +27,7 @@ Collapsed summaries are single-line per slot: one command line plus one output l
25
27
  | compact (default) | all three tools collapsed |
26
28
  | markdown (`/tidy-markdown` toggles) | `edit`/`write` on Markdown files (`.md`, `.mdx`, `.markdown`) expand to full content; everything else stays collapsed |
27
29
 
28
- Switching modes clears per-tool manual toggles and re-renders existing rows. The mode is not persisted across restarts.
30
+ Switching modes clears shortcut state and row-local click state, then re-renders existing rows. The mode is not persisted across restarts.
29
31
 
30
32
  ## Shortcuts
31
33
 
@@ -37,7 +39,7 @@ On Mac press `Command+Option+letter` (verified; maps to the registered `Ctrl+Alt
37
39
  | `Command+Option+E` | `edit` | official rendering ↔ collapsed summary |
38
40
  | `Command+Option+W` | `write` | official rendering ↔ collapsed summary |
39
41
 
40
- A notification shows the current state on each toggle; press again to switch back.
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.
41
43
 
42
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.
43
45
 
@@ -89,6 +91,11 @@ If a shortcut still does nothing on your machine, check:
89
91
 
90
92
  ## Changelog
91
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
+
92
99
  ### 0.2.0
93
100
 
94
101
  - All three tools collapsed by default (edit/write used to expand)
package/README.zh-CN.md CHANGED
@@ -18,6 +18,8 @@
18
18
 
19
19
  折叠摘要每槽单行:命令 1 行加输出 1 行(计数后缀内联在输出行尾)。
20
20
 
21
+ 在 Pi 的 fullscreen 模式下,左键点击任意一个工具块,可单独在紧凑摘要和完整渲染之间切换。再次点击即收起。三个工具都只有这两种状态;`bash` 和 `write` 不经过官方中间预览。
22
+
21
23
  ## 模式
22
24
 
23
25
  | 模式 | 行为 |
@@ -25,7 +27,7 @@
25
27
  | compact(默认) | 三工具全折叠 |
26
28
  | markdown(`/tidy-markdown` 切换) | Markdown 文件(`.md`、`.mdx`、`.markdown`)的 `edit`/`write` 展开全文,其余照样折叠 |
27
29
 
28
- 切换模式会清空各工具的手动钉选并重渲染已有行。模式不持久化,重启后回到 compact。
30
+ 切换模式会清空快捷键状态和各工具块的点击状态,并重渲染已有行。模式不持久化,重启后回到 compact。
29
31
 
30
32
  ## 快捷键
31
33
 
@@ -37,7 +39,7 @@ Mac 上按 `Command+Option+字母`(实测有效,对应插件注册的 `Ctrl+
37
39
  | `Command+Option+E` | `edit` | 官方渲染 ↔ 折叠摘要 |
38
40
  | `Command+Option+W` | `write` | 官方渲染 ↔ 折叠摘要 |
39
41
 
40
- 每次切换会弹出通知提示当前状态;再按一次切回。
42
+ 快捷键按工具类型切换所有已有工具块,并清除该工具之前的逐块点击状态。每次切换会弹出通知;再按一次切回。
41
43
 
42
44
  `bash` 和 `write` 展开时**一次按键直接显示完整输出**——跳过 Pi 内置预览(bash 的 5 行尾部预览、write 的 10 行预览)。`edit` 展开时使用 Pi **官方渲染**(它没有预览态)。全局 `Ctrl+O` 对这三个工具均无效。
43
45
 
@@ -89,6 +91,11 @@ Pi 会在终端支持时协商 [Kitty 键盘协议](https://sw.kovidgoyal.net/ki
89
91
 
90
92
  ## 更新日志
91
93
 
94
+ ### 0.2.1
95
+
96
+ - 支持在 Pi fullscreen 模式下点击单个 bash/edit/write 工具块,在紧凑摘要和完整渲染之间切换
97
+ - 保留工具级快捷键;快捷键会统一设置同类工具块
98
+
92
99
  ### 0.2.0 - 2026-09-05
93
100
 
94
101
  - 默认三工具全折叠(edit/write 原默认展开)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runminton/pi-tidy-tools",
3
- "version": "0.2.0",
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",
package/tidy-tools.ts CHANGED
@@ -4,8 +4,9 @@
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: Ctrl+Alt+E (edit), Ctrl+Alt+W (write), Ctrl+Alt+B (bash)
8
- * toggle each tool between collapsed and fully expanded rendering.
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.
10
11
  * - Modes: compact (default, all three collapsed) and markdown
11
12
  * (`/tidy-markdown` toggles; edit/write on Markdown files expand,
@@ -26,6 +27,7 @@ import type { EditToolDetails } from "@earendil-works/pi-coding-agent";
26
27
  import {
27
28
  Box,
28
29
  Container,
30
+ MouseRegion,
29
31
  Text,
30
32
  type Component,
31
33
  sliceByColumn,
@@ -216,6 +218,9 @@ function getCollapsedBackground(isPartial: boolean, isError: boolean): string {
216
218
  let markdownMode = false;
217
219
  // 手动钉选:Ctrl+Alt+? 或 /tidy-<tool> 设置,优先于模式默认;切换模式时清空。
218
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>();
219
224
 
220
225
  function isMarkdownPath(value: unknown): boolean {
221
226
  if (typeof value !== "string") return false;
@@ -239,8 +244,34 @@ function modeDefaultExpanded(name: string, args: any): boolean {
239
244
  return false;
240
245
  }
241
246
 
242
- function isToolExpanded(name: string, args: any): boolean {
243
- return manualExpanded.get(name) ?? modeDefaultExpanded(name, args);
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
+ });
244
275
  }
245
276
 
246
277
  // Some terminals (Kitty keyboard protocol flag-1 mode) encode Ctrl+letter as
@@ -286,6 +317,7 @@ function refreshToolRows(ctx: ExtensionContext): void {
286
317
 
287
318
  function toggleToolExpanded(ctx: ExtensionContext, name: string): void {
288
319
  manualExpanded.set(name, !(manualExpanded.get(name) ?? modeBaseExpanded(name)));
320
+ expansionRevisions.set(name, (expansionRevisions.get(name) ?? 0) + 1);
289
321
  refreshToolRows(ctx);
290
322
  ctx.ui.notify(`${name}: ${manualExpanded.get(name) ? "expanded" : "collapsed"}`, "info");
291
323
  }
@@ -350,8 +382,8 @@ function registerCollapsibleTool(
350
382
 
351
383
  renderCall(args, theme, context) {
352
384
  const def = getDef(context.cwd);
353
- if (isToolExpanded(def.name, args) && def.renderCall) {
354
- return def.renderCall(
385
+ if (isToolExpanded(def.name, args, context) && def.renderCall) {
386
+ const component = def.renderCall(
355
387
  args as never,
356
388
  theme as never,
357
389
  // One-key full expansion: skip the official preview so
@@ -362,9 +394,10 @@ function registerCollapsibleTool(
362
394
  lastComponent: undefined,
363
395
  } as never,
364
396
  );
397
+ return makeToolClickable(component, def.name, args, context);
365
398
  }
366
399
  const component = collapsedCall(args, theme);
367
- if (!usesSelfShell) return component;
400
+ if (!usesSelfShell) return makeToolClickable(component, def.name, args, context);
368
401
 
369
402
  const shell = getCollapsedToolShell(
370
403
  context.state,
@@ -372,21 +405,22 @@ function registerCollapsibleTool(
372
405
  getCollapsedBackground(context.isPartial, context.isError),
373
406
  );
374
407
  shell.setCall(component);
375
- return shell;
408
+ return makeToolClickable(shell, def.name, args, context);
376
409
  },
377
410
 
378
411
  renderResult(result, options, theme, context) {
379
412
  const def = getDef(context.cwd);
380
- if (isToolExpanded(def.name, context.args) && def.renderResult) {
381
- return def.renderResult(
413
+ if (isToolExpanded(def.name, context.args, context) && def.renderResult) {
414
+ const component = def.renderResult(
382
415
  result as never,
383
416
  { ...options, ...(forceFullExpansion ? { expanded: true } : {}) } as never,
384
417
  theme as never,
385
418
  { ...context, lastComponent: undefined } as never,
386
419
  );
420
+ return makeToolClickable(component, def.name, context.args, context);
387
421
  }
388
422
  const component = collapsedResult(result, options, theme, context);
389
- if (!usesSelfShell) return component;
423
+ if (!usesSelfShell) return makeToolClickable(component, def.name, context.args, context);
390
424
 
391
425
  const shell = getCollapsedToolShell(
392
426
  context.state,
@@ -436,16 +470,17 @@ export default function (pi: ExtensionAPI) {
436
470
  },
437
471
 
438
472
  renderCall(args, theme, context) {
439
- if (isToolExpanded("bash", args)) {
473
+ if (isToolExpanded("bash", args, context)) {
440
474
  const def = getBashDef(context.cwd);
441
475
  if (def.renderCall) {
442
- return def.renderCall(
476
+ const component = def.renderCall(
443
477
  args as never,
444
478
  theme as never,
445
479
  // One-key full expansion for bash: skip the official
446
480
  // preview so Ctrl+Alt+B shows the complete output.
447
481
  { ...context, expanded: true, lastComponent: undefined } as never,
448
482
  );
483
+ return makeToolClickable(component, "bash", args, context);
449
484
  }
450
485
  }
451
486
 
@@ -455,18 +490,18 @@ export default function (pi: ExtensionAPI) {
455
490
  const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
456
491
  const renderedCommand = theme.fg("toolTitle", theme.bold(`$ ${command}`)) + timeoutSuffix;
457
492
 
458
- return new LimitedLinesText(
493
+ return makeToolClickable(new LimitedLinesText(
459
494
  renderedCommand,
460
495
  MAX_COLLAPSED_COMMAND_LINES,
461
496
  theme.fg("muted", "..."),
462
- );
497
+ ), "bash", args, context);
463
498
  },
464
499
 
465
500
  renderResult(result, options, theme, context) {
466
- if (isToolExpanded("bash", context.args)) {
501
+ if (isToolExpanded("bash", context.args, context)) {
467
502
  const def = getBashDef(context.cwd);
468
503
  if (def.renderResult) {
469
- return def.renderResult(
504
+ const component = def.renderResult(
470
505
  result as never,
471
506
  // One-key full expansion for bash: skip the official
472
507
  // preview so Ctrl+Alt+B shows the complete output.
@@ -474,9 +509,11 @@ export default function (pi: ExtensionAPI) {
474
509
  theme as never,
475
510
  { ...context, lastComponent: undefined } as never,
476
511
  );
512
+ return makeToolClickable(component, "bash", context.args, context);
477
513
  }
478
514
  }
479
515
 
516
+ const clickable = (component: Component) => makeToolClickable(component, "bash", context.args, context);
480
517
  const output = expandTabs(errorText(result).trim().replace(/\r/g, ""));
481
518
  const allLines = output.split("\n");
482
519
  const first = allLines[0] ?? "";
@@ -487,32 +524,32 @@ export default function (pi: ExtensionAPI) {
487
524
  // 失败输出同样压成 1 行:红色首行 + 内联计数,与成功态同形。
488
525
  if (context.isError) {
489
526
  if (!first) {
490
- return new CollapsedBashOutput(
527
+ return clickable(new CollapsedBashOutput(
491
528
  theme.fg("error", "failed"),
492
529
  "",
493
530
  theme.fg("muted", "..."),
494
- );
531
+ ));
495
532
  }
496
- return new CollapsedBashOutput(
533
+ return clickable(new CollapsedBashOutput(
497
534
  theme.fg("error", first),
498
535
  moreSuffix,
499
536
  theme.fg("muted", "..."),
500
- );
537
+ ));
501
538
  }
502
539
  // Preview only the first line. Wrapping the full output on every
503
540
  // frame is what made the TUI lag with large outputs.
504
541
  if (!first) {
505
- return new CollapsedBashOutput(
542
+ return clickable(new CollapsedBashOutput(
506
543
  theme.fg("muted", "(no output)"),
507
544
  "",
508
545
  theme.fg("muted", "..."),
509
- );
546
+ ));
510
547
  }
511
- return new CollapsedBashOutput(
548
+ return clickable(new CollapsedBashOutput(
512
549
  theme.fg("toolOutput", first),
513
550
  moreSuffix,
514
551
  theme.fg("muted", "..."),
515
- );
552
+ ));
516
553
  },
517
554
  });
518
555
 
@@ -589,6 +626,9 @@ export default function (pi: ExtensionAPI) {
589
626
  handler: async (_args, ctx) => {
590
627
  markdownMode = !markdownMode;
591
628
  manualExpanded.clear();
629
+ for (const name of ["bash", "edit", "write"]) {
630
+ expansionRevisions.set(name, (expansionRevisions.get(name) ?? 0) + 1);
631
+ }
592
632
  refreshToolRows(ctx);
593
633
  ctx.ui.notify(`markdown mode: ${markdownMode ? "on" : "off"}`, "info");
594
634
  },