@pi-kaush/pi-tool-call-markers 0.2.10 → 0.2.11

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/CHANGELOG.md CHANGED
@@ -13,10 +13,11 @@
13
13
  - Adopt Pi's native content-color split in collapsed rows: tool names ride
14
14
  `toolTitle` and call content/outcomes ride `toolOutput` (structural
15
15
  chrome stays muted; failed rows stay uniformly error).
16
- - Tint the collapsed "+ Thought" label with the session's active
17
- thinking-level token (thinkingOffthinkingMax), re-tinting on
16
+ - Quiet the settled "+ Thought" label to the muted tone collapsed tool
17
+ calls use; only the live "⠋ Thinking" spinner keeps the session's active
18
+ thinking-level tint (thinkingOff…thinkingMax), re-tinting on
18
19
  thinking_level_select; the expanded thinking block is untouched. The
19
- PI_TOOL_CALL_MARKERS_THOUGHT_COLOR values are now `level` (default),
20
+ PI_TOOL_CALL_MARKERS_THOUGHT_COLOR values are `level` (default),
20
21
  `mdheading`, and `inherit`; the midpoint `gray` variant and its RGB
21
22
  color math are gone.
22
23
  - Paint user-run `!` bash block surfaces with the theme's
package/README.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  Give Pi's collapsed tool calls a quiet, width-safe transcript shell while preserving native details under `Ctrl+O`. The package also includes a display-only thinking-block adapter.
4
4
 
5
+ ### Install
6
+
7
+ ```fish
8
+ pi install npm:@pi-kaush/pi-tool-call-markers
9
+ ```
10
+
11
+ Restart Pi or run `/reload`.
12
+
5
13
  ## What it changes
6
14
 
7
15
  Collapsed tool rows use semantic theme colors with no gear, background fill, box padding, or filled blank rows:
@@ -73,15 +81,9 @@ When Pi exposes its per-row hidden-thinking and streaming fields, hidden reasoni
73
81
 
74
82
  The live label samples Pi's native braille spinner sequence from the content updates Pi already renders; it does not add a timer. The adapter stores the first local streaming timestamp per assistant row in a `WeakMap`. A restored message or an older runtime with no streaming argument uses `+ Thought`. Visible-thinking mode remains native. There is no interval, timeout, render request, model call, or network work.
75
83
 
76
- ## Install
84
+ ## Local development
77
85
 
78
- ```bash
79
- pi install npm:@pi-kaush/pi-tool-call-markers
80
- ```
81
-
82
- For local development:
83
-
84
- ```bash
86
+ ```fish
85
87
  pi \
86
88
  -e ./extensions/pi-tool-call-markers/src/index.ts \
87
89
  -e ./extensions/pi-tool-call-markers/src/thinking-block-merger.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-kaush/pi-tool-call-markers",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Compact and group Pi tool calls, with a bundled extension for adjacent thinking blocks.",
5
5
  "license": "MIT",
6
6
  "author": "Kaushik Gopal",
package/src/index.ts CHANGED
@@ -1660,6 +1660,14 @@ function uninstallPresentationPatch(
1660
1660
  delete proto[PRESENTATION_PATCHED];
1661
1661
  }
1662
1662
 
