pi-extension-utils 0.7.6 → 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.
@@ -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/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.6",
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",