@dimina-kit/electron-deck 0.1.0-dev.20260610082053

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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/dist/client/index.d.ts +20 -0
  4. package/dist/client/index.d.ts.map +1 -0
  5. package/dist/client/index.js +310 -0
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/client/layout-client.d.ts +51 -0
  8. package/dist/client/layout-client.d.ts.map +1 -0
  9. package/dist/electron-deck-DfFKeFEC.js +2295 -0
  10. package/dist/electron-deck-DfFKeFEC.js.map +1 -0
  11. package/dist/electron-deck.d.ts +57 -0
  12. package/dist/electron-deck.d.ts.map +1 -0
  13. package/dist/errors-NE2ig7L5.js +53 -0
  14. package/dist/errors-NE2ig7L5.js.map +1 -0
  15. package/dist/errors.d.ts +33 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/events.d.ts +14 -0
  18. package/dist/events.d.ts.map +1 -0
  19. package/dist/host/capability.d.ts +50 -0
  20. package/dist/host/capability.d.ts.map +1 -0
  21. package/dist/host/control-bus.d.ts +92 -0
  22. package/dist/host/control-bus.d.ts.map +1 -0
  23. package/dist/host/index.d.ts +42 -0
  24. package/dist/host/index.d.ts.map +1 -0
  25. package/dist/host/index.js +2 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +3 -0
  29. package/dist/internal/deck-app.d.ts +415 -0
  30. package/dist/internal/deck-app.d.ts.map +1 -0
  31. package/dist/internal/electron-types.d.ts +131 -0
  32. package/dist/internal/electron-types.d.ts.map +1 -0
  33. package/dist/internal/event-bus.d.ts +29 -0
  34. package/dist/internal/event-bus.d.ts.map +1 -0
  35. package/dist/internal/ipc-registry-memory.d.ts +25 -0
  36. package/dist/internal/ipc-registry-memory.d.ts.map +1 -0
  37. package/dist/internal/lifecycle-manager.d.ts +28 -0
  38. package/dist/internal/lifecycle-manager.d.ts.map +1 -0
  39. package/dist/internal/resource-registry.d.ts +9 -0
  40. package/dist/internal/resource-registry.d.ts.map +1 -0
  41. package/dist/internal/trust-set.d.ts +53 -0
  42. package/dist/internal/trust-set.d.ts.map +1 -0
  43. package/dist/internal/wire-transport.d.ts +159 -0
  44. package/dist/internal/wire-transport.d.ts.map +1 -0
  45. package/dist/main/compositor.d.ts +123 -0
  46. package/dist/main/compositor.d.ts.map +1 -0
  47. package/dist/main/connection.d.ts +37 -0
  48. package/dist/main/connection.d.ts.map +1 -0
  49. package/dist/main/debug-tap.d.ts +55 -0
  50. package/dist/main/debug-tap.d.ts.map +1 -0
  51. package/dist/main/disposable.d.ts +18 -0
  52. package/dist/main/disposable.d.ts.map +1 -0
  53. package/dist/main/index.d.ts +16 -0
  54. package/dist/main/index.d.ts.map +1 -0
  55. package/dist/main/index.js +152 -0
  56. package/dist/main/index.js.map +1 -0
  57. package/dist/main/logger.d.ts +15 -0
  58. package/dist/main/logger.d.ts.map +1 -0
  59. package/dist/main/scope.d.ts +52 -0
  60. package/dist/main/scope.d.ts.map +1 -0
  61. package/dist/main/view-handle.d.ts +131 -0
  62. package/dist/main/view-handle.d.ts.map +1 -0
  63. package/dist/preload/index.cjs +105 -0
  64. package/dist/preload/index.cjs.map +1 -0
  65. package/dist/preload/index.d.ts +43 -0
  66. package/dist/preload/index.d.ts.map +1 -0
  67. package/dist/preload/index.js +80 -0
  68. package/dist/preload/index.js.map +1 -0
  69. package/dist/protocol-BJqo-vP2.js +23 -0
  70. package/dist/protocol-BJqo-vP2.js.map +1 -0
  71. package/dist/shared/protocol.d.ts +76 -0
  72. package/dist/shared/protocol.d.ts.map +1 -0
  73. package/dist/types.d.ts +504 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/view-handle-CR-yWNfr.js +701 -0
  76. package/dist/view-handle-CR-yWNfr.js.map +1 -0
  77. package/package.json +86 -0
