@nativedesktop/react 0.4.1 → 0.4.3

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/dist/index.js CHANGED
@@ -87,57 +87,81 @@ class BinaryUnsupportedValue extends Error {
87
87
  var textEncoder = new TextEncoder;
88
88
 
89
89
  class ByteWriter {
90
- ab = new ArrayBuffer(256);
91
- buf = new Uint8Array(this.ab);
92
- view = new DataView(this.ab);
90
+ buf;
91
+ view;
92
+ cap;
93
93
  len = 0;
94
- ensure(n) {
95
- if (this.len + n <= this.ab.byteLength)
96
- return;
97
- let cap = this.ab.byteLength * 2;
94
+ constructor(capacity) {
95
+ this.buf = new Uint8Array(capacity);
96
+ this.view = new DataView(this.buf.buffer);
97
+ this.cap = capacity;
98
+ }
99
+ grow(n) {
100
+ let cap = this.cap * 2;
98
101
  while (cap < this.len + n)
99
102
  cap *= 2;
100
- const nextAb = new ArrayBuffer(cap);
101
- new Uint8Array(nextAb).set(this.buf.subarray(0, this.len));
102
- this.ab = nextAb;
103
- this.buf = new Uint8Array(nextAb);
104
- this.view = new DataView(nextAb);
103
+ const next = new Uint8Array(cap);
104
+ next.set(this.buf.subarray(0, this.len));
105
+ this.buf = next;
106
+ this.view = new DataView(next.buffer);
107
+ this.cap = cap;
105
108
  }
106
109
  u8(v) {
107
- this.ensure(1);
110
+ if (this.len + 1 > this.cap)
111
+ this.grow(1);
108
112
  this.buf[this.len++] = v & 255;
109
113
  }
110
114
  u16(v) {
111
- this.ensure(2);
115
+ if (this.len + 2 > this.cap)
116
+ this.grow(2);
112
117
  this.view.setUint16(this.len, v, true);
113
118
  this.len += 2;
114
119
  }
115
120
  u32(v) {
116
- this.ensure(4);
121
+ if (this.len + 4 > this.cap)
122
+ this.grow(4);
117
123
  this.view.setUint32(this.len, v >>> 0, true);
118
124
  this.len += 4;
119
125
  }
120
126
  i64(v) {
121
- this.ensure(8);
127
+ if (this.len + 8 > this.cap)
128
+ this.grow(8);
122
129
  this.view.setBigInt64(this.len, BigInt(v), true);
123
130
  this.len += 8;
124
131
  }
125
132
  f64(v) {
126
- this.ensure(8);
133
+ if (this.len + 8 > this.cap)
134
+ this.grow(8);
127
135
  this.view.setFloat64(this.len, v, true);
128
136
  this.len += 8;
129
137
  }
130
- bytes(b) {
131
- this.ensure(b.length);
132
- this.buf.set(b, this.len);
133
- this.len += b.length;
134
- }
135
138
  lenString(s) {
136
- this.ensure(4 + s.length * 3);
139
+ const n = s.length;
140
+ if (this.len + 4 + n * 3 > this.cap)
141
+ this.grow(4 + n * 3);
137
142
  const lenPos = this.len;
138
- const { written } = textEncoder.encodeInto(s, this.buf.subarray(this.len + 4));
139
- this.view.setUint32(lenPos, written, true);
140
- this.len += 4 + written;
143
+ let p = lenPos + 4;
144
+ let ascii = true;
145
+ for (let i = 0;i < n; i++) {
146
+ const c = s.charCodeAt(i);
147
+ if (c > 127) {
148
+ ascii = false;
149
+ break;
150
+ }
151
+ this.buf[p++] = c;
152
+ }
153
+ if (!ascii) {
154
+ const { written } = textEncoder.encodeInto(s, this.buf.subarray(lenPos + 4));
155
+ p = lenPos + 4 + written;
156
+ }
157
+ this.view.setUint32(lenPos, p - lenPos - 4, true);
158
+ this.len = p;
159
+ }
160
+ patchU16(pos, v) {
161
+ this.view.setUint16(pos, v, true);
162
+ }
163
+ patchU32(pos, v) {
164
+ this.view.setUint32(pos, v >>> 0, true);
141
165
  }
142
166
  slice() {
143
167
  return this.buf.slice(0, this.len);
@@ -155,93 +179,103 @@ function encodeCommitBatchBinary(batch) {
155
179
  index.set(s, i);
156
180
  return i;
157
181
  };
158
- const ops = new ByteWriter;
182
+ let lastKey = "";
183
+ let lastKeyRef = 0;
184
+ const internKey = (k) => {
185
+ if (k === lastKey)
186
+ return lastKeyRef;
187
+ lastKey = k;
188
+ lastKeyRef = intern(k);
189
+ return lastKeyRef;
190
+ };
191
+ const out = new ByteWriter(32 * batch.ops.length + 4096);
192
+ out.u8(78);
193
+ out.u8(1);
194
+ for (let i = 0;i < 6; i++)
195
+ out.u8(0);
196
+ out.i64(batch.commitId);
197
+ out.u32(batch.generation);
198
+ out.u32(batch.ops.length);
199
+ const stringTableOffsetPos = out.len;
200
+ out.u32(0);
159
201
  const writeProps = (props) => {
160
- const keys = Object.keys(props);
161
- ops.u16(keys.length);
162
- for (const k of keys) {
163
- ops.u32(intern(k));
202
+ const countPos = out.len;
203
+ out.u16(0);
204
+ let count = 0;
205
+ for (const k in props) {
206
+ count++;
207
+ out.u32(internKey(k));
164
208
  const v = props[k];
165
209
  if (v === null || v === undefined) {
166
- ops.u8(0);
210
+ out.u8(0);
167
211
  } else if (typeof v === "boolean") {
168
- ops.u8(1);
169
- ops.u8(v ? 1 : 0);
212
+ out.u8(1);
213
+ out.u8(v ? 1 : 0);
170
214
  } else if (typeof v === "number") {
171
215
  if (Number.isInteger(v)) {
172
- ops.u8(2);
173
- ops.i64(v);
216
+ out.u8(2);
217
+ out.i64(v);
174
218
  } else {
175
- ops.u8(3);
176
- ops.f64(v);
219
+ out.u8(3);
220
+ out.f64(v);
177
221
  }
178
222
  } else if (typeof v === "string") {
179
- ops.u8(4);
180
- ops.u32(intern(v));
223
+ out.u8(4);
224
+ out.u32(intern(v));
181
225
  } else {
182
226
  throw new BinaryUnsupportedValue(k, v);
183
227
  }
184
228
  }
229
+ out.patchU16(countPos, count);
185
230
  };
186
231
  for (const op of batch.ops) {
187
232
  switch (op.op) {
188
233
  case "create": {
189
- ops.u8(1);
190
- ops.u32(op.id);
234
+ out.u8(1);
235
+ out.u32(op.id);
191
236
  const wt = WIDGET_TYPE[op.widget];
192
237
  if (wt === undefined)
193
238
  throw new Error(`ndp-binary: unknown widget "${op.widget}"`);
194
- ops.u16(wt);
239
+ out.u16(wt);
195
240
  writeProps(op.props);
196
241
  break;
197
242
  }
198
243
  case "append":
199
- ops.u8(2);
200
- ops.u32(op.parent);
201
- ops.u32(op.child);
244
+ out.u8(2);
245
+ out.u32(op.parent);
246
+ out.u32(op.child);
202
247
  break;
203
248
  case "insertBefore":
204
- ops.u8(3);
205
- ops.u32(op.parent);
206
- ops.u32(op.child);
207
- ops.u32(op.before ?? 0);
249
+ out.u8(3);
250
+ out.u32(op.parent);
251
+ out.u32(op.child);
252
+ out.u32(op.before ?? 0);
208
253
  break;
209
254
  case "remove":
210
- ops.u8(4);
211
- ops.u32(op.id);
255
+ out.u8(4);
256
+ out.u32(op.id);
212
257
  break;
213
258
  case "setText":
214
- ops.u8(5);
215
- ops.u32(op.id);
216
- ops.u32(intern(op.text));
259
+ out.u8(5);
260
+ out.u32(op.id);
261
+ out.u32(intern(op.text));
217
262
  break;
218
263
  case "update":
219
- ops.u8(6);
220
- ops.u32(op.id);
264
+ out.u8(6);
265
+ out.u32(op.id);
221
266
  writeProps(op.props);
222
267
  break;
223
268
  case "hide":
224
- ops.u8(7);
225
- ops.u32(op.id);
269
+ out.u8(7);
270
+ out.u32(op.id);
226
271
  break;
227
272
  case "unhide":
228
- ops.u8(8);
229
- ops.u32(op.id);
273
+ out.u8(8);
274
+ out.u32(op.id);
230
275
  break;
231
276
  }
232
277
  }
233
- const opBytes = ops.slice();
234
- const stringTableOffset = 28 + opBytes.length;
235
- const out = new ByteWriter;
236
- out.u8(78);
237
- out.u8(1);
238
- for (let i = 0;i < 6; i++)
239
- out.u8(0);
240
- out.i64(batch.commitId);
241
- out.u32(batch.generation);
242
- out.u32(batch.ops.length);
243
- out.u32(stringTableOffset);
244
- out.bytes(opBytes);
278
+ out.patchU32(stringTableOffsetPos, out.len);
245
279
  out.u32(strings.length);
246
280
  for (const s of strings)
247
281
  out.lenString(s);
@@ -253,10 +287,15 @@ var NDP_VERSION = 1;
253
287
 
254
288
  // ../../runtime/ndp.ts
255
289
  var TRACE = process.env.NDP_TRACE === "1";
290
+ var textDecoder = new TextDecoder;
291
+ var textEncoder2 = new TextEncoder;
256
292
 
257
293
  class Ndp {
258
294
  socket;
259
- inbox = new Uint8Array(0);
295
+ inbox = new Uint8Array(64 * 1024);
296
+ inboxView = new DataView(this.inbox.buffer);
297
+ inboxHead = 0;
298
+ inboxTail = 0;
260
299
  eventCb = null;
261
300
  helloAckResolve = null;
262
301
  outbox = [];
@@ -321,22 +360,52 @@ class Ndp {
321
360
  }
322
361
  }
323
362
  }
363
+ reserveInbox(extra) {
364
+ const pending = this.inboxTail - this.inboxHead;
365
+ if (this.inboxTail + extra <= this.inbox.length)
366
+ return;
367
+ if (pending + extra <= this.inbox.length) {
368
+ this.compactInbox();
369
+ return;
370
+ }
371
+ let cap = this.inbox.length * 2;
372
+ while (cap < pending + extra)
373
+ cap *= 2;
374
+ const next = new Uint8Array(cap);
375
+ next.set(this.inbox.subarray(this.inboxHead, this.inboxTail));
376
+ this.inbox = next;
377
+ this.inboxView = new DataView(next.buffer);
378
+ this.inboxHead = 0;
379
+ this.inboxTail = pending;
380
+ }
381
+ compactInbox() {
382
+ if (this.inboxHead === 0)
383
+ return;
384
+ this.inbox.copyWithin(0, this.inboxHead, this.inboxTail);
385
+ this.inboxTail -= this.inboxHead;
386
+ this.inboxHead = 0;
387
+ }
324
388
  onData(chunk) {
325
- const merged = new Uint8Array(this.inbox.length + chunk.length);
326
- merged.set(this.inbox, 0);
327
- merged.set(chunk, this.inbox.length);
328
- this.inbox = merged;
329
- while (this.inbox.length >= 4) {
330
- const view = new DataView(this.inbox.buffer, this.inbox.byteOffset, 4);
331
- const len = view.getUint32(0, true);
332
- if (this.inbox.length < 4 + len)
389
+ this.reserveInbox(chunk.length);
390
+ this.inbox.set(chunk, this.inboxTail);
391
+ this.inboxTail += chunk.length;
392
+ while (this.inboxTail - this.inboxHead >= 4) {
393
+ const len = this.inboxView.getUint32(this.inboxHead, true);
394
+ if (this.inboxTail - this.inboxHead < 4 + len)
333
395
  break;
334
- const json = new TextDecoder().decode(this.inbox.subarray(4, 4 + len));
335
- this.inbox = this.inbox.subarray(4 + len);
396
+ const start = this.inboxHead + 4;
397
+ const json = textDecoder.decode(this.inbox.subarray(start, start + len));
398
+ this.inboxHead = start + len;
336
399
  if (TRACE)
337
400
  console.error("<< " + json);
338
401
  this.dispatch(JSON.parse(json));
339
402
  }
403
+ if (this.inboxHead === this.inboxTail) {
404
+ this.inboxHead = 0;
405
+ this.inboxTail = 0;
406
+ } else if (this.inboxHead * 2 >= this.inbox.length) {
407
+ this.compactInbox();
408
+ }
340
409
  }
341
410
  dispatch(msg) {
342
411
  if (msg.type === "helloAck") {
@@ -366,12 +435,12 @@ class Ndp {
366
435
  }
367
436
  }
368
437
  send(obj) {
369
- const json = new TextEncoder().encode(JSON.stringify(obj));
438
+ const json = textEncoder2.encode(JSON.stringify(obj));
370
439
  const frame = new Uint8Array(4 + json.length);
371
440
  new DataView(frame.buffer).setUint32(0, json.length, true);
372
441
  frame.set(json, 4);
373
442
  if (TRACE)
374
- console.error(">> " + new TextDecoder().decode(json));
443
+ console.error(">> " + textDecoder.decode(json));
375
444
  const wasEmpty = this.outbox.length === 0;
376
445
  this.outbox.push(frame);
377
446
  if (wasEmpty)
@@ -524,7 +593,7 @@ var widgetEvents = {
524
593
  tabview: [{ name: "selectionChanged", handler: "onSelectionChanged", payload: "index" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
525
594
  grid: [{ name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
526
595
  listview: [{ name: "rowActivated", handler: "onRowActivated", payload: "index" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
527
- webview: [{ name: "navigate", handler: "onNavigate", payload: "text" }, { name: "titleChanged", handler: "onTitleChanged", payload: "text" }, { name: "loadingChanged", handler: "onLoadingChanged", payload: "checked" }, { name: "backAvailable", handler: "onBackAvailable", payload: "checked" }, { name: "forwardAvailable", handler: "onForwardAvailable", payload: "checked" }, { name: "loadProgress", handler: "onLoadProgress", payload: "value" }, { name: "loadFailed", handler: "onLoadFailed", payload: "data" }, { name: "newWindow", handler: "onNewWindow", payload: "text" }, { name: "downloadRequested", handler: "onDownloadRequested", payload: "data" }, { name: "javaScriptResult", handler: "onJavaScriptResult", payload: "data" }, { name: "scriptMessage", handler: "onScriptMessage", payload: "data" }, { name: "schemeRequest", handler: "onSchemeRequest", payload: "data" }, { name: "cookiesResult", handler: "onCookiesResult", payload: "data" }, { name: "cookiesChanged", handler: "onCookiesChanged", payload: "data" }, { name: "faviconChanged", handler: "onFaviconChanged", payload: "data" }, { name: "findResult", handler: "onFindResult", payload: "data" }, { name: "securityChanged", handler: "onSecurityChanged", payload: "data" }, { name: "linkHover", handler: "onLinkHover", payload: "text" }, { name: "contextMenu", handler: "onContextMenu", payload: "data" }, { name: "contextMenuItemClicked", handler: "onContextMenuItemClicked", payload: "data" }, { name: "sessionSaved", handler: "onSessionSaved", payload: "data" }, { name: "audioStateChanged", handler: "onAudioStateChanged", payload: "data" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
596
+ webview: [{ name: "navigate", handler: "onNavigate", payload: "text" }, { name: "titleChanged", handler: "onTitleChanged", payload: "text" }, { name: "loadingChanged", handler: "onLoadingChanged", payload: "checked" }, { name: "backAvailable", handler: "onBackAvailable", payload: "checked" }, { name: "forwardAvailable", handler: "onForwardAvailable", payload: "checked" }, { name: "loadProgress", handler: "onLoadProgress", payload: "value" }, { name: "loadFailed", handler: "onLoadFailed", payload: "data" }, { name: "newWindow", handler: "onNewWindow", payload: "text" }, { name: "downloadRequested", handler: "onDownloadRequested", payload: "data" }, { name: "javaScriptResult", handler: "onJavaScriptResult", payload: "data" }, { name: "scriptMessage", handler: "onScriptMessage", payload: "data" }, { name: "schemeRequest", handler: "onSchemeRequest", payload: "data" }, { name: "cookiesResult", handler: "onCookiesResult", payload: "data" }, { name: "cookiesChanged", handler: "onCookiesChanged", payload: "data" }, { name: "faviconChanged", handler: "onFaviconChanged", payload: "data" }, { name: "findResult", handler: "onFindResult", payload: "data" }, { name: "securityChanged", handler: "onSecurityChanged", payload: "data" }, { name: "linkHover", handler: "onLinkHover", payload: "text" }, { name: "contextMenu", handler: "onContextMenu", payload: "data" }, { name: "contextMenuItemClicked", handler: "onContextMenuItemClicked", payload: "data" }, { name: "sessionSaved", handler: "onSessionSaved", payload: "data" }, { name: "audioStateChanged", handler: "onAudioStateChanged", payload: "data" }, { name: "extensionsList", handler: "onExtensionsList", payload: "data" }, { name: "extensionActions", handler: "onExtensionActions", payload: "data" }, { name: "chromeDialog", handler: "onChromeDialog", payload: "data" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
528
597
  nativeview: [{ name: "nativeEvent", handler: "onNativeEvent", payload: "data" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
529
598
  splitview: [{ name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
530
599
  headerbar: [{ name: "back", handler: "onBack", payload: "none" }, { name: "forward", handler: "onForward", payload: "none" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
@@ -578,6 +647,9 @@ var widgetEvents = {
578
647
  chart: [{ name: "pointSelected", handler: "onPointSelected", payload: "data" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }],
579
648
  codeeditor: [{ name: "changed", handler: "onChange", payload: "text" }, { name: "cursorMoved", handler: "onCursorMoved", payload: "data" }, { name: "diagnosticClicked", handler: "onDiagnosticClicked", payload: "data" }, { name: "dragStarted", handler: "onDragStarted", payload: "text" }, { name: "dragEnded", handler: "onDragEnded", payload: "none" }, { name: "dragOver", handler: "onDragOver", payload: "dragPoint" }, { name: "dropped", handler: "onDropped", payload: "dragPoint" }]
580
649
  };
650
+ var widgetRefProps = {
651
+ popover: { anchorRef: "anchor" }
652
+ };
581
653
  var handlerPropNames = {
582
654
  window: ["onAlertResult", "onOpenFileResult", "onSaveFileResult", "onNewTabRequested", "onClosed", "onFocused", "onSizeChanged", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
583
655
  box: ["onHoverChanged", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
@@ -597,7 +669,7 @@ var handlerPropNames = {
597
669
  tabview: ["onSelectionChanged", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
598
670
  grid: ["onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
599
671
  listview: ["onRowActivated", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
600
- webview: ["onNavigate", "onTitleChanged", "onLoadingChanged", "onBackAvailable", "onForwardAvailable", "onLoadProgress", "onLoadFailed", "onNewWindow", "onDownloadRequested", "onJavaScriptResult", "onScriptMessage", "onSchemeRequest", "onCookiesResult", "onCookiesChanged", "onFaviconChanged", "onFindResult", "onSecurityChanged", "onLinkHover", "onContextMenu", "onContextMenuItemClicked", "onSessionSaved", "onAudioStateChanged", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
672
+ webview: ["onNavigate", "onTitleChanged", "onLoadingChanged", "onBackAvailable", "onForwardAvailable", "onLoadProgress", "onLoadFailed", "onNewWindow", "onDownloadRequested", "onJavaScriptResult", "onScriptMessage", "onSchemeRequest", "onCookiesResult", "onCookiesChanged", "onFaviconChanged", "onFindResult", "onSecurityChanged", "onLinkHover", "onContextMenu", "onContextMenuItemClicked", "onSessionSaved", "onAudioStateChanged", "onExtensionsList", "onExtensionActions", "onChromeDialog", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
601
673
  nativeview: ["onNativeEvent", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
602
674
  splitview: ["onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
603
675
  headerbar: ["onBack", "onForward", "onDragStarted", "onDragEnded", "onDragOver", "onDropped"],
@@ -670,7 +742,7 @@ var widgetCommands = {
670
742
  tabview: [],
671
743
  grid: [],
672
744
  listview: [],
673
- webview: ["goBack", "goForward", "reload", "stop", "executeJavaScript", "setZoom", "setUserAgent", "openDevTools", "addUserScript", "removeUserScript", "clearUserScripts", "registerScriptMessage", "unregisterScriptMessage", "respondScheme", "getCookies", "setCookie", "deleteCookie", "findStart", "findNext", "findPrevious", "findStop", "saveSession", "restoreSession", "setMuted", "setContextMenuItems", "focus"],
745
+ webview: ["goBack", "goForward", "reload", "stop", "executeJavaScript", "setZoom", "setUserAgent", "openDevTools", "addUserScript", "removeUserScript", "clearUserScripts", "registerScriptMessage", "unregisterScriptMessage", "respondScheme", "getCookies", "setCookie", "deleteCookie", "findStart", "findNext", "findPrevious", "findStop", "saveSession", "restoreSession", "setMuted", "setContextMenuItems", "listExtensions", "listExtensionActions", "installExtension", "uninstallExtension", "setExtensionEnabled", "focus"],
674
746
  nativeview: [],
675
747
  splitview: [],
676
748
  headerbar: [],
@@ -1114,6 +1186,7 @@ function emitCreateIfNew(inst) {
1114
1186
  if (inst.type === "label" && text !== undefined)
1115
1187
  props.text = text;
1116
1188
  delete props.children;
1189
+ resolveRefProps(inst, props);
1117
1190
  for (const h of handlerPropNames[inst.type] ?? [])
1118
1191
  delete props[h];
1119
1192
  activeBatch.push({ op: "create", id: inst.id, widget: intrinsicToName[inst.type], props });
@@ -1123,6 +1196,89 @@ function emitCreateIfNew(inst) {
1123
1196
  activeBatch.push({ op: "append", parent: inst.id, child: child.id });
1124
1197
  }
1125
1198
  }
1199
+ var lastRefIds = new WeakMap;
1200
+ function refId(v) {
1201
+ const id = v?.current?.id;
1202
+ return typeof id === "number" ? id : undefined;
1203
+ }
1204
+ function resolveRefProps(inst, props) {
1205
+ const refs = widgetRefProps[inst.type];
1206
+ if (!refs)
1207
+ return;
1208
+ const sent = {};
1209
+ for (const [refName, wireName] of Object.entries(refs)) {
1210
+ if (!(refName in props))
1211
+ continue;
1212
+ const id = refId(props[refName]);
1213
+ delete props[refName];
1214
+ if (id === undefined)
1215
+ continue;
1216
+ props[wireName] = id;
1217
+ sent[wireName] = id;
1218
+ }
1219
+ lastRefIds.set(inst, sent);
1220
+ }
1221
+ function diffRefProps(inst, props, changed) {
1222
+ const refs = widgetRefProps[inst.type];
1223
+ if (!refs)
1224
+ return;
1225
+ let sent = lastRefIds.get(inst);
1226
+ if (!sent) {
1227
+ sent = {};
1228
+ lastRefIds.set(inst, sent);
1229
+ }
1230
+ for (const [refName, wireName] of Object.entries(refs)) {
1231
+ if (wireName in props)
1232
+ continue;
1233
+ const id = refName in props ? refId(props[refName]) : undefined;
1234
+ if (id === sent[wireName])
1235
+ continue;
1236
+ if (id === undefined) {
1237
+ changed[wireName] = null;
1238
+ delete sent[wireName];
1239
+ continue;
1240
+ }
1241
+ changed[wireName] = id;
1242
+ sent[wireName] = id;
1243
+ }
1244
+ }
1245
+ var PROP_COMPARE_DEPTH = 4;
1246
+ function isPlainObject(v) {
1247
+ if (typeof v !== "object" || v === null)
1248
+ return false;
1249
+ const proto = Object.getPrototypeOf(v);
1250
+ return proto === Object.prototype || proto === null;
1251
+ }
1252
+ function propsEqual(a, b, depth = 0) {
1253
+ if (Object.is(a, b))
1254
+ return true;
1255
+ if (depth >= PROP_COMPARE_DEPTH)
1256
+ return false;
1257
+ if (Array.isArray(a)) {
1258
+ if (!Array.isArray(b) || a.length !== b.length)
1259
+ return false;
1260
+ for (let i = 0;i < a.length; i++)
1261
+ if (!propsEqual(a[i], b[i], depth + 1))
1262
+ return false;
1263
+ return true;
1264
+ }
1265
+ if (!isPlainObject(a) || !isPlainObject(b))
1266
+ return false;
1267
+ const keys = Object.keys(a);
1268
+ if (keys.length !== Object.keys(b).length)
1269
+ return false;
1270
+ for (const k of keys) {
1271
+ if (!Object.prototype.hasOwnProperty.call(b, k))
1272
+ return false;
1273
+ if (!propsEqual(a[k], b[k], depth + 1))
1274
+ return false;
1275
+ }
1276
+ return true;
1277
+ }
1278
+ var REMOVAL_VALUE = {
1279
+ style: Object.freeze({}),
1280
+ cssClasses: Object.freeze([])
1281
+ };
1126
1282
  function setPriorityFor(kind) {
1127
1283
  currentUpdatePriority = kind === "discrete" ? DiscreteEventPriority : kind === "continuous" ? ContinuousEventPriority : DefaultEventPriority;
1128
1284
  }
@@ -1201,15 +1357,21 @@ var hostConfig = {
1201
1357
  activeBatch.push({ op: "setText", id: inst.id, text: t });
1202
1358
  }
1203
1359
  const skip = handlerPropNames[type] ?? [];
1360
+ const refs = widgetRefProps[type];
1361
+ const wire = (k) => k !== "children" && !skip.includes(k) && !(type === "label" && k === "text") && !(refs !== undefined && (k in refs));
1204
1362
  const changed = {};
1205
1363
  for (const k of Object.keys(newProps)) {
1206
- if (k === "children" || skip.includes(k))
1207
- continue;
1208
- if (type === "label" && k === "text")
1364
+ if (!wire(k))
1209
1365
  continue;
1210
- if (newProps[k] !== oldProps[k])
1366
+ if (!propsEqual(newProps[k], oldProps[k]))
1211
1367
  changed[k] = newProps[k];
1212
1368
  }
1369
+ for (const k of Object.keys(oldProps)) {
1370
+ if (k in newProps || !wire(k))
1371
+ continue;
1372
+ changed[k] = REMOVAL_VALUE[k] ?? null;
1373
+ }
1374
+ diffRefProps(inst, newProps, changed);
1213
1375
  if (Object.keys(changed).length)
1214
1376
  activeBatch.push({ op: "update", id: inst.id, props: changed });
1215
1377
  inst.props = newProps;
@@ -1770,6 +1932,8 @@ function makeRef(instance) {
1770
1932
  var pendingEvals = new Map;
1771
1933
  var pendingCookies = new Map;
1772
1934
  var pendingSessions = new Map;
1935
+ var pendingExtensions = new Map;
1936
+ var pendingActions = new Map;
1773
1937
  var seq2 = 0;
1774
1938
  function nextId(prefix) {
1775
1939
  return `${prefix}${++seq2}`;
@@ -1825,6 +1989,58 @@ function onSessionSaved(e) {
1825
1989
  pendingSessions.delete(result.id);
1826
1990
  call2.resolve(result.state ?? "");
1827
1991
  }
1992
+ function listExtensions(node) {
1993
+ const id = nextId("ext");
1994
+ return new Promise((resolve2, reject2) => {
1995
+ pendingExtensions.set(id, { resolve: resolve2, reject: reject2 });
1996
+ sendCommand(node, "listExtensions", { id });
1997
+ });
1998
+ }
1999
+ function onExtensionsList(e) {
2000
+ const result = e.data;
2001
+ const call2 = pendingExtensions.get(result.id);
2002
+ if (!call2)
2003
+ return;
2004
+ pendingExtensions.delete(result.id);
2005
+ if (result.ok)
2006
+ call2.resolve(result.extensions ?? []);
2007
+ else
2008
+ call2.reject(new Error(result.error ?? "listExtensions failed"));
2009
+ }
2010
+ function installExtension(node, path) {
2011
+ return extensionMutation(node, "installExtension", { path });
2012
+ }
2013
+ function uninstallExtension(node, extensionId) {
2014
+ return extensionMutation(node, "uninstallExtension", { extensionId });
2015
+ }
2016
+ function setExtensionEnabled(node, extensionId, enabled) {
2017
+ return extensionMutation(node, "setExtensionEnabled", { extensionId, enabled });
2018
+ }
2019
+ function extensionMutation(node, command, args) {
2020
+ const id = nextId("ext");
2021
+ return new Promise((resolve2, reject2) => {
2022
+ pendingExtensions.set(id, { resolve: resolve2, reject: reject2 });
2023
+ sendCommand(node, command, { id, ...args });
2024
+ });
2025
+ }
2026
+ function listExtensionActions(node) {
2027
+ const id = nextId("ext");
2028
+ return new Promise((resolve2, reject2) => {
2029
+ pendingActions.set(id, { resolve: resolve2, reject: reject2 });
2030
+ sendCommand(node, "listExtensionActions", { id });
2031
+ });
2032
+ }
2033
+ function onExtensionActions(e) {
2034
+ const result = e.data;
2035
+ const call2 = pendingActions.get(result.id);
2036
+ if (!call2)
2037
+ return;
2038
+ pendingActions.delete(result.id);
2039
+ if (result.ok)
2040
+ call2.resolve(result.actions ?? []);
2041
+ else
2042
+ call2.reject(new Error(result.error ?? "listExtensionActions failed"));
2043
+ }
1828
2044
  function setContextMenuItems(node, items) {
1829
2045
  sendCommand(node, "setContextMenuItems", { items });
1830
2046
  }
@@ -2194,6 +2410,7 @@ export {
2194
2410
  useContext,
2195
2411
  useCallback,
2196
2412
  use,
2413
+ uninstallExtension,
2197
2414
  system,
2198
2415
  startTransition,
2199
2416
  showToast,
@@ -2201,6 +2418,7 @@ export {
2201
2418
  showAlert,
2202
2419
  showAbout,
2203
2420
  setUnhandledErrorPolicy,
2421
+ setExtensionEnabled,
2204
2422
  setContextMenuItems,
2205
2423
  sendNativeCommand,
2206
2424
  sendCommand,
@@ -2221,11 +2439,16 @@ export {
2221
2439
  onSaveFileResult,
2222
2440
  onOpenFileResult,
2223
2441
  onJavaScriptResult,
2442
+ onExtensionsList,
2443
+ onExtensionActions,
2224
2444
  onCookiesResult,
2225
2445
  onAlertResult,
2226
2446
  notifications,
2227
2447
  moveNode,
2228
2448
  memo,
2449
+ listExtensions,
2450
+ listExtensionActions,
2451
+ installExtension,
2229
2452
  hasWidget,
2230
2453
  hasCommand,
2231
2454
  getCookies,
@@ -1,4 +1,4 @@
1
- import type { ReactNode, Ref } from "react";
1
+ import type { ReactNode, Ref, RefObject } from "react";
2
2
  export { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  export type WidgetName = "Window" | "Box" | "Label" | "Button" | "TextInput" | "TextArea" | "Checkbox" | "Radio" | "Select" | "Slider" | "ProgressBar" | "Image" | "ScrollView" | "Separator" | "Spinner" | "TabView" | "Grid" | "ListView" | "WebView" | "NativeView" | "SplitView" | "HeaderBar" | "ToolbarView" | "SearchInput" | "SourceList" | "SourceTree" | "Menubar" | "Menu" | "MenuItem" | "SettingsGroup" | "Row" | "SwitchRow" | "Clamp" | "Overlay" | "Switch" | "ToggleButton" | "SegmentedControl" | "NumberInput" | "LinkButton" | "LevelIndicator" | "ColorPicker" | "Banner" | "MenuButton" | "SplitButton" | "Popover" | "Expander" | "StatusPage" | "ToastOverlay" | "DatePicker" | "Table" | "TreeView" | "FontPicker" | "Video" | "TrayItem" | "ShareButton" | "Terminal" | "Paned" | "CommandPalette" | "Avatar" | "Badge" | "Tag" | "Kbd" | "ComboBox" | "Breadcrumb" | "Dialog" | "Sheet" | "RichText" | "ProgressCircle" | "Skeleton" | "Chart" | "CodeEditor";
4
4
  export type WidgetType = "window" | "box" | "label" | "button" | "textinput" | "textarea" | "checkbox" | "radio" | "select" | "slider" | "progressbar" | "image" | "scrollview" | "separator" | "spinner" | "tabview" | "grid" | "listview" | "webview" | "nativeview" | "splitview" | "headerbar" | "toolbarview" | "searchinput" | "sourcelist" | "sourcetree" | "menubar" | "menu" | "menuitem" | "settingsgroup" | "row" | "switchrow" | "clamp" | "overlay" | "switch" | "togglebutton" | "segmentedcontrol" | "numberinput" | "linkbutton" | "levelindicator" | "colorpicker" | "banner" | "menubutton" | "splitbutton" | "popover" | "expander" | "statuspage" | "toastoverlay" | "datepicker" | "table" | "treeview" | "fontpicker" | "video" | "trayitem" | "sharebutton" | "terminal" | "paned" | "commandpalette" | "avatar" | "badge" | "tag" | "kbd" | "combobox" | "breadcrumb" | "dialog" | "sheet" | "richtext" | "progresscircle" | "skeleton" | "chart" | "codeeditor";
@@ -992,6 +992,15 @@ export declare namespace JSX {
992
992
  onAudioStateChanged?: (e: {
993
993
  data: unknown;
994
994
  }) => void;
995
+ onExtensionsList?: (e: {
996
+ data: unknown;
997
+ }) => void;
998
+ onExtensionActions?: (e: {
999
+ data: unknown;
1000
+ }) => void;
1001
+ onChromeDialog?: (e: {
1002
+ data: unknown;
1003
+ }) => void;
995
1004
  onDragStarted?: (e: {
996
1005
  text: string;
997
1006
  }) => void;
@@ -2118,6 +2127,8 @@ export declare namespace JSX {
2118
2127
  children?: ReactNode;
2119
2128
  };
2120
2129
  popover: {
2130
+ anchor?: number;
2131
+ anchorRef?: RefObject<NdNodeRef | null>;
2121
2132
  open?: boolean;
2122
2133
  position?: "top" | "bottom" | "left" | "right";
2123
2134
  enabled?: boolean;
@@ -14,7 +14,7 @@ export interface RowJson {
14
14
  id: string | null;
15
15
  subtitle: string | null;
16
16
  }
17
- /** One tree-snapshot node. itemCount is ListView's row count (M5c-D4), null for every widget that isn't data-driven; rows is SourceList's ordered row data, null for every widget that isn't row-driven. role/enabled/focused/value are the accessibility-tree fields (M16): role is the widget's schema-declared automation role (null when the type declares none); enabled/focused/value come from a live per-node backend probe and default to true/false/null on backends without the probe. */
17
+ /** One tree-snapshot node. itemCount is ListView's row count (M5c-D4), null for every widget that isn't data-driven; rows is SourceList's ordered row data, null for every widget that isn't row-driven. role/enabled/focused/value are the accessibility-tree fields (M16): role is the widget's schema-declared automation role (null when the type declares none); enabled/focused/value come from a live per-node backend probe and default to true/false/null on backends without the probe. checked/selected/expanded/placeholder/label/options come from the same probe and are null on every node the field does not apply to (a Label has no checked state), so a locator can ask isChecked/isSelected/isExpanded of any node without first knowing its kind. */
18
18
  export interface JsonNode {
19
19
  ref: number;
20
20
  type: string;
@@ -29,6 +29,12 @@ export interface JsonNode {
29
29
  enabled: boolean;
30
30
  focused: boolean;
31
31
  value: unknown | null;
32
+ checked: boolean | null;
33
+ selected: boolean | null;
34
+ expanded: boolean | null;
35
+ placeholder: string | null;
36
+ label: string | null;
37
+ options: string[] | null;
32
38
  }
33
39
  export interface GetTreeResult {
34
40
  coordinateSpace: string;
@@ -82,7 +88,7 @@ export interface ResolveResult {
82
88
  refs: number[];
83
89
  count: number;
84
90
  }
85
- /** key/main/visible/title come from the live windowState probe on the Window node's handle; tabGroup is the create-time prop (null for plain windows). */
91
+ /** key/main/visible/title come from the live windowState probe on the Window node's handle; tabGroup is the create-time prop (null for plain windows). geometry is the window's frame in the same logical, top-left-origin units node Geometry uses (w/h are width/height), null on a backend whose probe does not report it. */
86
92
  export interface WindowInfo {
87
93
  ref: number;
88
94
  title: string | null;
@@ -90,6 +96,7 @@ export interface WindowInfo {
90
96
  main: boolean;
91
97
  visible: boolean;
92
98
  tabGroup: string | null;
99
+ geometry: Geometry | null;
93
100
  }
94
101
  export interface WindowsResult {
95
102
  windows: WindowInfo[];
@@ -109,6 +116,14 @@ export interface DragResult {
109
116
  export interface KeysResult {
110
117
  dispatched: boolean;
111
118
  }
119
+ export interface FocusResult {
120
+ ok: boolean;
121
+ }
122
+ /** scrolled is false when the target has no scrollable ancestor (nothing to do), which is a success, not an error. */
123
+ export interface ScrollIntoViewResult {
124
+ ok: boolean;
125
+ scrolled: boolean;
126
+ }
112
127
  /** Live page state read off the engine on the UI thread (WebKitGTK's uri/title/is-loading/can-go-back/can-go-forward, WKWebView's url/title/isLoading/canGoBack/canGoForward) — no page JavaScript, no app cooperation. url/title are null before the first commit. */
113
128
  export interface WebViewInfo {
114
129
  ref: number;
@@ -187,6 +202,33 @@ export interface HoverParams {
187
202
  testId?: string;
188
203
  window?: number;
189
204
  }
205
+ /** Moves keyboard focus to the target, the same path the widget-level `focus` command takes (first responder on macOS, gtk_widget_grab_focus on GTK), so a following `keys`/`type` lands there. Target by exactly one of ref / testId. After it returns, waitFor {testId, state: "focused"} holds. */
206
+ export interface FocusParams {
207
+ ref?: number;
208
+ testId?: string;
209
+ window?: number;
210
+ }
211
+ /** Scrolls the target's nearest scrollable ancestor until the target is inside the viewport, so an off-screen node becomes actionable. A target with no scrollable ancestor answers {ok: true, scrolled: false} rather than an error. Target by exactly one of ref / testId. */
212
+ export interface ScrollIntoViewParams {
213
+ ref?: number;
214
+ testId?: string;
215
+ window?: number;
216
+ }
217
+ /** In-process render of ONE node's widget to a PNG, same result shape as screenshot. The image is the node's own surface, so its pixel dimensions come from the same handle Geometry measures rather than from the whole window at an unknown backing scale. `path` is an absolute path; absent, the host writes beside the automation socket and answers where. Target by exactly one of ref / testId. */
218
+ export interface SnapshotNodeParams {
219
+ ref?: number;
220
+ testId?: string;
221
+ window?: number;
222
+ path?: string;
223
+ }
224
+ /** Moves and/or resizes a window; omitted components keep their current value. Coordinates are logical points with a top-left origin, matching WindowInfo.geometry. `window` names a Window node ref (default: the root window). Answers that window's updated WindowInfo. GTK sizes the window but cannot position it (client-side placement is not a Wayland capability), so x/y are ignored there. */
225
+ export interface SetWindowFrameParams {
226
+ window?: number;
227
+ x?: number;
228
+ y?: number;
229
+ width?: number;
230
+ height?: number;
231
+ }
190
232
  /** Resolves a testID to the single ACTIONABLE instance. Candidates are ranked: actionable (the checkActionable predicate passes) before not; then key/front window before background. `refs` is every match in tree order, `ref` the winner (null when none is actionable and `actionable` is true). */
191
233
  export interface ResolveParams {
192
234
  testId: string;
@@ -276,6 +318,22 @@ export interface RpcMethods {
276
318
  params: HoverParams;
277
319
  result: ClickResult;
278
320
  };
321
+ focus: {
322
+ params: FocusParams;
323
+ result: FocusResult;
324
+ };
325
+ scrollIntoView: {
326
+ params: ScrollIntoViewParams;
327
+ result: ScrollIntoViewResult;
328
+ };
329
+ snapshotNode: {
330
+ params: SnapshotNodeParams;
331
+ result: ScreenshotResult;
332
+ };
333
+ setWindowFrame: {
334
+ params: SetWindowFrameParams;
335
+ result: WindowInfo;
336
+ };
279
337
  resolve: {
280
338
  params: ResolveParams;
281
339
  result: ResolveResult;
@@ -11,6 +11,11 @@ export interface WidgetEvent {
11
11
  payload: "none" | "text" | "checked" | "value" | "index" | "indexes" | "data" | "position" | "dragPoint";
12
12
  }
13
13
  export declare const widgetEvents: Record<string, WidgetEvent[]>;
14
+ /** JSX ref-prop name -> the wire prop that carries the target's node id
15
+ * (schema `refProp`). The reconciler reads `.current.id` off the ref and
16
+ * sends the number, so an app never handles a node id itself; only the
17
+ * widgets listed here have one. */
18
+ export declare const widgetRefProps: Record<string, Record<string, string>>;
14
19
  export declare const handlerPropNames: Record<string, string[]>;
15
20
  /** Imperative commands each widget accepts via the widgetCommand NDP frame
16
21
  * (M14) — the runtime validation table behind sendCommand(). */
@@ -24,7 +29,7 @@ export type WidgetCommandNames = {
24
29
  "checkbox": "focus";
25
30
  "radio": "focus";
26
31
  "select": "focus";
27
- "webview": "goBack" | "goForward" | "reload" | "stop" | "executeJavaScript" | "setZoom" | "setUserAgent" | "openDevTools" | "addUserScript" | "removeUserScript" | "clearUserScripts" | "registerScriptMessage" | "unregisterScriptMessage" | "respondScheme" | "getCookies" | "setCookie" | "deleteCookie" | "findStart" | "findNext" | "findPrevious" | "findStop" | "saveSession" | "restoreSession" | "setMuted" | "setContextMenuItems" | "focus";
32
+ "webview": "goBack" | "goForward" | "reload" | "stop" | "executeJavaScript" | "setZoom" | "setUserAgent" | "openDevTools" | "addUserScript" | "removeUserScript" | "clearUserScripts" | "registerScriptMessage" | "unregisterScriptMessage" | "respondScheme" | "getCookies" | "setCookie" | "deleteCookie" | "findStart" | "findNext" | "findPrevious" | "findStop" | "saveSession" | "restoreSession" | "setMuted" | "setContextMenuItems" | "listExtensions" | "listExtensionActions" | "installExtension" | "uninstallExtension" | "setExtensionEnabled" | "focus";
28
33
  "searchinput": "focus";
29
34
  "switch": "focus";
30
35
  "numberinput": "focus";
@@ -7,8 +7,8 @@ export type { SpacingScale } from "./metrics.ts";
7
7
  export { getAppDataDir, ensureAppDataDir } from "./paths.ts";
8
8
  export { defineNativeComponent } from "./native-component.ts";
9
9
  export type { NativeComponentOptions, NativeComponentProps, NativeComponentRef } from "./native-component.ts";
10
- export { executeJavaScript, onJavaScriptResult, getCookies, onCookiesResult, saveSession, onSessionSaved, setContextMenuItems, } from "./webview.ts";
11
- export type { Cookie, ContextMenuContext, ContextMenuItem, ContextMenuItemClick } from "./webview.ts";
10
+ export { executeJavaScript, onJavaScriptResult, getCookies, onCookiesResult, saveSession, onSessionSaved, setContextMenuItems, listExtensions, onExtensionsList, listExtensionActions, onExtensionActions, installExtension, uninstallExtension, setExtensionEnabled, } from "./webview.ts";
11
+ export type { Cookie, ContextMenuContext, ContextMenuItem, ContextMenuItemClick, ExtensionAction, InstalledExtension, } from "./webview.ts";
12
12
  export { showAlert, openFile, saveFile, showAbout, showTabOverview, onAlertResult, onOpenFileResult, onSaveFileResult, } from "./dialogs.ts";
13
13
  export type { DialogButton, ShowAlertOptions, AlertResult, OpenFileOptions as WindowOpenFileOptions, OpenFileResult as WindowOpenFileResult, SaveFileOptions as WindowSaveFileOptions, SaveFileResult as WindowSaveFileResult, ShowAboutOptions, } from "./dialogs.ts";
14
14
  export { showToast, dismissToast, onToastButtonClicked, onToastDismissed } from "./toast.ts";
@@ -22,6 +22,40 @@ export declare function saveSession(node: NdNodeRef<"webview">): Promise<string>
22
22
  export declare function onSessionSaved(e: {
23
23
  data: unknown;
24
24
  }): void;
25
+ export interface InstalledExtension {
26
+ id: string;
27
+ name: string;
28
+ version: string;
29
+ enabled: boolean;
30
+ /** A `data:` URI, ready for an `<image>` src. Empty when the extension has no icon. */
31
+ iconUrl: string;
32
+ /** `chrome-extension://…` options page, or "" when the extension declares none. */
33
+ optionsUrl: string;
34
+ }
35
+ export declare function listExtensions(node: NdNodeRef<"webview">): Promise<InstalledExtension[]>;
36
+ export declare function onExtensionsList(e: {
37
+ data: unknown;
38
+ }): void;
39
+ export declare function installExtension(node: NdNodeRef<"webview">, path: string): Promise<InstalledExtension[]>;
40
+ export declare function uninstallExtension(node: NdNodeRef<"webview">, extensionId: string): Promise<InstalledExtension[]>;
41
+ export declare function setExtensionEnabled(node: NdNodeRef<"webview">, extensionId: string, enabled: boolean): Promise<InstalledExtension[]>;
42
+ export interface ExtensionAction {
43
+ id: string;
44
+ name: string;
45
+ enabled: boolean;
46
+ /** `action.default_title`, falling back to the extension's name. */
47
+ title: string;
48
+ /** `chrome-extension://…` icon from `action.default_icon`, or the registry icon. */
49
+ iconUrl: string;
50
+ /** `chrome-extension://…` popup page, or "" for an action that fires `onClicked`. */
51
+ popupUrl: string;
52
+ /** Always "": Chromium answers `chrome.action.getBadgeText` to the extension alone. */
53
+ badgeText: string;
54
+ }
55
+ export declare function listExtensionActions(node: NdNodeRef<"webview">): Promise<ExtensionAction[]>;
56
+ export declare function onExtensionActions(e: {
57
+ data: unknown;
58
+ }): void;
25
59
  export type ContextMenuContext = "all" | "page" | "link" | "image" | "selection" | "editable";
26
60
  export interface ContextMenuItem {
27
61
  id?: string;
@@ -2,6 +2,9 @@ import type { Runtime, Op, CommitBatch, EventMsg } from "../packages/react/src/g
2
2
  export declare class Ndp {
3
3
  private socket;
4
4
  private inbox;
5
+ private inboxView;
6
+ private inboxHead;
7
+ private inboxTail;
5
8
  private eventCb;
6
9
  private helloAckResolve;
7
10
  private outbox;
@@ -22,6 +25,8 @@ export declare class Ndp {
22
25
  get hostCommands(): Set<string> | null;
23
26
  static connect(): Promise<Ndp>;
24
27
  private pump;
28
+ private reserveInbox;
29
+ private compactInbox;
25
30
  private onData;
26
31
  private dispatch;
27
32
  private send;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativedesktop/react",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/FormalSnake/NativeDesktop#readme",