pi-tui-footer 1.2.1 → 1.2.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/README.md CHANGED
@@ -17,7 +17,7 @@ pi install git:github.com/onenora/pi-tui-footer
17
17
  固定版本:
18
18
 
19
19
  ```bash
20
- pi install git:github.com/onenora/pi-tui-footer@v1.2.0
20
+ pi install git:github.com/onenora/pi-tui-footer@v1.2.2
21
21
  ```
22
22
 
23
23
  更新:
@@ -4,7 +4,7 @@ import {
4
4
  type ExtensionContext,
5
5
  type KeybindingsManager,
6
6
  } from "@earendil-works/pi-coding-agent";
7
- import type { EditorTheme, TUI } from "@earendil-works/pi-tui";
7
+ import type { EditorTheme, TuiMouseEvent, TuiMouseEventResult, TUI } from "@earendil-works/pi-tui";
8
8
  import { CURSOR_MARKER, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
9
9
  import type { CursorStyle } from "./config.ts";
10
10
  import {
@@ -24,6 +24,8 @@ const CURSOR_STYLE_SEQUENCES: Partial<Record<CursorStyle, string>> = {
24
24
  underline: "\x1b[4 q",
25
25
  };
26
26
  const DEFAULT_CURSOR_STYLE_SEQUENCE = "\x1b[0 q";
27
+ const EDITOR_FRAME_LEFT_INSET = 2;
28
+ const EDITOR_FRAME_HORIZONTAL_CHROME = EDITOR_FRAME_LEFT_INSET * 2;
27
29
 
28
30
  interface WorkingStatusIndicator {
29
31
  renderInBorder(width: number): string;
@@ -221,6 +223,16 @@ export class OpenTuiEditor extends CustomEditor {
221
223
  super.setPaddingX(0);
222
224
  }
223
225
 
226
+ override handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined {
227
+ if (event.width < EDITOR_FRAME_HORIZONTAL_CHROME) return super.handleMouse(event);
228
+
229
+ return super.handleMouse({
230
+ ...event,
231
+ x: event.x - EDITOR_FRAME_LEFT_INSET,
232
+ width: event.width - EDITOR_FRAME_HORIZONTAL_CHROME,
233
+ });
234
+ }
235
+
224
236
  setWorkingStatusIndicator(indicator: WorkingStatusIndicator | undefined): void {
225
237
  this.embeddedWorkingStatusIndicator = indicator;
226
238
  this.tui.requestRender();
@@ -262,12 +274,12 @@ export class OpenTuiEditor extends CustomEditor {
262
274
  }
263
275
 
264
276
  render(width: number): string[] {
265
- if (width < 4) return this.renderBase(width);
277
+ if (width < EDITOR_FRAME_HORIZONTAL_CHROME) return this.renderBase(width);
266
278
 
267
279
  const rail = this.getRail();
268
280
  const borderPaint = this.getBorder;
269
281
  // ponytail: 1-char rail + 1-char gap on each side = 4 chars of chrome.
270
- const innerWidth = Math.max(0, width - 4);
282
+ const innerWidth = Math.max(0, width - EDITOR_FRAME_HORIZONTAL_CHROME);
271
283
  const baseLines = this.renderBase(innerWidth);
272
284
  const bottomIdx = findBottomBorderIndex(baseLines);
273
285
 
@@ -4,7 +4,7 @@
4
4
  * thinking peek preview, rounded tool frames, and turn telemetry.
5
5
  *
6
6
  * Integrated from:
7
- * - pi-open-tui (v0.3.8, MIT, by OldSun, https://github.com/OldSuns/pi-open-tui)
7
+ * - pi-open-tui (v0.3.9, MIT, by OldSun, https://github.com/OldSuns/pi-open-tui)
8
8
  * - pi-rounded-tools (v0.1.3, MIT, by OrionPax, https://github.com/orionpax1997/pi-rounded-tools)
9
9
  *
10
10
  * Security audit notes:
@@ -4,7 +4,7 @@
4
4
  * Merged 2026-08 from the standalone local extension rounded-tools.ts (a
5
5
  * localized copy of npm:pi-rounded-tools@0.1.3, MIT, by OrionPax,
6
6
  * https://github.com/orionpax1997/pi-rounded-tools), integrated into pi-tui
7
- * (based on https://github.com/OldSuns/pi-open-tui v0.3.8, MIT, by
7
+ * (based on https://github.com/OldSuns/pi-open-tui v0.3.9, MIT, by
8
8
  * OldSun) and gated behind `roundedTools` in pi-tui config
9
9
  * (~/.pi/agent/pi-tui.json, default on).
10
10
  *
@@ -43,23 +43,28 @@ export function truncateBranch(branch: string, maxLen: number): string {
43
43
  }
44
44
 
45
45
  export function truncatePath(path: string, maxLen: number): string {
46
- if (path.length <= maxLen) return path;
46
+ // `maxLen` is a terminal-column budget, so every comparison here must use
47
+ // display width. Comparing String.length let wide (CJK) paths stay wider than
48
+ // the budget: for a basename of 4 characters but 8 columns, `truncatePath`
49
+ // returned it unchanged, so fitSegmentsByPriority's `while (totalW() > maxW)`
50
+ // loop never made progress and livelocked the renderer at 100% CPU.
51
+ if (visibleWidth(path) <= maxLen) return path;
47
52
  if (maxLen <= 3) return "...".slice(0, maxLen);
48
53
  const sepChar = path.includes("/") ? "/" : "\\";
49
54
  const parts = path.split(/[\\/]/);
50
- if (parts.length <= 2) return path.slice(0, maxLen - 3) + "...";
55
+ if (parts.length <= 2) return truncateToWidth(path, maxLen, "...");
51
56
  // Keep first segment (e.g. ~) and as many trailing segments as fit.
52
57
  const tail: string[] = [];
53
58
  let tailLen = 0;
54
59
  for (let i = parts.length - 1; i >= 1; i--) {
55
60
  const seg = parts[i]!;
56
- if (tailLen + seg.length + 4 > maxLen) break;
61
+ if (tailLen + visibleWidth(seg) + 4 > maxLen) break;
57
62
  tail.unshift(seg);
58
- tailLen += seg.length + 1;
63
+ tailLen += visibleWidth(seg) + 1;
59
64
  }
60
65
  const head = parts[0]!;
61
66
  const result = `${head}${sepChar}...${sepChar}${tail.join(sepChar)}`;
62
- return result.length > maxLen ? result.slice(0, maxLen - 3) + "..." : result;
67
+ return visibleWidth(result) > maxLen ? truncateToWidth(result, maxLen, "...") : result;
63
68
  }
64
69
 
65
70
  export function finiteOrZero(value: unknown): number {
@@ -158,7 +163,25 @@ export function fitSegmentsByPriority(
158
163
  if (totalW() <= maxW) break;
159
164
  }
160
165
  }
166
+ // A segment whose `truncate` cannot actually shrink would otherwise spin here
167
+ // forever and block the render timer. Require each pass to make progress, and
168
+ // drop the worst segment when it does not, so layout always terminates.
169
+ let lastTotal = Infinity;
161
170
  while (totalW() > maxW) {
171
+ const nowTotal = totalW();
172
+ if (nowTotal >= lastTotal) {
173
+ let worst = -1;
174
+ for (let i = 0; i < items.length; i++) {
175
+ if (items[i].text !== "" && (worst === -1 || items[i].priority < items[worst].priority)) {
176
+ worst = i;
177
+ }
178
+ }
179
+ if (worst === -1) break;
180
+ items[worst].text = "";
181
+ items[worst].w = 0;
182
+ continue;
183
+ }
184
+ lastTotal = nowTotal;
162
185
  let target = -1;
163
186
  for (let i = 0; i < items.length; i++) {
164
187
  if (items[i].text !== "" && (target === -1 || items[i].priority < items[target].priority)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-tui-footer",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "pi extension: TUI footer/header with git status, telemetry, rounded tools, editor and settings enhancements",
5
5
  "type": "module",
6
6
  "keywords": [