pi-zentui 0.22.2 → 0.22.3

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
@@ -146,7 +146,7 @@ Useful shortcuts:
146
146
 
147
147
  **Thinking (Experimental)** uses one private `AssistantMessageComponent` renderer for Rail, Tree, and Streaming, tested against exact Pi versions 0.80.5, 0.82.1, 0.83.0, 0.84.0, and 0.84.4. It is disabled by default and may break after Pi updates. Zentui installs an enabled startup mode before transcript restoration. A healthy installed controller lets active Streaming switch live to Rail or Tree, and lets Rail and Tree switch live between each other, without reinstalling its patch. Entering Streaming from Rail or Tree saves the choice but keeps the active structural mode until restart. Disabling live restores native thinking and releases Streaming resources. First enable and re-enable after a live disable are also restart-gated. Mode changes while disabled only preconfigure the next enable. Startup failures, missing constructors, incompatible private child layouts, parser limits, theme/render/width errors, and displaced patch ownership fail open to complete native thinking. If cleanup throws while leaving Streaming, disabling still restores native thinking and a structural selection still becomes active; the successful change warns that Streaming is unavailable for the rest of the session.
148
148
 
149
- Rail shows every parsed label in each native contiguous thinking run (`│ Label`, with only the open final phase shown as `│ • Label`). Tree independently shows the latest five labels in each run (`├─ · Label`, settled `└─ · Label`, open `└─ • Label`); it never aggregates across intervening text or tool blocks. Rail and Tree follow Pi's thinking visibility, and unsafe or unstructured content remains native. Labels are rendered by fresh host-shaped Pi Markdown instances before cropping, so emphasis, code, links, HTML, LaTeX, custom transforms, and native `thinkingText` styling remain host-controlled. Every label occupies one terminal row: ANSI/OSC/grapheme-aware cropping adds `…` only when needed. Native horizontal padding stays external. Connectors are styled directly with the current theme's `accent` callback on every render, so custom themes control them independently. Hidden native thinking remains hidden and keeps Pi's native hidden label.
149
+ Rail shows every parsed label in each native contiguous thinking run (`│ Label`, with only the open final phase shown as `│ • Label`). Tree independently shows the latest five labels in each run (`├─ · Label`, settled `└─ · Label`, open `└─ • Label`); it never aggregates across intervening text or tool blocks. Rail and Tree follow Pi's thinking visibility. Complete strict SGR styling is stripped before parsing; every other terminal control and unsafe or unstructured content keeps the complete run native. Labels are rendered by fresh host-shaped Pi Markdown instances before cropping, so emphasis, code, links, HTML, LaTeX, custom transforms, and native `thinkingText` styling remain host-controlled. Every label occupies one terminal row: ANSI/OSC/grapheme-aware cropping adds `…` only when needed. Native horizontal padding stays external. Connectors are styled directly with the current theme's `accent` callback on every render, so custom themes control them independently. Hidden native thinking remains hidden and keeps Pi's native hidden label.
150
150
 
151
151
  Streaming retains Pi's host-rendered final five rows under `Thinking 7.1s`, folds completed reasoning under `Thought` or current-session `Thought for 12.3s`, and owns the configured thinking-toggle binding (Ctrl+T by default) only when started in Streaming. Its input listener and timer are acquired only for an enabled Streaming session start; startup acquisition failure uses native thinking and marks Streaming unavailable. Restored completions cannot recover a duration because Pi does not persist the thinking-end timestamp. Expand/refold and lifecycle tracking are bounded to 256 retained assistant components; evicted entries are first restored natively. All modes restore/dispose on shutdown. Thinking (Experimental) never writes the Working line and does not change its existing **Thinking time** option, working text, Footer, Editor, statuses, or model behavior.
152
152
 
@@ -1,4 +1,4 @@
1
- import { sanitizeUserMessageSourceText } from "./user-message-osc";
1
+ import { sanitizeSgrOnlySourceText } from "./user-message-osc";
2
2
 
3
3
  /** Hard limits keep private transcript parsing synchronous and bounded. Limits are inclusive. */
4
4
  export const THINKING_STEPS_MAX_INPUT_LENGTH = 65_536;
@@ -109,9 +109,8 @@ function finishSteps(steps: MutableStep[]): ThinkingStep[] | undefined {
109
109
  /** Parse source-level structure; fenced and display-math blocks remain opaque bodies. */
110
110
  export function parseThinkingSteps(markdown: string): readonly ThinkingStep[] | undefined {
111
111
  if (!markdown || markdown.length > THINKING_STEPS_MAX_INPUT_LENGTH) return undefined;
112
- const source = markdown.replace(/\r\n/g, "\n");
113
- const sanitized = sanitizeUserMessageSourceText(source);
114
- if (sanitized !== source || !source.trim()) return undefined;
112
+ const source = sanitizeSgrOnlySourceText(markdown.replace(/\r\n/g, "\n"));
113
+ if (source === undefined || !source.trim()) return undefined;
115
114
 
116
115
  const lines = source.split("\n");
117
116
  const steps: MutableStep[] = [];
@@ -221,15 +221,29 @@ function filterBalancedUserMessageTerminalText(text: string, options: FilterOpti
221
221
  return filterUserMessageTerminalText(text, { ...options, preserveValidOsc8: false }).text;
222
222
  }
223
223
 
224
+ const sourceFilterOptions = {
225
+ preserveValidOsc8: false,
226
+ preserveSourceWhitespace: true,
227
+ } as const;
228
+
224
229
  /** Strip every terminal control from untrusted source text before Markdown rendering. */
225
230
  export function sanitizeUserMessageSourceText(text: string): string {
226
231
  return filterBalancedUserMessageTerminalText(text, {
227
- preserveValidOsc8: false,
228
- preserveSourceWhitespace: true,
232
+ ...sourceFilterOptions,
229
233
  preserveSgr: false,
230
234
  });
231
235
  }
232
236
 
237
+ /** Strip strict 7-bit CSI SGR, rejecting source that contains any other terminal control. */
238
+ export function sanitizeSgrOnlySourceText(text: string): string | undefined {
239
+ const sgrPreserved = filterBalancedUserMessageTerminalText(text, {
240
+ ...sourceFilterOptions,
241
+ preserveSgr: true,
242
+ });
243
+ if (sgrPreserved !== text) return undefined;
244
+ return sanitizeUserMessageSourceText(text);
245
+ }
246
+
233
247
  /**
234
248
  * Sanitize a predecessor-rendered row. Pi formatting needs only SGR and
235
249
  * structurally valid 7-bit OSC 8; every other terminal control is removed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-zentui",
3
- "version": "0.22.2",
3
+ "version": "0.22.3",
4
4
  "description": "A Starship-inspired statusline and Opencode-style TUI for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",