pi-extension-utils 0.7.4 → 0.7.5

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
@@ -1,6 +1,6 @@
1
1
  # pi-extension-utils
2
2
 
3
- Shared utilities for Pi extensions that need coordinated UI, fullscreen overlays, reminders, and logging.
3
+ Shared utilities for Pi extensions that need coordinated UI, fullscreen custom UIs, reminders, and logging.
4
4
 
5
5
  Two faces:
6
6
 
@@ -14,6 +14,39 @@ export function createTuiModeCapture() {
14
14
  },
15
15
  };
16
16
  }
17
+ function getLayoutRootTui(tui) {
18
+ if ((typeof tui !== "object" && typeof tui !== "function") || tui === null)
19
+ return undefined;
20
+ if (!("layoutRoot" in tui) || typeof tui.setLayoutRoot !== "function")
21
+ return undefined;
22
+ return tui;
23
+ }
24
+ // Pi 0.84 exposes setLayoutRoot() but no getter. Its TypeScript-private
25
+ // layoutRoot remains a runtime property; keep that compatibility bridge here.
26
+ function getLayoutRoot(tui) {
27
+ return tui.layoutRoot;
28
+ }
29
+ function createOverlayInputProxy(component) {
30
+ const target = component;
31
+ const proxy = {
32
+ render: () => [],
33
+ invalidate: () => target.invalidate?.(),
34
+ handleInput: (data) => target.handleInput?.(data),
35
+ get wantsKeyRelease() {
36
+ return target.wantsKeyRelease;
37
+ },
38
+ dispose: () => target.dispose?.(),
39
+ };
40
+ if (((typeof target === "object" && target !== null) || typeof target === "function") && "focused" in target) {
41
+ Object.defineProperty(proxy, "focused", {
42
+ get: () => target.focused,
43
+ set: (focused) => {
44
+ target.focused = focused;
45
+ },
46
+ });
47
+ }
48
+ return proxy;
49
+ }
17
50
  export function createUiClient(fullscreen, ctx, capture = createTuiModeCapture()) {
18
51
  return {
19
52
  async fullscreen(factory) {
@@ -22,20 +55,52 @@ export function createUiClient(fullscreen, ctx, capture = createTuiModeCapture()
22
55
  throw new Error("pi-extension-utils: ui.fullscreen requires an interactive UI (ctx.ui.custom is unavailable)");
23
56
  }
24
57
  const lease = fullscreen.acquire();
58
+ let restoreLayoutRoot;
59
+ let closed = false;
60
+ const close = () => {
61
+ closed = true;
62
+ restoreLayoutRoot?.();
63
+ };
25
64
  try {
26
65
  const wrapped = (tui, theme, keybindings, done) => {
27
66
  capture.noteTui(tui);
28
- return factory(tui, theme, keybindings, done);
67
+ const mount = (component) => {
68
+ const layoutTui = capture.isFullscreenTui() ? getLayoutRootTui(tui) : undefined;
69
+ if (!layoutTui)
70
+ return component;
71
+ if (closed)
72
+ return createOverlayInputProxy(component);
73
+ const priorRoot = getLayoutRoot(layoutTui);
74
+ let restored = false;
75
+ restoreLayoutRoot = () => {
76
+ if (restored)
77
+ return;
78
+ restored = true;
79
+ layoutTui.setLayoutRoot(priorRoot);
80
+ layoutTui.requestRender?.(true);
81
+ };
82
+ layoutTui.setLayoutRoot(component);
83
+ layoutTui.requestRender?.(true);
84
+ return createOverlayInputProxy(component);
85
+ };
86
+ const component = factory(tui, theme, keybindings, (result) => {
87
+ close();
88
+ done(result);
89
+ });
90
+ if (component && typeof component.then === "function") {
91
+ return Promise.resolve(component).then(mount);
92
+ }
93
+ return mount(component);
29
94
  };
30
95
  // In the fullscreen TUI a plain custom component mounts inside the
31
- // bottom dock (the editor slot), where the dock layout shrinks and
32
- // clips it while the transcript tail renders above it. Mount it as a
33
- // full-screen overlay instead so the component owns the whole screen,
34
- // matching the regular-TUI behavior it was designed for.
96
+ // bottom dock. Use the overlay path for focus/lifecycle, then let the
97
+ // wrapped factory replace a capable viewport TUI's layout root. The
98
+ // returned empty input proxy keeps the host overlay from rendering the
99
+ // same dashboard a second time.
35
100
  // Before any wrapped factory has rendered, the mode is unknown. Default
36
101
  // that cold-start case to an overlay: it is the only mount that remains
37
- // fullscreen and receives focus in the alt-screen TUI. Preserve the
38
- // legacy editor-slot mount only when regular mode was positively seen.
102
+ // safe in the alt-screen TUI. Preserve the legacy editor-slot mount only
103
+ // when regular mode was positively seen.
39
104
  const options = capture.isRegularTui()
40
105
  ? undefined
41
106
  : {
@@ -45,7 +110,12 @@ export function createUiClient(fullscreen, ctx, capture = createTuiModeCapture()
45
110
  return await ui.custom(wrapped, options);
46
111
  }
47
112
  finally {
48
- lease.release();
113
+ try {
114
+ close();
115
+ }
116
+ finally {
117
+ lease.release();
118
+ }
49
119
  }
50
120
  },
51
121
  };
package/docs/client.md CHANGED
@@ -44,9 +44,13 @@ await client.ui.fullscreen((tui, theme, keybindings, done) => new MyComponent(tu
44
44
 
45
45
  - acquires a fullscreen lease
46
46
  - blanks coordinated widgets
47
- - calls `ctx.ui.custom()`
47
+ - preserves the regular-TUI editor-slot mount
48
+ - temporarily replaces a capable fullscreen viewport TUI's layout root with the dashboard and forces redraws on install and restoration
49
+ - uses the overlay path as the safe cold-start/fallback mount without rendering the real dashboard twice
48
50
  - releases the lease in `finally`
49
51
 
52
+ Pi 0.84.x exposes the layout-root setter but no public getter, so exact restoration uses its runtime `layoutRoot` property through an isolated structural bridge. If a fullscreen TUI does not expose that setter/property shape, `ui.fullscreen()` safely falls back to the full-size overlay; that fallback cannot guarantee that terminal image placements from the transcript are cleared.
53
+
50
54
  Use `client.fullscreen.acquire()` only when you need manual lease control.
51
55
 
52
56
  ## Pane overlay
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-extension-utils",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Shared Pi extension utilities for coordinated widgets, fullscreen leases, and logging.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",