pi-extension-utils 0.7.5 → 0.7.7

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.
@@ -76,6 +76,7 @@ export function createUiClient(fullscreen, ctx, capture = createTuiModeCapture()
76
76
  if (restored)
77
77
  return;
78
78
  restored = true;
79
+ priorRoot?.invalidate?.();
79
80
  layoutTui.setLayoutRoot(priorRoot);
80
81
  layoutTui.requestRender?.(true);
81
82
  };
@@ -22,11 +22,59 @@ export function connectWidgetCoordinator(pi, opts) {
22
22
  factory: record.factory,
23
23
  });
24
24
  }
25
+ function emitHello() {
26
+ pi.events.emit(EVENTS.hello, basePayload(clientId));
27
+ }
28
+ function createWidgetRecord(placement, key, factory, order) {
29
+ const record = {
30
+ placement,
31
+ key,
32
+ factory,
33
+ fallbackFactory: (tui, theme) => {
34
+ const mount = {
35
+ tui,
36
+ theme,
37
+ component: record.factory(tui, theme),
38
+ disposed: false,
39
+ };
40
+ record.fallbackMount = mount;
41
+ return {
42
+ render: (width) => (mount.disposed ? [] : mount.component.render(width)),
43
+ invalidate: () => {
44
+ if (!mount.disposed)
45
+ mount.component.invalidate?.();
46
+ },
47
+ dispose: () => {
48
+ if (mount.disposed)
49
+ return;
50
+ mount.disposed = true;
51
+ mount.component.dispose?.();
52
+ if (record.fallbackMount === mount)
53
+ record.fallbackMount = undefined;
54
+ },
55
+ };
56
+ },
57
+ order,
58
+ };
59
+ return record;
60
+ }
61
+ function updateFallback(record) {
62
+ const mount = record.fallbackMount;
63
+ if (!mount || mount.disposed) {
64
+ restoreFallback(record);
65
+ return;
66
+ }
67
+ mount.disposed = true;
68
+ mount.component.dispose?.();
69
+ mount.component = record.factory(mount.tui, mount.theme);
70
+ mount.disposed = false;
71
+ mount.tui?.requestRender?.();
72
+ }
25
73
  function clearFallback(record) {
26
74
  opts.ctx.ui.setWidget(record.key, undefined, { placement: record.placement });
27
75
  }
28
76
  function restoreFallback(record) {
29
- opts.ctx.ui.setWidget(record.key, record.factory, { placement: record.placement });
77
+ opts.ctx.ui.setWidget(record.key, record.fallbackFactory, { placement: record.placement });
30
78
  }
31
79
  function attach() {
32
80
  if (disposed || coordinated)
@@ -45,7 +93,7 @@ export function connectWidgetCoordinator(pi, opts) {
45
93
  return;
46
94
  attach();
47
95
  });
48
- pi.events.emit(EVENTS.hello, basePayload(clientId));
96
+ emitHello();
49
97
  const fullscreen = {
50
98
  acquire() {
51
99
  const token = `${clientId}-lease-${nextLeaseId++}`;
@@ -59,6 +107,8 @@ export function connectWidgetCoordinator(pi, opts) {
59
107
  for (const record of widgets.values())
60
108
  clearFallback(record);
61
109
  }
110
+ if (!coordinated)
111
+ emitHello();
62
112
  return {
63
113
  release() {
64
114
  if (released)
@@ -84,19 +134,31 @@ export function connectWidgetCoordinator(pi, opts) {
84
134
  set(placement, key, factory, setOpts = {}) {
85
135
  if (disposed)
86
136
  return;
87
- const record = {
88
- placement,
89
- key,
90
- factory: opts.tui ? withTuiCapture(factory, opts.tui) : factory,
91
- order: setOpts.order ?? 0,
92
- };
93
- widgets.set(widgetId(placement, key), record);
137
+ const id = widgetId(placement, key);
138
+ const wrappedFactory = opts.tui ? withTuiCapture(factory, opts.tui) : factory;
139
+ let record = widgets.get(id);
140
+ const isNew = !record;
141
+ if (record) {
142
+ record.factory = wrappedFactory;
143
+ record.order = setOpts.order ?? 0;
144
+ }
145
+ else {
146
+ record = createWidgetRecord(placement, key, wrappedFactory, setOpts.order ?? 0);
147
+ widgets.set(id, record);
148
+ }
94
149
  if (coordinated) {
95
150
  emitRegister(record);
96
151
  }
97
152
  else if (leases.size === 0) {
98
- opts.ctx.ui.setWidget(key, record.factory, { placement });
153
+ if (isNew) {
154
+ restoreFallback(record);
155
+ }
156
+ else {
157
+ updateFallback(record);
158
+ }
99
159
  }
160
+ if (!coordinated)
161
+ emitHello();
100
162
  },
101
163
  remove(placement, key) {
102
164
  const id = widgetId(placement, key);
package/docs/client.md CHANGED
@@ -45,7 +45,7 @@ await client.ui.fullscreen((tui, theme, keybindings, done) => new MyComponent(tu
45
45
  - acquires a fullscreen lease
46
46
  - blanks coordinated widgets
47
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
48
+ - temporarily replaces a capable fullscreen viewport TUI's layout root with the dashboard; restoration invalidates the prior root when supported, reinstalls that exact root, then forces a redraw
49
49
  - uses the overlay path as the safe cold-start/fallback mount without rendering the real dashboard twice
50
50
  - releases the lease in `finally`
51
51
 
package/docs/widgets.md CHANGED
@@ -50,7 +50,7 @@ await client.ui.fullscreen((tui, theme, keybindings, done) => new MyComponent(tu
50
50
 
51
51
  ## Fallback
52
52
 
53
- If the host is not ready yet, widget calls use the extension's own `ctx.ui.setWidget`. When the host announces readiness, the client clears the fallback widget and re-registers through the coordinator.
53
+ If the host is not ready yet, each logical widget registers one stable proxy through the extension's own `ctx.ui.setWidget`. Later `widgets.set` calls replace the proxy's delegated component and request a redraw without re-registering the raw widget, preserving cross-extension insertion order on Pi versions that delete before setting. Set and fullscreen-acquire activity also retry the hello handshake without a polling timer. When the host announces readiness, the client clears the fallback widget and re-registers through the coordinator.
54
54
 
55
55
  ## Example
56
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-extension-utils",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
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",