@@ -0,0 +1,701 @@
1
+ //#region src/main/disposable.ts
2
+ function toDisposable(fn) {
3
+ let done = false;
4
+ return { dispose() {
5
+ if (done) return;
6
+ done = true;
7
+ return fn();
8
+ } };
9
+ }
10
+ var DisposableRegistry = class {
11
+ entries = [];
12
+ _disposed = false;
13
+ /**
14
+ * Number of live entries. A wrapper's `dispose` splices its entry out and
15
+ * `disposeAll` clears the array, so `entries.length` is the live count.
16
+ */
17
+ get size() {
18
+ return this.entries.length;
19
+ }
20
+ add(d) {
21
+ if (this._disposed) throw new Error("cannot add to disposed registry");
22
+ const fn = typeof d === "function" ? d : () => d.dispose();
23
+ const entry = {
24
+ fn,
25
+ released: false
26
+ };
27
+ this.entries.push(entry);
28
+ return { dispose: () => {
29
+ if (entry.released) return;
30
+ entry.released = true;
31
+ const i = this.entries.indexOf(entry);
32
+ if (i >= 0) this.entries.splice(i, 1);
33
+ return fn();
34
+ } };
35
+ }
36
+ async disposeAll() {
37
+ if (this._disposed) return;
38
+ this._disposed = true;
39
+ const items = this.entries.slice().reverse();
40
+ this.entries = [];
41
+ const errors = [];
42
+ for (const entry of items) {
43
+ if (entry.released) continue;
44
+ entry.released = true;
45
+ try {
46
+ await entry.fn();
47
+ } catch (e) {
48
+ errors.push(e);
49
+ }
50
+ }
51
+ if (errors.length > 0) throw new AggregateError(errors, "DisposableRegistry encountered errors during disposeAll");
52
+ }
53
+ dispose() {
54
+ return this.disposeAll();
55
+ }
56
+ };
57
+ //#endregion
58
+ //#region src/main/logger.ts
59
+ var LEVEL_PRIORITY = {
60
+ debug: 0,
61
+ info: 1,
62
+ warn: 2,
63
+ error: 3
64
+ };
65
+ var LEVEL_PREFIX = {
66
+ debug: "[DEBUG]",
67
+ info: "[INFO]",
68
+ warn: "[WARN]",
69
+ error: "[ERROR]"
70
+ };
71
+ var currentLevel = "debug";
72
+ function shouldLog(level) {
73
+ return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[currentLevel];
74
+ }
75
+ function formatMessage(level, tag, message) {
76
+ return `${(/* @__PURE__ */ new Date()).toISOString()} ${LEVEL_PREFIX[level]} [${tag}] ${message}`;
77
+ }
78
+ /**
79
+ * Create a tagged logger for a specific module.
80
+ * All output goes to console with structured prefix.
81
+ */
82
+ function createLogger(tag) {
83
+ return {
84
+ debug(message, ...args) {
85
+ if (shouldLog("debug")) console.debug(formatMessage("debug", tag, message), ...args);
86
+ },
87
+ info(message, ...args) {
88
+ if (shouldLog("info")) console.info(formatMessage("info", tag, message), ...args);
89
+ },
90
+ warn(message, ...args) {
91
+ if (shouldLog("warn")) console.warn(formatMessage("warn", tag, message), ...args);
92
+ },
93
+ error(message, ...args) {
94
+ if (shouldLog("error")) console.error(formatMessage("error", tag, message), ...args);
95
+ }
96
+ };
97
+ }
98
+ function setLogLevel(level) {
99
+ currentLevel = level;
100
+ }
101
+ //#endregion
102
+ //#region src/main/scope.ts
103
+ /**
104
+ * `Scope` — engine-agnostic nested-lifetime primitive, generalizing the
105
+ * Connection/Disposable semantics in this package (foundation.md §4).
106
+ *
107
+ * A `Scope` owns a single "lifetime segment". Resources bound via `own()` and
108
+ * sub-scopes created via `child()` are released together — both on `reset()`
109
+ * (soft reuse: dispose the segment, open a fresh one, stay alive) and `close()`
110
+ * (terminal: dispose everything, die). Teardown is LIFO and, crucially,
111
+ * `reset()`/`close()` are COMPLETION FENCES: their Promise resolves (and the
112
+ * `'reset'`/`'closed'` listeners fire) only AFTER the underlying async
113
+ * disposeAll has fully finished — unlike `connection.ts`, which fires the
114
+ * teardown and forgets (`void ...disposeAll()`).
115
+ *
116
+ * Cross-layer LIFO: a scope's teardown disposes its child sub-scopes (deepest
117
+ * first, recursively) BEFORE its own directly-owned resources, so a grandchild
118
+ * tears down before a child, which tears down before the root.
119
+ */
120
+ var log = createLogger("scope");
121
+ function asInternal(s) {
122
+ return s;
123
+ }
124
+ /** True if `maybeAncestor` is `node` or an ancestor of it (walking parents). */
125
+ function isSelfOrAncestor(maybeAncestor, node) {
126
+ let cur = node;
127
+ while (cur) {
128
+ if (cur === maybeAncestor) return true;
129
+ cur = cur.__parent().parent;
130
+ }
131
+ return false;
132
+ }
133
+ var NOOP_DISPOSABLE = { dispose() {} };
134
+ function toDispose(d) {
135
+ return typeof d === "function" ? d : () => d.dispose();
136
+ }
137
+ /**
138
+ * Dispose a resource handed to a dead scope's `own()` — immediately, exactly
139
+ * once, never delegating to a disposed segment (that would throw). Both sync
140
+ * throws and async rejections are caught/logged so a late teardown can never
141
+ * escape as an unhandledRejection in the main process.
142
+ */
143
+ function disposeLate(d) {
144
+ try {
145
+ const r = toDispose(d)();
146
+ if (r && typeof r.then === "function") r.catch((e) => log.error("late own() async disposer rejected", e));
147
+ } catch (e) {
148
+ log.error("late own() resource disposer threw", e);
149
+ }
150
+ }
151
+ function newSegment() {
152
+ return {
153
+ children: [],
154
+ resources: new DisposableRegistry()
155
+ };
156
+ }
157
+ async function disposeSegment(segment) {
158
+ const children = segment.children.slice().reverse();
159
+ segment.children = [];
160
+ for (const child of children) try {
161
+ await child.close();
162
+ } catch (e) {
163
+ log.error("child scope close threw during segment teardown", e);
164
+ }
165
+ await segment.resources.disposeAll();
166
+ }
167
+ function createScope() {
168
+ let segment = newSegment();
169
+ let alive = true;
170
+ let inFlight = null;
171
+ let inFlightKind = null;
172
+ const resetListeners = /* @__PURE__ */ new Set();
173
+ const closedListeners = /* @__PURE__ */ new Set();
174
+ let parent = null;
175
+ let owningSegment = null;
176
+ const childRemovers = /* @__PURE__ */ new Map();
177
+ function emit(ev) {
178
+ const set = ev === "reset" ? resetListeners : closedListeners;
179
+ for (const cb of [...set]) try {
180
+ cb();
181
+ } catch (e) {
182
+ log.error(`listener for "${ev}" threw`, e);
183
+ }
184
+ }
185
+ function runReset() {
186
+ const old = segment;
187
+ segment = newSegment();
188
+ inFlightKind = "reset";
189
+ const p = (async () => {
190
+ try {
191
+ await disposeSegment(old);
192
+ } finally {
193
+ if (inFlightKind === "reset") {
194
+ inFlight = null;
195
+ inFlightKind = null;
196
+ emit("reset");
197
+ }
198
+ }
199
+ })();
200
+ inFlight = p;
201
+ return p;
202
+ }
203
+ function runClose() {
204
+ alive = false;
205
+ const old = segment;
206
+ segment = newSegment();
207
+ inFlightKind = "close";
208
+ const p = (async () => {
209
+ try {
210
+ await disposeSegment(old);
211
+ } finally {
212
+ inFlight = null;
213
+ inFlightKind = null;
214
+ emit("closed");
215
+ }
216
+ })();
217
+ inFlight = p;
218
+ return p;
219
+ }
220
+ function attachChild(child, seg) {
221
+ seg.children.push(child);
222
+ const remover = child.on("closed", () => {
223
+ const i = seg.children.indexOf(child);
224
+ if (i >= 0) seg.children.splice(i, 1);
225
+ childRemovers.delete(child);
226
+ });
227
+ childRemovers.set(child, remover);
228
+ child.__setParent(scope, seg);
229
+ }
230
+ function detachChild(child, seg) {
231
+ const i = seg.children.indexOf(child);
232
+ if (i >= 0) seg.children.splice(i, 1);
233
+ const remover = childRemovers.get(child);
234
+ if (remover) {
235
+ remover.dispose();
236
+ childRemovers.delete(child);
237
+ }
238
+ child.__setParent(null, null);
239
+ }
240
+ const scope = {
241
+ get alive() {
242
+ return alive;
243
+ },
244
+ __currentSegment() {
245
+ return segment;
246
+ },
247
+ __inFlight() {
248
+ return inFlight;
249
+ },
250
+ __parent() {
251
+ return {
252
+ parent,
253
+ owningSegment
254
+ };
255
+ },
256
+ __attachChild(child, seg) {
257
+ attachChild(child, seg);
258
+ },
259
+ __detachChild(child, seg) {
260
+ detachChild(child, seg);
261
+ },
262
+ __setParent(p, seg) {
263
+ parent = p;
264
+ owningSegment = seg;
265
+ },
266
+ own(d) {
267
+ if (!alive) {
268
+ disposeLate(d);
269
+ return NOOP_DISPOSABLE;
270
+ }
271
+ return segment.resources.add(d);
272
+ },
273
+ child() {
274
+ if (!alive) {
275
+ const dead = createScope();
276
+ dead.close();
277
+ return dead;
278
+ }
279
+ const sub = asInternal(createScope());
280
+ attachChild(sub, segment);
281
+ return sub;
282
+ },
283
+ reset() {
284
+ if (!alive) return inFlight ?? Promise.resolve();
285
+ if (inFlight) return inFlight;
286
+ return runReset();
287
+ },
288
+ close() {
289
+ if (!alive) return inFlight ?? Promise.resolve();
290
+ if (inFlight && inFlightKind === "close") return inFlight;
291
+ if (inFlight && inFlightKind === "reset") {
292
+ alive = false;
293
+ const afterReset = inFlight;
294
+ inFlightKind = "close";
295
+ const upgraded = (async () => {
296
+ await afterReset.catch(() => {});
297
+ const leftover = segment;
298
+ segment = newSegment();
299
+ try {
300
+ await disposeSegment(leftover);
301
+ } finally {
302
+ inFlight = null;
303
+ inFlightKind = null;
304
+ emit("closed");
305
+ }
306
+ })();
307
+ inFlight = upgraded;
308
+ return upgraded;
309
+ }
310
+ return runClose();
311
+ },
312
+ on(ev, cb) {
313
+ const set = ev === "reset" ? resetListeners : closedListeners;
314
+ set.add(cb);
315
+ let removed = false;
316
+ return { dispose() {
317
+ if (removed) return;
318
+ removed = true;
319
+ set.delete(cb);
320
+ } };
321
+ },
322
+ async adopt(childPublic, newParentPublic) {
323
+ const child = asInternal(childPublic);
324
+ const newParent = asInternal(newParentPublic);
325
+ const self = scope;
326
+ while (true) {
327
+ const f = self.__inFlight() ?? newParent.__inFlight();
328
+ if (!f) break;
329
+ await f.catch(() => {});
330
+ }
331
+ if (!self.alive) throw new Error("adopt: donor scope is not alive");
332
+ if (!newParent.alive) throw new Error("adopt: newParent scope is not alive");
333
+ const fromSegment = self.__currentSegment();
334
+ const toSegment = newParent.__currentSegment();
335
+ if (child.__parent().parent !== self || fromSegment.children.indexOf(child) < 0) throw new Error("adopt: child is not a direct child of this scope current segment");
336
+ if (isSelfOrAncestor(child, newParent)) throw new Error("adopt: would create a cycle (newParent is child or a descendant of it)");
337
+ self.__detachChild(child, fromSegment);
338
+ newParent.__attachChild(child, toSegment);
339
+ }
340
+ };
341
+ return scope;
342
+ }
343
+ //#endregion
344
+ //#region src/main/compositor.ts
345
+ /**
346
+ * Typed failure thrown by {@link Compositor.commit}. `kind` distinguishes a
347
+ * preflight refusal (`host-destroyed`, native byte-for-byte pre-commit,
348
+ * `applied:false`) from a mid-apply native throw (`apply-failed`,
349
+ * `applied:'partial'`); for `apply-failed`, `recovered` reports whether the
350
+ * best-effort rollback restored the pre-apply snapshot order (`false` ⇒ native
351
+ * is untrusted and the host must be treated as dead).
352
+ */
353
+ var CommitError = class extends Error {
354
+ kind;
355
+ applied;
356
+ recovered;
357
+ constructor(args) {
358
+ super(args.message ?? `commit failed: ${args.kind}`);
359
+ this.name = "CommitError";
360
+ this.kind = args.kind;
361
+ this.applied = args.applied;
362
+ if (args.recovered !== void 0) this.recovered = args.recovered;
363
+ }
364
+ };
365
+ var DEFAULT_ZONE = 0;
366
+ /**
367
+ * Smallest representable gap between fractional keys before we renumber. Once a
368
+ * midpoint would round to (or below) this distance from a neighbor, the zone is
369
+ * rebalanced rather than risk key collision / loss of ordering.
370
+ */
371
+ var MIN_KEY_GAP = 1e-9;
372
+ function createCompositor(host) {
373
+ const views = /* @__PURE__ */ new Map();
374
+ let mountSeqCounter = 0;
375
+ /** Views in a zone, sorted by the in-zone order `(orderKey, viewId)`. */
376
+ function zoneSorted(zone) {
377
+ const out = [];
378
+ for (const v of views.values()) if (v.zone === zone) out.push(v);
379
+ out.sort(compareInZone);
380
+ return out;
381
+ }
382
+ function compareInZone(a, b) {
383
+ if (a.orderKey !== b.orderKey) return a.orderKey - b.orderKey;
384
+ return a.ref.id < b.ref.id ? -1 : a.ref.id > b.ref.id ? 1 : 0;
385
+ }
386
+ /** Full target order: zones ascending (low renders first/bottom), then
387
+ * in-zone order. */
388
+ function targetOrder() {
389
+ const all = [...views.values()];
390
+ all.sort((a, b) => {
391
+ if (a.zone !== b.zone) return a.zone - b.zone;
392
+ return compareInZone(a, b);
393
+ });
394
+ return all;
395
+ }
396
+ /** Next order key that lands a view at the TOP (end) of its zone. */
397
+ function topKey(zone) {
398
+ const z = zoneSorted(zone);
399
+ const last = z[z.length - 1];
400
+ return last === void 0 ? 0 : last.orderKey + 1;
401
+ }
402
+ /**
403
+ * Renumber a zone to evenly-spaced integer keys, preserving the current
404
+ * relative order exactly. Invisible: the visible order is unchanged, so a
405
+ * commit after a rebalance produces no extra host churn.
406
+ */
407
+ function rebalanceZone(zone) {
408
+ zoneSorted(zone).forEach((v, i) => {
409
+ v.orderKey = i;
410
+ });
411
+ }
412
+ /**
413
+ * Compute the fractional key that places a view immediately before `anchor`
414
+ * within its zone (the midpoint between `anchor` and its predecessor), or at
415
+ * the zone top when `anchor` is null. If the midpoint would exhaust precision,
416
+ * rebalance the zone and recompute on the fresh integer keys.
417
+ */
418
+ function keyBefore(zone, anchor, movingId) {
419
+ if (anchor === null) return topKey(zone);
420
+ const midpointBefore = (anchorId) => {
421
+ const z = zoneSorted(zone).filter((v) => v.ref.id !== movingId);
422
+ const idx = z.findIndex((v) => v.ref.id === anchorId);
423
+ const at = z[idx];
424
+ if (idx < 0 || at === void 0) return null;
425
+ const hiKey = at.orderKey;
426
+ const prev = z[idx - 1];
427
+ const loKey = prev !== void 0 ? prev.orderKey : hiKey - 1;
428
+ const mid = (loKey + hiKey) / 2;
429
+ if (mid <= loKey + MIN_KEY_GAP || mid >= hiKey - MIN_KEY_GAP) return null;
430
+ return mid;
431
+ };
432
+ const first = midpointBefore(anchor.ref.id);
433
+ if (first === null && zoneSorted(zone).some((v) => v.ref.id === anchor.ref.id)) {
434
+ rebalanceZone(zone);
435
+ const retry = midpointBefore(anchor.ref.id);
436
+ if (retry !== null) return retry;
437
+ }
438
+ return first ?? topKey(zone);
439
+ }
440
+ const compositor = {
441
+ mount(view, opts) {
442
+ const zone = opts?.zone ?? DEFAULT_ZONE;
443
+ if (views.get(view.id)) return;
444
+ views.set(view.id, {
445
+ ref: view,
446
+ zone,
447
+ orderKey: topKey(zone),
448
+ mountSeq: ++mountSeqCounter
449
+ });
450
+ },
451
+ unmount(viewId) {
452
+ views.delete(viewId);
453
+ },
454
+ reorder(viewId, opts) {
455
+ const mv = views.get(viewId);
456
+ if (!mv) throw new Error(`reorder: view "${viewId}" is not mounted`);
457
+ const before = opts.before;
458
+ const targetZone = opts.zone ?? mv.zone;
459
+ if (before === void 0 || before === null) {
460
+ mv.zone = targetZone;
461
+ mv.orderKey = topKey(targetZone);
462
+ return;
463
+ }
464
+ const anchor = views.get(before);
465
+ if (!anchor) throw new Error(`reorder: before anchor "${before}" is not mounted`);
466
+ if (opts.zone !== void 0 && anchor.zone !== opts.zone) throw new Error(`reorder: before anchor "${before}" is in zone ${anchor.zone}, which conflicts with the requested zone ${opts.zone}`);
467
+ mv.zone = anchor.zone;
468
+ mv.orderKey = keyBefore(anchor.zone, anchor, viewId);
469
+ },
470
+ commit() {
471
+ const target = targetOrder();
472
+ const targetIds = target.map((v) => v.ref.id);
473
+ const current = host.children().map((v) => v.id);
474
+ const targetSet = new Set(targetIds);
475
+ const currentSet = new Set(current);
476
+ const removals = [];
477
+ const additions = [];
478
+ const shared = targetIds.filter((id) => currentSet.has(id));
479
+ const currentIndexOf = /* @__PURE__ */ new Map();
480
+ current.forEach((id, i) => currentIndexOf.set(id, i));
481
+ const keepIds = /* @__PURE__ */ new Set();
482
+ let prevPos = -1;
483
+ for (const id of shared) {
484
+ const pos = currentIndexOf.get(id);
485
+ if (pos > prevPos) {
486
+ keepIds.add(id);
487
+ prevPos = pos;
488
+ } else break;
489
+ }
490
+ for (const v of host.children()) if (!targetSet.has(v.id)) removals.push(v);
491
+ target.forEach((v, i) => {
492
+ const isNew = !currentSet.has(v.ref.id);
493
+ const mustMove = currentSet.has(v.ref.id) && !keepIds.has(v.ref.id);
494
+ if (isNew || mustMove) additions.push({
495
+ ref: v.ref,
496
+ atIndex: i
497
+ });
498
+ });
499
+ if (removals.length === 0 && additions.length === 0) return;
500
+ if (host.isDestroyed) {
501
+ if (additions.length === 0) return;
502
+ throw new CommitError({
503
+ kind: "host-destroyed",
504
+ applied: false,
505
+ message: "commit: contentView is destroyed"
506
+ });
507
+ }
508
+ const snapshot = host.children().slice();
509
+ additions.sort((a, b) => a.atIndex - b.atIndex);
510
+ try {
511
+ for (const r of removals) host.removeChildView(r);
512
+ for (const a of additions) host.addChildView(a.ref);
513
+ } catch (applyErr) {
514
+ let recovered;
515
+ try {
516
+ for (const v of host.children().slice()) host.removeChildView(v);
517
+ for (const v of snapshot) host.addChildView(v);
518
+ recovered = true;
519
+ } catch {
520
+ recovered = false;
521
+ }
522
+ throw new CommitError({
523
+ kind: "apply-failed",
524
+ applied: "partial",
525
+ recovered,
526
+ message: `commit apply failed: ${applyErr?.message ?? applyErr}`
527
+ });
528
+ }
529
+ },
530
+ detachAll() {
531
+ for (const id of [...views.keys()]) views.delete(id);
532
+ compositor.commit();
533
+ }
534
+ };
535
+ return compositor;
536
+ }
537
+ //#endregion
538
+ //#region src/main/view-handle.ts
539
+ function createViewHandle(deps) {
540
+ const { nativeView } = deps;
541
+ const ref = nativeView.ref;
542
+ let current = null;
543
+ let currentZone;
544
+ let viewScope = null;
545
+ let owningWindowScope = null;
546
+ let active = false;
547
+ let visibleBounds = null;
548
+ let migrating = false;
549
+ let lockChain = Promise.resolve();
550
+ function withLock(fn) {
551
+ const run = lockChain.then(fn, fn);
552
+ lockChain = run.then(() => {}, () => {});
553
+ return run;
554
+ }
555
+ function ensureMounted() {
556
+ if (!current) return;
557
+ current.compositor.mount(ref, { zone: currentZone });
558
+ current.compositor.commit();
559
+ }
560
+ async function doDispose() {
561
+ if (!viewScope) return;
562
+ await viewScope.close();
563
+ }
564
+ async function doMove(dest, opts) {
565
+ if (!active || !current || !viewScope) throw new Error("moveTo: the view is not currently placed (disposed or never placed)");
566
+ const src = current;
567
+ const srcZone = currentZone;
568
+ const destZone = opts.zone;
569
+ const vs = viewScope;
570
+ src.compositor.unmount(ref.id);
571
+ try {
572
+ src.compositor.commit();
573
+ } catch (e) {
574
+ src.compositor.mount(ref, { zone: srcZone });
575
+ try {
576
+ src.compositor.commit();
577
+ } catch {
578
+ await doDispose();
579
+ throw e;
580
+ }
581
+ throw e;
582
+ }
583
+ dest.compositor.mount(ref, { zone: destZone });
584
+ try {
585
+ dest.compositor.commit();
586
+ } catch (destErr) {
587
+ dest.compositor.unmount(ref.id);
588
+ src.compositor.mount(ref, { zone: srcZone });
589
+ try {
590
+ src.compositor.commit();
591
+ } catch {
592
+ await doDispose();
593
+ throw destErr;
594
+ }
595
+ throw destErr;
596
+ }
597
+ current = {
598
+ compositor: dest.compositor,
599
+ windowScope: dest.windowScope
600
+ };
601
+ currentZone = destZone;
602
+ if (opts.rehome) {
603
+ const donor = owningWindowScope ?? src.windowScope;
604
+ try {
605
+ await donor.adopt(vs, dest.windowScope);
606
+ } catch (adoptErr) {
607
+ dest.compositor.unmount(ref.id);
608
+ try {
609
+ dest.compositor.commit();
610
+ } catch {}
611
+ src.compositor.mount(ref, { zone: srcZone });
612
+ current = src;
613
+ currentZone = srcZone;
614
+ try {
615
+ src.compositor.commit();
616
+ } catch {
617
+ await doDispose();
618
+ throw adoptErr;
619
+ }
620
+ throw adoptErr;
621
+ }
622
+ owningWindowScope = dest.windowScope;
623
+ }
624
+ }
625
+ const handle = {
626
+ placeIn(target, opts) {
627
+ if (viewScope) throw new Error("ViewHandle.placeIn: view already placed — use moveTo() to migrate");
628
+ current = {
629
+ compositor: target.compositor,
630
+ windowScope: target.windowScope
631
+ };
632
+ currentZone = opts.zone;
633
+ ensureMounted();
634
+ active = true;
635
+ const vs = target.windowScope.child();
636
+ viewScope = vs;
637
+ owningWindowScope = target.windowScope;
638
+ vs.own(() => {
639
+ if (current) {
640
+ current.compositor.unmount(ref.id);
641
+ current.compositor.commit();
642
+ }
643
+ deps.nativeView.destroy?.();
644
+ });
645
+ vs.own(() => {
646
+ active = false;
647
+ });
648
+ vs.own(() => {
649
+ deps.onDispose?.();
650
+ });
651
+ return handle;
652
+ },
653
+ applyPlacement(p) {
654
+ if (!active || !current) return;
655
+ if (migrating) return;
656
+ if (p.visible) {
657
+ ensureMounted();
658
+ nativeView.setBounds(p.bounds);
659
+ visibleBounds = {
660
+ x: p.bounds.x,
661
+ y: p.bounds.y,
662
+ width: p.bounds.width,
663
+ height: p.bounds.height
664
+ };
665
+ } else {
666
+ current.compositor.unmount(ref.id);
667
+ current.compositor.commit();
668
+ visibleBounds = null;
669
+ }
670
+ },
671
+ moveTo(dest, opts) {
672
+ return withLock(async () => {
673
+ migrating = true;
674
+ try {
675
+ return await doMove(dest, opts);
676
+ } finally {
677
+ migrating = false;
678
+ }
679
+ });
680
+ },
681
+ dispose() {
682
+ return withLock(() => doDispose());
683
+ },
684
+ get webContents() {
685
+ return nativeView.webContents;
686
+ },
687
+ bounds() {
688
+ if (!active || !visibleBounds) return null;
689
+ return { ...visibleBounds };
690
+ },
691
+ capturePage() {
692
+ if (!nativeView.capturePage) return Promise.reject(/* @__PURE__ */ new Error("ViewHandle.capturePage: native view has no capturePage"));
693
+ return nativeView.capturePage();
694
+ }
695
+ };
696
+ return handle;
697
+ }
698
+ //#endregion
699
+ export { createLogger as a, toDisposable as c, createScope as i, CommitError as n, setLogLevel as o, createCompositor as r, DisposableRegistry as s, createViewHandle as t };
700
+
701
+ //# sourceMappingURL=view-handle-CR-yWNfr.js.map