@zigai/pi-footer 0.1.8 → 0.1.9

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": "@zigai/pi-footer",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Pi package that replaces Pi's footer with a compact status line.",
5
5
  "keywords": [
6
6
  "footer",
package/src/index.ts CHANGED
@@ -5,9 +5,11 @@ import {
5
5
  patchFooterReset,
6
6
  rememberFooterForTransition,
7
7
  } from "./footer-transition.ts";
8
+ import { patchTuiShrinkRedraw } from "./tui-shrink-redraw.ts";
8
9
 
9
10
  export default function uiEnhancements(pi: ExtensionAPI) {
10
11
  patchFooterReset();
12
+ patchTuiShrinkRedraw();
11
13
 
12
14
  const getThinkingLevel = () => pi.getThinkingLevel();
13
15
 
@@ -0,0 +1,95 @@
1
+ import { TUI } from "@earendil-works/pi-tui";
2
+
3
+ const TUI_SHRINK_REDRAW_PATCH_MARKER = Symbol.for("zigai.pi-footer.tui-shrink-redraw-patch");
4
+
5
+ type PatchableTuiInstance = {
6
+ previousLines?: unknown;
7
+ previousViewportTop?: unknown;
8
+ fullRedrawCount?: unknown;
9
+ overlayStack?: unknown;
10
+ requestRender(force?: boolean): void;
11
+ };
12
+
13
+ type PatchableTuiPrototype = {
14
+ doRender?: (this: PatchableTuiInstance) => void;
15
+ requestRender?: (this: PatchableTuiInstance, force?: boolean) => void;
16
+ [TUI_SHRINK_REDRAW_PATCH_MARKER]?: true;
17
+ };
18
+
19
+ function getLineCount(tui: PatchableTuiInstance): number {
20
+ if (!Array.isArray(tui.previousLines)) return 0;
21
+ return tui.previousLines.length;
22
+ }
23
+
24
+ function getNumber(value: unknown): number | undefined {
25
+ if (typeof value !== "number") return undefined;
26
+ if (!Number.isFinite(value)) return undefined;
27
+ return value;
28
+ }
29
+
30
+ function hasOverlayStack(tui: PatchableTuiInstance): boolean {
31
+ if (!Array.isArray(tui.overlayStack)) return false;
32
+ return tui.overlayStack.length > 0;
33
+ }
34
+
35
+ function fullRedrawOccurred(previous: number | undefined, next: number | undefined): boolean {
36
+ if (previous === undefined || next === undefined) return false;
37
+ return next > previous;
38
+ }
39
+
40
+ function shouldForceRedrawAfterShrink(
41
+ tui: PatchableTuiInstance,
42
+ previousLineCount: number,
43
+ nextLineCount: number,
44
+ previousViewportTop: number | undefined,
45
+ previousFullRedrawCount: number | undefined,
46
+ ): boolean {
47
+ if (nextLineCount >= previousLineCount) return false;
48
+ if (previousViewportTop === undefined || previousViewportTop <= 0) return false;
49
+ if (hasOverlayStack(tui)) return false;
50
+
51
+ const nextFullRedrawCount = getNumber(tui.fullRedrawCount);
52
+ if (fullRedrawOccurred(previousFullRedrawCount, nextFullRedrawCount)) return false;
53
+
54
+ return true;
55
+ }
56
+
57
+ export function patchTuiShrinkRedraw(): void {
58
+ // Pi's differential renderer does not re-anchor the viewport when content
59
+ // shrinks while scrolled. That can leave the footer one row above the
60
+ // terminal bottom after a transient loader/widget row disappears. Force a
61
+ // one-shot full redraw after such a shrink so the viewport snaps back to the
62
+ // bottom without requiring extension widgets to render placeholder rows.
63
+ const prototype = TUI.prototype as unknown as PatchableTuiPrototype;
64
+ if (prototype[TUI_SHRINK_REDRAW_PATCH_MARKER] === true) return;
65
+
66
+ const originalDoRender = prototype.doRender;
67
+ const originalRequestRender = prototype.requestRender;
68
+ if (typeof originalDoRender !== "function") return;
69
+ if (typeof originalRequestRender !== "function") return;
70
+
71
+ prototype.doRender = function patchedDoRender(this: PatchableTuiInstance): void {
72
+ const previousLineCount = getLineCount(this);
73
+ const previousViewportTop = getNumber(this.previousViewportTop);
74
+ const previousFullRedrawCount = getNumber(this.fullRedrawCount);
75
+
76
+ originalDoRender.call(this);
77
+
78
+ const nextLineCount = getLineCount(this);
79
+ if (
80
+ !shouldForceRedrawAfterShrink(
81
+ this,
82
+ previousLineCount,
83
+ nextLineCount,
84
+ previousViewportTop,
85
+ previousFullRedrawCount,
86
+ )
87
+ ) {
88
+ return;
89
+ }
90
+
91
+ originalRequestRender.call(this, true);
92
+ };
93
+
94
+ prototype[TUI_SHRINK_REDRAW_PATCH_MARKER] = true;
95
+ }