pi-minimalist 0.1.1 → 0.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-minimalist",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Compact one-line tool and thinking rows for Pi's interactive transcript",
5
5
  "keywords": ["pi-package"],
6
6
  "license": "MIT",
package/src/components.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { truncateToWidth } from "@earendil-works/pi-tui";
10
- import type { Component } from "@earendil-works/pi-tui";
10
+ import type { Component, TuiMouseEvent, TuiMouseEventResult } from "@earendil-works/pi-tui";
11
11
  import { gutterWidth, type Row } from "./row.ts";
12
12
 
13
13
  /**
@@ -50,6 +50,7 @@ export type ResolveRow = (width: number) => Row | null;
50
50
  */
51
51
  export class CompactLine implements Component {
52
52
  private resolve: ResolveRow = () => null;
53
+ private click?: () => boolean;
53
54
  private ticker: unknown;
54
55
  private timers: Timers;
55
56
 
@@ -57,9 +58,20 @@ export class CompactLine implements Component {
57
58
  this.timers = timers;
58
59
  }
59
60
 
60
- /** Install the row resolver. Called on every updateDisplay with fresh state. */
61
- setRow(resolve: ResolveRow): void {
61
+ /**
62
+ * Install the row resolver. Called on every updateDisplay with fresh state.
63
+ * `click` returning true stops Pi's own left-click (expand) handler.
64
+ */
65
+ setRow(resolve: ResolveRow, click?: () => boolean): void {
62
66
  this.resolve = resolve;
67
+ this.click = click;
68
+ }
69
+
70
+ // Pi's MouseRegion asks its child first, so a handled click never reaches
71
+ // the region's own expand/collapse toggle.
72
+ handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined {
73
+ if (event.type !== "click" || event.button !== "left") return undefined;
74
+ return this.click?.() ? { handled: true } : undefined;
63
75
  }
64
76
 
65
77
  /**
@@ -55,6 +55,8 @@ export class RunGrouping {
55
55
  private cycle = 0;
56
56
  private cycles = new Map<number, Cycle>();
57
57
  private agentActive = false;
58
+ /** Rows the user opened out of a summary by clicking it. Never fold again. */
59
+ private opened = new Set<string>();
58
60
  private version = 0;
59
61
  private cache?: ViewCache;
60
62
 
@@ -226,6 +228,33 @@ export class RunGrouping {
226
228
  return painter.summary(view);
227
229
  }
228
230
 
231
+ /**
232
+ * Open the summary this row draws back into separate rows, after a click.
233
+ *
234
+ * Needed because the summary is drawn by ONE member (the tail) and the others
235
+ * draw zero lines: Pi's own click handler would expand only the tail.
236
+ * Returns false when the row draws no summary, so Pi's click still applies.
237
+ */
238
+ open(id: string): boolean {
239
+ const entry = this.byId.get(id);
240
+ if (!entry) return false;
241
+ const { activity, grouped } = this.views();
242
+ const members = [entry];
243
+ if (activity.get(id) === "summary") {
244
+ members.push(...this.entries.filter((e) => e.cycle === entry.cycle && activity.get(e.id) === "hide"));
245
+ } else if (typeof grouped.get(id) === "object") {
246
+ // A run's hidden members are the contiguous "hide" rows before its tail.
247
+ for (let i = this.entries.indexOf(entry) - 1; i >= 0 && grouped.get(this.entries[i].id) === "hide"; i--) {
248
+ members.push(this.entries[i]);
249
+ }
250
+ } else {
251
+ return false;
252
+ }
253
+ for (const member of members) this.opened.add(member.id);
254
+ this.invalidateViews();
255
+ return true;
256
+ }
257
+
229
258
  view(id: string): RowView {
230
259
  return this.views().grouped.get(id) ?? "show";
231
260
  }
@@ -320,7 +349,7 @@ export class RunGrouping {
320
349
  }
321
350
 
322
351
  private activityFoldable(entry: Entry): boolean {
323
- if (entry.expanded) return false;
352
+ if (entry.expanded || this.opened.has(entry.id)) return false;
324
353
  if (entry.outcome === "pending" && entry.kind !== "prose" && this.config.get("keepActiveToolsExpanded")) {
325
354
  return false;
326
355
  }
@@ -328,7 +357,7 @@ export class RunGrouping {
328
357
  }
329
358
 
330
359
  private foldable(entry: Entry | undefined): boolean {
331
- if (!entry?.foldable || entry.expanded) return false;
360
+ if (!entry?.foldable || entry.expanded || this.opened.has(entry.id)) return false;
332
361
  if (entry.kind === "thinking" && !this.config.get("thinkingAsToolCall")) return false;
333
362
  if (entry.outcome === "pending" && this.config.get("keepActiveToolsExpanded")) return false;
334
363
  return true;
package/src/thinking.ts CHANGED
@@ -59,7 +59,7 @@ export function createThinkingPreview(deps: ThinkingDeps): ThinkingPreview {
59
59
 
60
60
  if (owner && runIndex !== undefined) {
61
61
  const id = grouping.thinkingId(owner, runIndex);
62
- line.setRow(() => grouping.rowFor(id, new Painter(theme, config), paint));
62
+ line.setRow(() => grouping.rowFor(id, new Painter(theme, config), paint), () => grouping.open(id));
63
63
  } else {
64
64
  line.setRow(paint);
65
65
  }
@@ -194,7 +194,10 @@ export function createToolRenderer(deps: RendererDeps): ToolRenderer {
194
194
  return component;
195
195
  }
196
196
  grouping.observe(id, summaryName(name, args), status.outcome, context.expanded === true);
197
- component.setRow((width) => grouping.rowFor(id, new Painter(theme, config), () => paint(width)));
197
+ component.setRow(
198
+ (width) => grouping.rowFor(id, new Painter(theme, config), () => paint(width)),
199
+ () => grouping.open(id),
200
+ );
198
201
  return component;
199
202
  },
200
203