@dimina-kit/electron-deck 0.1.0-dev.20260701083540 → 0.1.0-dev.20260702145840
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/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +92 -12
- package/dist/client/index.js.map +1 -1
- package/dist/client/layout-client.d.ts +35 -14
- package/dist/client/layout-client.d.ts.map +1 -1
- package/dist/client/placement-publisher.d.ts +14 -0
- package/dist/client/placement-publisher.d.ts.map +1 -0
- package/dist/dock-react/index.js +1 -1
- package/dist/{electron-deck-C9_Yu3fd.js → electron-deck-B9fun93z.js} +105 -78
- package/dist/electron-deck-B9fun93z.js.map +1 -0
- package/dist/host/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/deck-app.d.ts +43 -11
- package/dist/internal/deck-app.d.ts.map +1 -1
- package/dist/internal/wire-transport.d.ts +17 -14
- package/dist/internal/wire-transport.d.ts.map +1 -1
- package/dist/layout/index.d.ts +4 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/layout/placement-reconcile.d.ts +50 -0
- package/dist/layout/placement-reconcile.d.ts.map +1 -0
- package/dist/layout/snapshot-reconcile.d.ts +28 -0
- package/dist/layout/snapshot-reconcile.d.ts.map +1 -0
- package/dist/{layout-B1t15b_q.js → layout-ByOiOdnc.js} +267 -2
- package/dist/layout-ByOiOdnc.js.map +1 -0
- package/dist/main/index.js +1 -1
- package/dist/main/view-handle.d.ts.map +1 -1
- package/dist/preload/index.cjs +4 -4
- package/dist/preload/index.cjs.map +1 -1
- package/dist/preload/index.d.ts +1 -1
- package/dist/preload/index.js +4 -4
- package/dist/preload/index.js.map +1 -1
- package/dist/{protocol-BJqo-vP2.js → protocol-B6TOT9AP.js} +2 -2
- package/dist/protocol-B6TOT9AP.js.map +1 -0
- package/dist/shared/protocol.d.ts +1 -1
- package/dist/{view-handle-CR-yWNfr.js → view-handle-DDhpBuW-.js} +2 -2
- package/dist/{view-handle-CR-yWNfr.js.map → view-handle-DDhpBuW-.js.map} +1 -1
- package/package.json +2 -2
- package/dist/electron-deck-C9_Yu3fd.js.map +0 -1
- package/dist/layout-B1t15b_q.js.map +0 -1
- package/dist/protocol-BJqo-vP2.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { n as DeckRemoteError, r as EventNotBoundError } from "./errors-NE2ig7L5.js";
|
|
2
|
-
import { i as DeckChannel, t as BRIDGE_PROTOCOL_VERSION } from "./protocol-
|
|
3
|
-
import { i as createScope, r as createCompositor, t as createViewHandle } from "./view-handle-
|
|
2
|
+
import { i as DeckChannel, t as BRIDGE_PROTOCOL_VERSION } from "./protocol-B6TOT9AP.js";
|
|
3
|
+
import { i as createScope, r as createCompositor, t as createViewHandle } from "./view-handle-DDhpBuW-.js";
|
|
4
|
+
import { i as reconcile, n as dispatchOps, r as createInitialState, t as cleanSnapshot } from "./layout-ByOiOdnc.js";
|
|
4
5
|
import { randomUUID } from "node:crypto";
|
|
5
6
|
//#region src/events.ts
|
|
6
7
|
var internals = /* @__PURE__ */ new WeakMap();
|
|
@@ -395,15 +396,15 @@ var WireTransport = class {
|
|
|
395
396
|
deps;
|
|
396
397
|
state = "idle";
|
|
397
398
|
busSubscription = null;
|
|
398
|
-
/** Whether the optional
|
|
399
|
+
/** Whether the optional Snapshot / LayoutSubscribe handlers were registered
|
|
399
400
|
* (either eagerly at start() via deps, or lazily via armSlotChannels) — so
|
|
400
401
|
* dispose() only removes channels it actually registered. */
|
|
401
|
-
|
|
402
|
+
snapshotRegistered = false;
|
|
402
403
|
layoutSubscribeRegistered = false;
|
|
403
404
|
/** Effective slot callbacks. Populated from deps at start() (eager path, used
|
|
404
405
|
* by the wire unit tests) OR by {@link armSlotChannels} (lazy path, used by
|
|
405
406
|
* deck-app so an app with no anchored views registers only Invoke + Probe). */
|
|
406
|
-
|
|
407
|
+
onSnapshotCb = null;
|
|
407
408
|
onLayoutSubscribeCb = null;
|
|
408
409
|
constructor(deps) {
|
|
409
410
|
this.deps = deps;
|
|
@@ -418,10 +419,10 @@ var WireTransport = class {
|
|
|
418
419
|
cleanups.push(() => this.tryRemoveHandler(DeckChannel.Probe));
|
|
419
420
|
this.deps.ipcMain.handle(DeckChannel.Invoke, (event, ...args) => this.handleInvoke(event?.sender?.id, event?.senderFrame, event?.sender?.mainFrame, args[0]));
|
|
420
421
|
cleanups.push(() => this.tryRemoveHandler(DeckChannel.Invoke));
|
|
421
|
-
if (this.deps.
|
|
422
|
-
this.
|
|
423
|
-
this.
|
|
424
|
-
cleanups.push(() => this.
|
|
422
|
+
if (this.deps.onSnapshot) {
|
|
423
|
+
this.onSnapshotCb = this.deps.onSnapshot;
|
|
424
|
+
this.registerSnapshotHandler();
|
|
425
|
+
cleanups.push(() => this.unregisterSnapshotHandler());
|
|
425
426
|
}
|
|
426
427
|
if (this.deps.onLayoutSubscribe) {
|
|
427
428
|
this.onLayoutSubscribeCb = this.deps.onLayoutSubscribe;
|
|
@@ -454,15 +455,15 @@ var WireTransport = class {
|
|
|
454
455
|
console.error(`[electron-deck] removeHandler(${channel}) failed:`, e);
|
|
455
456
|
}
|
|
456
457
|
}
|
|
457
|
-
|
|
458
|
-
if (this.
|
|
459
|
-
this.deps.ipcMain.handle(DeckChannel.
|
|
460
|
-
this.
|
|
458
|
+
registerSnapshotHandler() {
|
|
459
|
+
if (this.snapshotRegistered) return;
|
|
460
|
+
this.deps.ipcMain.handle(DeckChannel.Snapshot, (event, ...args) => this.handleSnapshot(event?.sender?.id, event?.senderFrame, event?.sender?.mainFrame, args[0]));
|
|
461
|
+
this.snapshotRegistered = true;
|
|
461
462
|
}
|
|
462
|
-
|
|
463
|
-
if (!this.
|
|
464
|
-
this.
|
|
465
|
-
this.tryRemoveHandler(DeckChannel.
|
|
463
|
+
unregisterSnapshotHandler() {
|
|
464
|
+
if (!this.snapshotRegistered) return;
|
|
465
|
+
this.snapshotRegistered = false;
|
|
466
|
+
this.tryRemoveHandler(DeckChannel.Snapshot);
|
|
466
467
|
}
|
|
467
468
|
registerLayoutSubscribeHandler() {
|
|
468
469
|
if (this.layoutSubscribeRegistered) return;
|
|
@@ -482,11 +483,11 @@ var WireTransport = class {
|
|
|
482
483
|
* no anchored views never registers these channels (keeping the wire's
|
|
483
484
|
* handler footprint at Invoke + Probe until a slot is actually minted).
|
|
484
485
|
*/
|
|
485
|
-
armSlotChannels(
|
|
486
|
-
this.
|
|
486
|
+
armSlotChannels(onSnapshot, onLayoutSubscribe) {
|
|
487
|
+
this.onSnapshotCb = onSnapshot;
|
|
487
488
|
this.onLayoutSubscribeCb = onLayoutSubscribe;
|
|
488
489
|
if (this.state !== "started") return;
|
|
489
|
-
this.
|
|
490
|
+
this.registerSnapshotHandler();
|
|
490
491
|
this.registerLayoutSubscribeHandler();
|
|
491
492
|
}
|
|
492
493
|
dispose() {
|
|
@@ -505,12 +506,12 @@ var WireTransport = class {
|
|
|
505
506
|
} catch (e) {
|
|
506
507
|
console.error("[electron-deck] removeHandler(probe) failed:", e);
|
|
507
508
|
}
|
|
508
|
-
if (this.
|
|
509
|
-
this.
|
|
509
|
+
if (this.snapshotRegistered) {
|
|
510
|
+
this.snapshotRegistered = false;
|
|
510
511
|
try {
|
|
511
|
-
this.deps.ipcMain.removeHandler(DeckChannel.
|
|
512
|
+
this.deps.ipcMain.removeHandler(DeckChannel.Snapshot);
|
|
512
513
|
} catch (e) {
|
|
513
|
-
console.error("[electron-deck] removeHandler(
|
|
514
|
+
console.error("[electron-deck] removeHandler(snapshot) failed:", e);
|
|
514
515
|
}
|
|
515
516
|
}
|
|
516
517
|
if (this.layoutSubscribeRegistered) {
|
|
@@ -576,17 +577,16 @@ var WireTransport = class {
|
|
|
576
577
|
}
|
|
577
578
|
}
|
|
578
579
|
/**
|
|
579
|
-
* `__electron-deck:
|
|
580
|
-
*
|
|
581
|
-
* sub-frame) → DROP silently
|
|
580
|
+
* `__electron-deck:snapshot` inbound. Same gate as {@link handleInvoke} (trust +
|
|
581
|
+
* main-frame). On any gate failure (non-object payload, untrusted sender,
|
|
582
|
+
* sub-frame) → DROP silently. The payload's inner shape is opaque to the wire —
|
|
583
|
+
* authorized + validated downstream.
|
|
582
584
|
*/
|
|
583
|
-
|
|
584
|
-
if (
|
|
585
|
-
const msg = rawMsg;
|
|
586
|
-
if (typeof msg.slotToken !== "string") return;
|
|
585
|
+
handleSnapshot(senderId, senderFrame, mainFrame, rawSnapshot) {
|
|
586
|
+
if (rawSnapshot === null || typeof rawSnapshot !== "object" || Array.isArray(rawSnapshot)) return;
|
|
587
587
|
if (typeof senderId !== "number" || !this.deps.senderPolicy.isTrusted(senderId)) return;
|
|
588
588
|
if (!this.isMainFrameSender(senderFrame, mainFrame)) return;
|
|
589
|
-
this.
|
|
589
|
+
this.onSnapshotCb?.(senderId, rawSnapshot);
|
|
590
590
|
}
|
|
591
591
|
/**
|
|
592
592
|
* `__electron-deck:layout-subscribe` inbound. Same gate as {@link handleInvoke}.
|
|
@@ -875,12 +875,31 @@ var DeckApp = class {
|
|
|
875
875
|
/**
|
|
876
876
|
* slot-token registry (view-handle.md「slot-token 握手」/ capability-and-lifecycle.md「anchor slotToken 原子下发」):
|
|
877
877
|
* each anchored `placeIn` mints an unguessable token bound to (viewId, slotId,
|
|
878
|
-
* authorizedWcId). The `__electron-deck:
|
|
879
|
-
*
|
|
880
|
-
*
|
|
878
|
+
* authorizedWcId, zone). The `__electron-deck:snapshot` apply path authorizes
|
|
879
|
+
* each of the renderer's per-view tokens (token known AND authorized to the
|
|
880
|
+
* sender wc), DERIVES the view's identity (viewId) + z-order (zone→layer) from
|
|
881
|
+
* this registry (never trusting the renderer-reported fields), reconciles the
|
|
882
|
+
* cleaned window-level table, and drives each view's `apply`. `resend` re-pushes
|
|
883
|
+
* the slot-grant (layout-subscribe replay).
|
|
881
884
|
*/
|
|
882
885
|
slotTokens = /* @__PURE__ */ new Map();
|
|
883
886
|
slotSeq = 0;
|
|
887
|
+
/**
|
|
888
|
+
* Per-control-wc level-triggered reconciler state (keyed by authorizedWcId).
|
|
889
|
+
* The renderer publishes a whole window-level desired-placement table each
|
|
890
|
+
* frame; `reconcile` diffs it against the last-applied actual and emits ops, so
|
|
891
|
+
* a lost or spurious per-view edge self-corrects instead of sticking a view
|
|
892
|
+
* detached (the white-screen failure mode). Reset on layout-subscribe (reload)
|
|
893
|
+
* and dropped on wc revoke.
|
|
894
|
+
*/
|
|
895
|
+
reconcileStates = /* @__PURE__ */ new Map();
|
|
896
|
+
/**
|
|
897
|
+
* Per-control-wc strictly-monotonic generation counter. Bumped on every
|
|
898
|
+
* layout-subscribe and stamped into each slot-grant, so a reload's higher
|
|
899
|
+
* generation resets the reconciler regardless of IPC ordering (a late in-flight
|
|
900
|
+
* pre-reload snapshot carries a lower generation → rejected).
|
|
901
|
+
*/
|
|
902
|
+
perWcGeneration = /* @__PURE__ */ new Map();
|
|
884
903
|
_runtime = null;
|
|
885
904
|
startCalled = false;
|
|
886
905
|
shutdownPromise = null;
|
|
@@ -1627,32 +1646,58 @@ var DeckApp = class {
|
|
|
1627
1646
|
* builds can't serve a renderer slot anyway).
|
|
1628
1647
|
*/
|
|
1629
1648
|
ensureSlotChannelsArmed() {
|
|
1630
|
-
this.wireTransport?.armSlotChannels((senderId,
|
|
1649
|
+
this.wireTransport?.armSlotChannels((senderId, rawSnapshot) => this.handleSnapshot(senderId, rawSnapshot), (senderId) => this.handleLayoutSubscribe(senderId));
|
|
1631
1650
|
}
|
|
1632
1651
|
/**
|
|
1633
|
-
* slot-token apply path (`__electron-deck:
|
|
1634
|
-
*
|
|
1635
|
-
*
|
|
1652
|
+
* slot-token apply path (`__electron-deck:snapshot`). The renderer publishes a
|
|
1653
|
+
* whole window-level desired-placement table; each entry's slotToken is the
|
|
1654
|
+
* credential. Steps:
|
|
1655
|
+
* 1. AUTHORIZE + clean: `cleanSnapshot` keeps only views whose token is known
|
|
1656
|
+
* AND authorized to `senderId` (anti-spoof), deriving each view's identity
|
|
1657
|
+
* (viewId) + z-order (zone→layer) from the registry — never from the
|
|
1658
|
+
* renderer-reported fields. A non-empty snapshot that fully fails
|
|
1659
|
+
* authorization is rejected wholesale (returns null) so a transient token
|
|
1660
|
+
* failure is NOT read as "detach everything".
|
|
1661
|
+
* 2. RECONCILE the cleaned table against this wc's last-applied actual state
|
|
1662
|
+
* → an ordered op list (level-triggered; a lost/spurious frame self-heals).
|
|
1663
|
+
* 3. DISPATCH the ops onto each view's `applyPlacement` sink.
|
|
1636
1664
|
*/
|
|
1637
|
-
|
|
1638
|
-
const
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1665
|
+
handleSnapshot(senderId, rawSnapshot) {
|
|
1666
|
+
const clean = cleanSnapshot(rawSnapshot, (slotToken) => {
|
|
1667
|
+
const entry = this.slotTokens.get(slotToken);
|
|
1668
|
+
if (!entry || entry.authorizedWcId !== senderId) return null;
|
|
1669
|
+
return {
|
|
1670
|
+
viewId: entry.viewId,
|
|
1671
|
+
layer: entry.zone
|
|
1672
|
+
};
|
|
1673
|
+
});
|
|
1674
|
+
if (!clean) return;
|
|
1675
|
+
const { state, ops } = reconcile(this.reconcileStates.get(senderId) ?? createInitialState(), clean);
|
|
1676
|
+
this.reconcileStates.set(senderId, state);
|
|
1677
|
+
const applyByViewId = /* @__PURE__ */ new Map();
|
|
1678
|
+
for (const entry of this.slotTokens.values()) if (entry.authorizedWcId === senderId) applyByViewId.set(entry.viewId, entry.apply);
|
|
1679
|
+
dispatchOps(ops, state, (viewId) => applyByViewId.get(viewId) ?? null);
|
|
1643
1680
|
}
|
|
1644
1681
|
/**
|
|
1645
1682
|
* Per-wc layout-subscribe replay (`__electron-deck:layout-subscribe`). When the
|
|
1646
|
-
* authorized control wc (re)subscribes (e.g. after reload),
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1683
|
+
* authorized control wc (re)subscribes (e.g. after reload), BUMP its generation
|
|
1684
|
+
* and RESET its reconciler (a reload restarts the renderer's epoch at 0; without
|
|
1685
|
+
* the reset the fresh low-epoch snapshots would be rejected as stale), then
|
|
1686
|
+
* re-push every grant it owns — now carrying the bumped generation — so it can
|
|
1687
|
+
* re-bind its DOM slots. A DIFFERENT wc's subscribe never touches another wc's
|
|
1688
|
+
* state or grants.
|
|
1649
1689
|
*/
|
|
1650
1690
|
handleLayoutSubscribe(senderId) {
|
|
1691
|
+
this.perWcGeneration.set(senderId, (this.perWcGeneration.get(senderId) ?? 0) + 1);
|
|
1692
|
+
this.reconcileStates.delete(senderId);
|
|
1651
1693
|
for (const entry of this.slotTokens.values()) if (entry.authorizedWcId === senderId) entry.resend();
|
|
1652
1694
|
}
|
|
1653
|
-
/** Drop every slot token authorized to `wcId`
|
|
1695
|
+
/** Drop every slot token + reconciler/generation state authorized to `wcId`
|
|
1696
|
+
* (window-close / shutdown hygiene). */
|
|
1654
1697
|
revokeSlotTokensForWc(wcId) {
|
|
1655
1698
|
for (const [token, entry] of this.slotTokens) if (entry.authorizedWcId === wcId) this.slotTokens.delete(token);
|
|
1699
|
+
this.reconcileStates.delete(wcId);
|
|
1700
|
+
this.perWcGeneration.delete(wcId);
|
|
1656
1701
|
}
|
|
1657
1702
|
handleSubWindowClosed(win, wc) {
|
|
1658
1703
|
this.trackedWindows.delete(win);
|
|
@@ -1934,24 +1979,26 @@ var DeckApp = class {
|
|
|
1934
1979
|
let placedSubstrate = null;
|
|
1935
1980
|
let slotToken;
|
|
1936
1981
|
let placed = false;
|
|
1937
|
-
const mintSlotToken = (controlWc, anchor) => {
|
|
1982
|
+
const mintSlotToken = (controlWc, anchor, zone) => {
|
|
1938
1983
|
this.ensureSlotChannelsArmed();
|
|
1939
1984
|
const authorizedWcId = controlWc.id;
|
|
1940
1985
|
const token = randomUUID();
|
|
1941
1986
|
slotToken = token;
|
|
1942
1987
|
++this.slotSeq;
|
|
1943
|
-
const grant = {
|
|
1944
|
-
viewId,
|
|
1945
|
-
slotId: anchor,
|
|
1946
|
-
slotToken: token
|
|
1947
|
-
};
|
|
1948
1988
|
const resend = () => {
|
|
1989
|
+
const grant = {
|
|
1990
|
+
viewId,
|
|
1991
|
+
slotId: anchor,
|
|
1992
|
+
slotToken: token,
|
|
1993
|
+
generation: this.perWcGeneration.get(authorizedWcId) ?? 0
|
|
1994
|
+
};
|
|
1949
1995
|
controlWc.send(DeckChannel.SlotGrant, grant);
|
|
1950
1996
|
};
|
|
1951
1997
|
this.slotTokens.set(token, {
|
|
1952
1998
|
viewId,
|
|
1953
1999
|
slotId: anchor,
|
|
1954
2000
|
authorizedWcId,
|
|
2001
|
+
zone,
|
|
1955
2002
|
resend,
|
|
1956
2003
|
apply: (p) => {
|
|
1957
2004
|
hostHandle.applyPlacement(p);
|
|
@@ -1975,7 +2022,7 @@ var DeckApp = class {
|
|
|
1975
2022
|
placed = true;
|
|
1976
2023
|
disarmRootClose();
|
|
1977
2024
|
const anchor = placeOpts.anchor;
|
|
1978
|
-
if (typeof anchor === "string" && anchor.length > 0) mintSlotToken(controlWc, anchor);
|
|
2025
|
+
if (typeof anchor === "string" && anchor.length > 0) mintSlotToken(controlWc, anchor, placeOpts.zone ?? 0);
|
|
1979
2026
|
return hostHandle;
|
|
1980
2027
|
},
|
|
1981
2028
|
moveTo: async (win, moveOpts) => {
|
|
@@ -2024,7 +2071,7 @@ var DeckApp = class {
|
|
|
2024
2071
|
slotToken = void 0;
|
|
2025
2072
|
}
|
|
2026
2073
|
const anchor = moveOpts.anchor;
|
|
2027
|
-
if (typeof anchor === "string" && anchor.length > 0) mintSlotToken(destControlWc, anchor);
|
|
2074
|
+
if (typeof anchor === "string" && anchor.length > 0) mintSlotToken(destControlWc, anchor, moveOpts.zone ?? 0);
|
|
2028
2075
|
},
|
|
2029
2076
|
applyPlacement: (p) => {
|
|
2030
2077
|
inner.applyPlacement(p);
|
|
@@ -2111,26 +2158,6 @@ var DeckApp = class {
|
|
|
2111
2158
|
};
|
|
2112
2159
|
}
|
|
2113
2160
|
};
|
|
2114
|
-
/**
|
|
2115
|
-
* Validate an inbound `__electron-deck:place` placement before applying it.
|
|
2116
|
-
* `{visible:false}` is always OK; `{visible:true, bounds:{x,y,width,height}}`
|
|
2117
|
-
* requires width≥0 && height≥0 — x/y may be ANY finite number (negative origin
|
|
2118
|
-
* is legitimate for scroll-follow). Anything else is rejected.
|
|
2119
|
-
*/
|
|
2120
|
-
function isValidPlacement(placement) {
|
|
2121
|
-
if (placement === null || typeof placement !== "object") return false;
|
|
2122
|
-
const p = placement;
|
|
2123
|
-
if (p.visible === false) return true;
|
|
2124
|
-
if (p.visible !== true) return false;
|
|
2125
|
-
const b = p.bounds;
|
|
2126
|
-
if (b === null || typeof b !== "object") return false;
|
|
2127
|
-
const { x, y, width, height } = b;
|
|
2128
|
-
if (typeof x !== "number" || !Number.isFinite(x)) return false;
|
|
2129
|
-
if (typeof y !== "number" || !Number.isFinite(y)) return false;
|
|
2130
|
-
if (typeof width !== "number" || !Number.isFinite(width) || width < 0) return false;
|
|
2131
|
-
if (typeof height !== "number" || !Number.isFinite(height) || height < 0) return false;
|
|
2132
|
-
return true;
|
|
2133
|
-
}
|
|
2134
2161
|
async function runWithTimeout(work, timeoutMs) {
|
|
2135
2162
|
let timer;
|
|
2136
2163
|
const timeout = new Promise((_, rej) => {
|
|
@@ -2286,4 +2313,4 @@ function validateHandlerMap(fieldName, value) {
|
|
|
2286
2313
|
//#endregion
|
|
2287
2314
|
export { WireTransport as a, InMemoryTypedIpcRegistry as c, createControlBus as i, EventBus as l, startElectronDeck as n, createCapabilityRegistry as o, validateConfig as r, createTrustSet as s, electronDeck as t, defineEvent as u };
|
|
2288
2315
|
|
|
2289
|
-
//# sourceMappingURL=electron-deck-
|
|
2316
|
+
//# sourceMappingURL=electron-deck-B9fun93z.js.map
|