1663
+ // TODO(compaction cards): native/failed compaction cards in the transcript
1664
+ // are currently rendered by the pi-verbatim-compaction extension. Its
1665
+ // src/chat-log.ts is deliberately self-contained (public Pi imports only), so
1666
+ // porting here is: drop in that file, registerEntryRenderer(COMPACTION_LOG_TYPE),
1667
+ // and wire pi.on("session_compact") / pi.on("session_compact_failed") filtered
1668
+ // to fromExtension === false. Do this only if pi-verbatim-compaction is
1669
+ // removed or native cards should exist without it; while both are installed,
1670
+ // rendering here too would double-render every native compaction.
1663
1671
  export default function (pi: ExtensionAPI) {
1664
1672
  const patch = installPresentationPatch();
1665
1673
  const grouping = patch ? installGroupingPatch(patch) : undefined;
@@ -6,13 +6,16 @@ const THINKING_GROUPING_PATCHED = Symbol.for("kg.pi.thinkingGrouping.v2");
6
6
  const PI_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
7
7
  const SPINNER_INTERVAL_MS = 80;
8
8
 
9
- // Visual experiment for the settled "+ Thought" label. "inherit" keeps Pi's
10
- // native styling (italic + thinkingText); "mdheading" rides the theme's
11
- // mdHeading token; the default tints the label with the session's active
12
- // thinking-level color (thinkingOff…thinkingMax), so the collapsed label
13
- // quietly advertises the level Pi is reasoning at. Only the collapsed label
14
- // is restyled the expanded thinking block keeps Pi's thinkingText. The env
15
- // var overrides the default so variants can be compared without republishing.
9
+ // Visual treatment for the collapsed thinking label. The live "Thinking…"
10
+ // spinner tints with the session's active thinking-level color
11
+ // (thinkingOff…thinkingMax), so progress reads as activity; the settled
12
+ // "+ Thought" row drops to the muted token collapsed tool calls use, so
13
+ // finished reasoning stops advertising the level. "inherit" keeps Pi's
14
+ // native styling (italic + thinkingText) for both states, and "mdheading"
15
+ // rides the theme's mdHeading token for both. The env var overrides the
16
+ // default so variants can be compared without republishing. Only the
17
+ // collapsed label is restyled — the expanded thinking block keeps Pi's
18
+ // thinkingText.
16
19
  const THOUGHT_LABEL_COLOR_ENV = "PI_TOOL_CALL_MARKERS_THOUGHT_COLOR";
17
20
  type ThoughtLabelColor = "inherit" | "level" | "mdheading";
18
21
  const DEFAULT_THOUGHT_LABEL_COLOR: ThoughtLabelColor = "level";
@@ -44,35 +47,48 @@ type ThemeDetail = {
44
47
  let activeTheme: ThemeDetail | undefined;
45
48
  let activeLevel: (() => string) | undefined;
46
49
 
47
- let thoughtLabelPrefix = "";
48
- let thoughtLabelSuffix = "";
50
+ type LabelStyle = { prefix: string; suffix: string };
51
+ let liveLabelStyle: LabelStyle | undefined;
52
+ let settledLabelStyle: LabelStyle | undefined;
53
+
54
+ function styledWith(token: string): LabelStyle | undefined {
55
+ const ansi = activeTheme?.getFgAnsi?.(token);
56
+ if (!ansi) return undefined;
57
+ // The styled label replaces Pi's italicized Text node wholesale (see
58
+ // restyleHiddenThinkingLabel). The leading italic-off still matters: the
59
+ // TUI's diff renderer can skip bytes shared with the previously drawn
60
+ // italic line, leaving the terminal in italic state otherwise.
61
+ return { prefix: `\x1b[23m${ansi}`, suffix: "\x1b[39m" };
62
+ }
49
63
 
50
64
  function updateThoughtLabelStyle(): void {
51
- thoughtLabelPrefix = "";
52
- thoughtLabelSuffix = "";
65
+ liveLabelStyle = undefined;
66
+ settledLabelStyle = undefined;
53
67
  const choice = thoughtLabelColorChoice();
54
68
  if (choice === "inherit" || !activeTheme) return;
55
69
 
56
- let ansi: string | undefined;
57
70
  const token =
58
71
  choice === "mdheading"
59
72
  ? "mdHeading"
60
73
  : (LEVEL_TOKEN[activeLevel?.() ?? "off"] ?? "thinkingOff");
61
- ansi = activeTheme.getFgAnsi?.(token);
62
- if (!ansi) return;
74
+ liveLabelStyle = styledWith(token);
75
+ settledLabelStyle =
76
+ choice === "mdheading" ? liveLabelStyle : styledWith("muted");
77
+ }
63
78
 
64
- // The styled label replaces Pi's italicized Text node wholesale (see
65
- // restyleHiddenThinkingLabel). The leading italic-off still matters: the
66
- // TUI's diff renderer can skip bytes shared with the previously drawn
67
- // italic line, leaving the terminal in italic state otherwise.
68
- thoughtLabelPrefix = `\x1b[23m${ansi}`;
69
- thoughtLabelSuffix = "\x1b[39m";
79
+ // The settled label is the finalized "+ Thought" row; every other label is
80
+ // the live spinner.
81
+ function isSettledThoughtLabel(label: string): boolean {
82
+ return label.startsWith("+ Thought");
70
83
  }
71
84
 
72
85
  export function visibleThoughtLabel(label: string): string {
73
- if (!thoughtLabelPrefix) return label;
86
+ const style = isSettledThoughtLabel(label)
87
+ ? settledLabelStyle
88
+ : liveLabelStyle;
89
+ if (!style) return label;
74
90
  const raw = label.replace(/\x1b\[[0-9;]*m/g, "");
75
- return `${thoughtLabelPrefix}${raw}${thoughtLabelSuffix}`;
91
+ return `${style.prefix}${raw}${style.suffix}`;
76
92
  }
77
93
 
78
94
  type AssistantMessageLike = {
@@ -98,7 +114,6 @@ type TextLikeChild = {
98
114
  // terminal. Swap the node's text for a self-contained styled version after
99
115
  // each native render pass instead.
100
116
  function restyleHiddenThinkingLabel(row: AssistantMessageRow): void {
101
- if (!thoughtLabelPrefix) return;
102
117
  if (
103
118
  row.hideThinkingBlock !== true ||
104
119
  typeof row.hiddenThinkingLabel !== "string"
@@ -108,6 +123,7 @@ function restyleHiddenThinkingLabel(row: AssistantMessageRow): void {
108
123
  const label = row.hiddenThinkingLabel;
109
124
  const children = row.contentContainer?.children;
110
125
  if (!Array.isArray(children)) return;
126
+ if (!liveLabelStyle && !settledLabelStyle) return;
111
127
  const styled = visibleThoughtLabel(label);
112
128
  for (const child of children) {
113
129
  const textChild = child as TextLikeChild | undefined;
@@ -419,7 +435,7 @@ export default function (pi: ExtensionAPI) {
419
435
  if (patch && !uninstallThinkingGroupingPatch(patch)) return;
420
436
  activeTheme = undefined;
421
437
  activeLevel = undefined;
422
- thoughtLabelPrefix = "";
423
- thoughtLabelSuffix = "";
438
+ liveLabelStyle = undefined;
439
+ settledLabelStyle = undefined;
424
440
  });
425
441
  }