@dimina-kit/electron-deck 0.1.0-dev.20260701083540 → 0.1.0-dev.20260701155140
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 +4 -4
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout-ByOiOdnc.js","names":[],"sources":["../src/layout/registry.ts","../src/layout/serialize.ts","../src/layout/sanitize.ts","../src/layout/mutations.ts","../src/layout/model.ts","../src/layout/user-actions.ts","../src/layout/placement-reconcile.ts","../src/layout/snapshot-reconcile.ts"],"sourcesContent":["/**\n * Panel registry — pure in-memory map of panel descriptors. Pure TS.\n */\nimport type { Disposable, PanelDescriptor, PanelRegistry } from './types.js'\n\nexport function createPanelRegistry(): PanelRegistry {\n\tconst map = new Map<string, PanelDescriptor>()\n\treturn {\n\t\tregister(p: PanelDescriptor): Disposable {\n\t\t\tmap.set(p.id, p)\n\t\t\treturn {\n\t\t\t\tdispose(): void {\n\t\t\t\t\t// Only drop if this exact descriptor is still registered, so a\n\t\t\t\t\t// re-register under the same id isn't undone by a stale handle.\n\t\t\t\t\tif (map.get(p.id) === p) map.delete(p.id)\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t\tget(id: string): PanelDescriptor | undefined {\n\t\t\treturn map.get(id)\n\t\t},\n\t\tlist(): readonly PanelDescriptor[] {\n\t\t\treturn [...map.values()]\n\t\t},\n\t}\n}\n","/**\n * Serialize / parse / validate — structural integrity + acyclicity. Pure TS.\n *\n * Default-DENY: parseLayout throws on any tree validateTree would flag.\n */\nimport type { LayoutNode, LayoutTree } from './types.js'\n\nconst MAX_DEPTH = 64\n\nexport function serializeLayout(t: LayoutTree): string {\n\treturn JSON.stringify(t)\n}\n\n/** Mutable cross-node state threaded through the whole walk. */\ninterface WalkState {\n\treadonly seenIds: Set<string>\n\treadonly seenObjects: Set<object>\n\treadonly panelOwners: Map<string, string>\n\treadonly knownPanelIds: ReadonlySet<string> | null\n}\n\n/** Report missing / duplicate node id, registering the id when first seen. */\nfunction checkNodeId(id: string | undefined, kind: unknown, state: WalkState, problems: string[]): void {\n\tif (id === undefined) {\n\t\tproblems.push(`node missing string id (kind=${String(kind)})`)\n\t}\n\telse if (state.seenIds.has(id)) {\n\t\tproblems.push(`duplicate node id: ${id}`)\n\t}\n\telse {\n\t\tstate.seenIds.add(id)\n\t}\n}\n\n/** Validate a single panel id within a tabgroup: type, cross-group ownership, orphan. */\nfunction checkPanel(p: unknown, id: string | undefined, state: WalkState, problems: string[]): void {\n\tif (typeof p !== 'string') {\n\t\tproblems.push(`tabs ${id ?? '?'}: non-string panel id`)\n\t\treturn\n\t}\n\tconst owner = state.panelOwners.get(p)\n\tif (owner !== undefined) {\n\t\tproblems.push(`duplicate panel id across groups: ${p}`)\n\t}\n\telse {\n\t\tstate.panelOwners.set(p, id ?? '?')\n\t}\n\tif (state.knownPanelIds && !state.knownPanelIds.has(p)) {\n\t\tproblems.push(`orphan panel not in known panel ids: ${p}`)\n\t}\n}\n\n/** Validate a `tabs` node: panels array, per-panel rules, active membership. */\nfunction checkTabs(node: unknown, id: string | undefined, state: WalkState, problems: string[]): void {\n\tconst tg = node as { panels?: unknown; active?: unknown }\n\tconst panels = Array.isArray(tg.panels) ? (tg.panels as unknown[]) : null\n\tif (!panels) {\n\t\tproblems.push(`tabs ${id ?? '?'}: panels is not an array`)\n\t\treturn\n\t}\n\tif (panels.length === 0) {\n\t\tproblems.push(`tabs ${id ?? '?'}: empty tabgroup`)\n\t}\n\tfor (const p of panels) {\n\t\tcheckPanel(p, id, state, problems)\n\t}\n\tconst active = tg.active\n\tif (typeof active !== 'string' || !panels.includes(active)) {\n\t\tproblems.push(`tabs ${id ?? '?'}: active ${String(active)} not in panels`)\n\t}\n}\n\n/** Validate one constraint entry. Returns true if it is PX-SIZED (non-null). */\nfunction checkConstraintEntry(c: unknown, id: string | undefined, problems: string[]): boolean {\n\tif (c === null) {\n\t\treturn false\n\t}\n\tif (typeof c !== 'object') {\n\t\tproblems.push(`split ${id ?? '?'}: constraint is not null nor an object: ${String(c)}`)\n\t\treturn false\n\t}\n\t// Exactly ONE of `fixedPx` / `minPx`, value finite > 0.\n\tconst keys = Object.keys(c as object)\n\tconst hasFixed = keys.includes('fixedPx')\n\tconst hasMin = keys.includes('minPx')\n\tif (keys.length !== 1 || !(hasFixed || hasMin)) {\n\t\tproblems.push(`split ${id ?? '?'}: constraint must have exactly one of 'fixedPx' or 'minPx', got [${keys.join(', ')}]`)\n\t}\n\tconst cKey = hasFixed ? 'fixedPx' : 'minPx'\n\tconst cVal = hasFixed ? (c as { fixedPx?: unknown }).fixedPx : (c as { minPx?: unknown }).minPx\n\tif (typeof cVal !== 'number' || !Number.isFinite(cVal) || cVal <= 0) {\n\t\tproblems.push(`split ${id ?? '?'}: constraint ${cKey} must be a finite number > 0, got ${String(cVal)}`)\n\t}\n\treturn true\n}\n\n/**\n * Validate the OPTIONAL `constraints` field's INTRINSIC format (array shape +\n * per-entry rules + exactly-one-of fixedPx/minPx + all-px guard), INDEPENDENTLY\n * of `children` validity. The LENGTH-vs-children comparison lives elsewhere.\n */\nfunction checkConstraints(raw: unknown, id: string | undefined, problems: string[]): void {\n\tconst constraints = Array.isArray(raw) ? (raw as unknown[]) : null\n\tif (!constraints) {\n\t\tproblems.push(`split ${id ?? '?'}: constraints is not an array`)\n\t\treturn\n\t}\n\t// Tracks whether EVERY child is PX-SIZED (a non-null constraint — `fixedPx`\n\t// OR `minPx`). Both are sized in px and excluded from the weight pool, so an\n\t// all-constrained split trips the rrp footgun (needs >= 1 weight-sized\n\t// child); only a `null` child clears it.\n\tlet everyConstrained = constraints.length > 0\n\tfor (const c of constraints) {\n\t\tif (!checkConstraintEntry(c, id, problems)) {\n\t\t\teveryConstrained = false\n\t\t}\n\t}\n\t// Guard the rrp footgun: an all-px-sized split has no weight-sized child to\n\t// absorb leftover space (rrp v4.10 requires >= 1).\n\tif (everyConstrained) {\n\t\tproblems.push(`split ${id ?? '?'}: all children are px-sized constraints; at least one must be weight-sized`)\n\t}\n}\n\n/** Validate the `sizes` field against a valid `children` array. */\nfunction checkSizes(sizes: unknown[] | null, children: unknown[], id: string | undefined, problems: string[]): void {\n\tif (!sizes || sizes.length !== children.length) {\n\t\tproblems.push(\n\t\t\t`split ${id ?? '?'}: sizes ${sizes ? sizes.length : 'missing'} != children ${children.length}`,\n\t\t)\n\t\treturn\n\t}\n\tfor (const s of sizes) {\n\t\tif (typeof s !== 'number' || !Number.isFinite(s)) {\n\t\t\tproblems.push(`split ${id ?? '?'}: non-finite size ${String(s)}`)\n\t\t}\n\t}\n}\n\n/**\n * Walk an arbitrary (possibly malformed / cyclic / shared-ref) node graph and\n * collect structural problems. Never recurses infinitely: a node already on the\n * current path (cycle) or already visited elsewhere (shared ref) is reported and\n * not descended into.\n */\nfunction collectProblems(\n\troot: unknown,\n\tknownPanelIds: ReadonlySet<string> | null,\n): string[] {\n\tconst problems: string[] = []\n\tconst state: WalkState = {\n\t\tseenIds: new Set<string>(),\n\t\tseenObjects: new Set<object>(),\n\t\tpanelOwners: new Map<string, string>(),\n\t\tknownPanelIds,\n\t}\n\n\tconst visit = (node: unknown, depth: number, path: Set<object>): void => {\n\t\tif (depth > MAX_DEPTH) {\n\t\t\tproblems.push(`depth exceeds ${MAX_DEPTH}`)\n\t\t\treturn\n\t\t}\n\t\tif (node === null || typeof node !== 'object') {\n\t\t\tproblems.push(`node is not an object: ${String(node)}`)\n\t\t\treturn\n\t\t}\n\t\tconst obj = node as object\n\t\tif (path.has(obj)) {\n\t\t\tproblems.push('cycle detected: node reachable from itself')\n\t\t\treturn\n\t\t}\n\t\tif (state.seenObjects.has(obj)) {\n\t\t\tproblems.push('shared node reference: same object appears twice')\n\t\t\treturn\n\t\t}\n\t\tstate.seenObjects.add(obj)\n\n\t\tconst n = node as { kind?: unknown; id?: unknown }\n\t\tconst kind = n.kind\n\t\tconst id = typeof n.id === 'string' ? n.id : undefined\n\n\t\tcheckNodeId(id, kind, state, problems)\n\n\t\tif (kind === 'tabs') {\n\t\t\tcheckTabs(node, id, state, problems)\n\t\t}\n\t\telse if (kind === 'split') {\n\t\t\tvisitSplit(node, id, depth, path, obj, state, problems, visit)\n\t\t}\n\t\telse {\n\t\t\tproblems.push(`unknown node kind: ${String(kind)}`)\n\t\t}\n\t}\n\n\tvisit(root, 0, new Set())\n\treturn problems\n}\n\n/** Validate a `split` node and recurse into its children. */\nfunction visitSplit(\n\tnode: unknown,\n\tid: string | undefined,\n\tdepth: number,\n\tpath: Set<object>,\n\tobj: object,\n\tstate: WalkState,\n\tproblems: string[],\n\tvisit: (node: unknown, depth: number, path: Set<object>) => void,\n): void {\n\tconst sp = node as { children?: unknown; sizes?: unknown; orientation?: unknown; constraints?: unknown }\n\tconst children = Array.isArray(sp.children) ? (sp.children as unknown[]) : null\n\tconst sizes = Array.isArray(sp.sizes) ? (sp.sizes as unknown[]) : null\n\tconst orientation = sp.orientation\n\tif (orientation !== 'row' && orientation !== 'column') {\n\t\tproblems.push(`split ${id ?? '?'}: invalid orientation ${String(orientation)}`)\n\t}\n\tif (sp.constraints !== undefined) {\n\t\tcheckConstraints(sp.constraints, id, problems)\n\t}\n\n\tif (!children) {\n\t\tproblems.push(`split ${id ?? '?'}: children is not an array`)\n\t\treturn\n\t}\n\tif (children.length < 2) {\n\t\tproblems.push(`split ${id ?? '?'}: must have >= 2 children, has ${children.length}`)\n\t}\n\tcheckSizes(sizes, children, id, problems)\n\t// LENGTH-vs-children comparison (gated on a valid children array).\n\tif (sp.constraints !== undefined && Array.isArray(sp.constraints)) {\n\t\tconst constraints = sp.constraints as unknown[]\n\t\tif (constraints.length !== children.length) {\n\t\t\tproblems.push(\n\t\t\t\t`split ${id ?? '?'}: constraints ${constraints.length} != children ${children.length}`,\n\t\t\t)\n\t\t}\n\t}\n\tconst nextPath = new Set(path)\n\tnextPath.add(obj)\n\tfor (const child of children) {\n\t\tvisit(child, depth + 1, nextPath)\n\t}\n}\n\nexport function validateTree(t: LayoutTree, knownPanelIds: ReadonlySet<string>): string[] {\n\tif (t === null || typeof t !== 'object') return ['tree is not an object']\n\tif ((t as { version?: unknown }).version !== 1) {\n\t\treturn [`unsupported version: ${String((t as { version?: unknown }).version)}`]\n\t}\n\treturn collectProblems((t as { root?: unknown }).root, knownPanelIds)\n}\n\nexport function parseLayout(json: string): LayoutTree {\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(json)\n\t}\n\tcatch {\n\t\tthrow new Error('parseLayout: input is not valid JSON')\n\t}\n\tif (raw === null || typeof raw !== 'object') {\n\t\tthrow new Error('parseLayout: top-level value is not an object')\n\t}\n\tconst obj = raw as { version?: unknown; root?: unknown }\n\tif (obj.version !== 1) {\n\t\tthrow new Error(`parseLayout: unsupported version ${String(obj.version)}`)\n\t}\n\tif (obj.root === undefined || obj.root === null) {\n\t\tthrow new Error('parseLayout: missing root')\n\t}\n\t// Validate structure with no knownPanelIds constraint (orphan check N/A on\n\t// pure deserialize — caller pairs with their own registry).\n\tconst problems = collectProblems(obj.root, null)\n\tif (problems.length > 0) {\n\t\tthrow new Error(`parseLayout: illegal layout — ${problems.join('; ')}`)\n\t}\n\treturn { version: 1, root: obj.root as LayoutNode }\n}\n","/**\n * Self-healing for persisted layout trees — pure TS (no react/electron).\n *\n * A FLEXIBLE child (`constraints[i]` null / no constraints) has no enforced\n * lower weight bound in the serialized tree: `validateTree` only rejects\n * NON-FINITE sizes, not non-positive ones. A user who dragged a flexible panel\n * to ~0 width writes a ~0 (or 0, or negative) weight into the tree; on the next\n * restore that panel comes back at 0 width, invisible and effectively stuck.\n *\n * `sanitizeFlexibleWeights` heals such a tree: every flexible child whose weight\n * is NON-POSITIVE (≤ 0 or non-finite) is lifted to a minimum positive weight, so\n * a restored panel is always visible. A small-but-POSITIVE weight is left as-is\n * — weights are a relative ratio (any positive scale is valid), so a tiny\n * positive weight is healthy, not collapsed. PX-SIZED children\n * (`fixedPx`/`minPx`) are sized in pixels, NOT weights — their `sizes[i]` entry\n * is a preserved-but-unused placeholder — so they are left untouched. The\n * function is pure (clone-on-write, never mutates the input).\n */\nimport type { LayoutNode, LayoutTree, SizeConstraint, SplitNode } from './types.js'\n\n/** The healed-up value for a collapsed flexible child. A small positive weight:\n * enough to make the panel visible without stealing meaningful space from its\n * healthy siblings (the user can re-drag afterwards). */\nconst HEALED_FLEX_WEIGHT = 1\n\n/** Is child `i` of `node` flexible (weight-sized) — i.e. has no px constraint? */\nfunction isFlexibleAt(node: SplitNode, i: number): boolean {\n\treturn (node.constraints?.[i] ?? null) === null\n}\n\nfunction sanitizeNode(node: LayoutNode): LayoutNode {\n\tif (node.kind === 'tabs') return node\n\n\t// Recurse first so nested splits are healed regardless of this level.\n\tconst children = node.children.map(sanitizeNode)\n\n\t// Heal this split's FLEXIBLE child weights. ONLY a NON-POSITIVE weight (≤ 0 or\n\t// non-finite — the \"dragged to 0 / negative\" collapse) is healed; every\n\t// POSITIVE weight is kept verbatim, however small. Weights are a RELATIVE ratio\n\t// (any positive scale is valid — `[0.001, 0.006]` is the same layout as\n\t// `[1, 6]`), so a small-but-positive weight is healthy and must not be rewritten\n\t// (that would crush the ratio). Px children are never touched.\n\tlet changed = children.some((c, i) => c !== node.children[i])\n\tconst sizes = node.sizes.map((w, i) => {\n\t\tif (!isFlexibleAt(node, i)) return w\n\t\tif (typeof w === 'number' && Number.isFinite(w) && w > 0) return w\n\t\tchanged = true\n\t\treturn HEALED_FLEX_WEIGHT\n\t})\n\n\tif (!changed) return node\n\tconst rebuilt: SplitNode = {\n\t\tkind: 'split',\n\t\tid: node.id,\n\t\torientation: node.orientation,\n\t\tchildren,\n\t\tsizes,\n\t}\n\t// Preserve the constraints array verbatim (only weights are touched).\n\treturn node.constraints !== undefined\n\t\t? { ...rebuilt, constraints: node.constraints as readonly (SizeConstraint | null)[] }\n\t\t: rebuilt\n}\n\n/**\n * Return a tree in which every FLEXIBLE child with a non-positive (≤ tiny\n * threshold) weight is healed to a minimum positive weight, recursively. Px\n * children are untouched. Pure: the input is never mutated; a fully-healthy tree\n * is returned structurally unchanged (same weights).\n */\nexport function sanitizeFlexibleWeights(tree: LayoutTree): LayoutTree {\n\tconst root = sanitizeNode(tree.root)\n\treturn root === tree.root ? tree : { version: 1, root }\n}\n","/**\n * Pure tree mutations. Every function returns a NEW tree and never mutates its\n * input (clone-on-write). All structural invariants (#1) are funnelled through\n * `normalizeRoot`, which collapses empty tabgroups + single-child splits,\n * cascading upward, while keeping the root a LayoutNode.\n */\nimport type { LayoutNode, LayoutTree, Orientation, SizeConstraint, SplitNode, TabGroupNode } from './types.js'\n\n// ───────────────────────── helpers ─────────────────────────\n\nfunction tg(id: string, panels: string[], active: string): TabGroupNode {\n\treturn { kind: 'tabs', id, panels, active }\n}\n\n/** Re-derive active after panels changed: keep current active if still present,\n * else select panels[min(removedIndex, len-1)] (clamp to last). */\nfunction deriveActive(panels: string[], prevActive: string, removedIndex: number): string {\n\tif (panels.length === 0) return ''\n\tif (panels.includes(prevActive)) return prevActive\n\tconst idx = Math.min(removedIndex, panels.length - 1)\n\treturn panels[Math.max(0, idx)]!\n}\n\n/**\n * Collapse a (already child-normalized) node:\n * - tabgroup with 0 panels => null (drop)\n * - split: drop null children; if 0 left => null; if 1 left => that child;\n * else rebuild with repaired sizes.\n */\nfunction normalize(node: LayoutNode): LayoutNode | null {\n\tif (node.kind === 'tabs') {\n\t\treturn node.panels.length === 0 ? null : node\n\t}\n\t// split: normalize children first.\n\tconst kept: LayoutNode[] = []\n\tconst keptSizes: number[] = []\n\tconst keptConstraints: (SizeConstraint | null)[] = []\n\tconst hadConstraints = node.constraints !== undefined\n\tnode.children.forEach((child, i) => {\n\t\tconst n = normalize(child)\n\t\tif (n !== null) {\n\t\t\tkept.push(n)\n\t\t\tkeptSizes.push(node.sizes[i] ?? 1)\n\t\t\tif (hadConstraints) keptConstraints.push(node.constraints![i] ?? null)\n\t\t}\n\t})\n\tif (kept.length === 0) return null\n\tif (kept.length === 1) return kept[0]!\n\tconst rebuilt: SplitNode = { kind: 'split', id: node.id, orientation: node.orientation, children: kept, sizes: keptSizes }\n\tif (!hadConstraints) return rebuilt\n\t// M3 repair: dropping children can leave a multi-child split whose survivors\n\t// are ALL fixed-px (the sole flexible child was the one removed). That tree is\n\t// rejected by validateTree (rrp needs >= 1 weight-sized child). Deterministically\n\t// clear the LAST survivor's constraint so >= 1 flexible child remains.\n\tconst allFixed = keptConstraints.length > 0 && !keptConstraints.some(c => c === null)\n\tif (allFixed) {\n\t\tkeptConstraints[keptConstraints.length - 1] = null\n\t}\n\treturn { ...rebuilt, constraints: keptConstraints }\n}\n\n/** Apply collapse to the root, guaranteeing the result is a LayoutNode. A root\n * that collapses to nothing is illegal upstream (we never empty the last group\n * without a replacement), so we only reach here with a survivor. */\nfunction normalizeRoot(root: LayoutNode): LayoutNode {\n\tconst n = normalize(root)\n\tif (n === null) {\n\t\tthrow new Error('mutation would empty the entire layout')\n\t}\n\treturn n\n}\n\ninterface Located {\n\tgroup: TabGroupNode\n}\n\nfunction findGroupContaining(root: LayoutNode, panelId: string): Located | null {\n\tlet found: TabGroupNode | null = null\n\tconst walk = (n: LayoutNode): void => {\n\t\tif (found) return\n\t\tif (n.kind === 'tabs') {\n\t\t\tif (n.panels.includes(panelId)) found = n\n\t\t}\n\t\telse {\n\t\t\tn.children.forEach(walk)\n\t\t}\n\t}\n\twalk(root)\n\treturn found ? { group: found } : null\n}\n\nfunction findGroupById(root: LayoutNode, groupId: string): TabGroupNode | null {\n\tlet found: TabGroupNode | null = null\n\tconst walk = (n: LayoutNode): void => {\n\t\tif (found) return\n\t\tif (n.kind === 'tabs') {\n\t\t\tif (n.id === groupId) found = n\n\t\t}\n\t\telse {\n\t\t\tn.children.forEach(walk)\n\t\t}\n\t}\n\twalk(root)\n\treturn found\n}\n\n/** Collect every panelId present anywhere in the tree. */\nfunction collectPanelIds(root: LayoutNode): Set<string> {\n\tconst out = new Set<string>()\n\tconst walk = (n: LayoutNode): void => {\n\t\tif (n.kind === 'tabs') {\n\t\t\tfor (const p of n.panels) out.add(p)\n\t\t}\n\t\telse {\n\t\t\tn.children.forEach(walk)\n\t\t}\n\t}\n\twalk(root)\n\treturn out\n}\n\n/** True if `panelId` exists in any tabgroup of the tree. */\nfunction hasPanel(root: LayoutNode, id: string): boolean {\n\treturn collectPanelIds(root).has(id)\n}\n\n/** Collect every node id present anywhere in the tree (splits + tabgroups). */\nfunction collectNodeIds(root: LayoutNode): Set<string> {\n\tconst out = new Set<string>()\n\tconst walk = (n: LayoutNode): void => {\n\t\tout.add(n.id)\n\t\tif (n.kind === 'split') n.children.forEach(walk)\n\t}\n\twalk(root)\n\treturn out\n}\n\n/**\n * Mint an id from `base` that is not already in `taken`. If `base` is free,\n * return it; otherwise append an incrementing `#N` suffix until free. Mutates\n * `taken` so successive calls in the same mutation can't collide with each other.\n */\nfunction freshId(taken: Set<string>, base: string): string {\n\tlet candidate = base\n\tlet n = 2\n\twhile (taken.has(candidate)) {\n\t\tcandidate = `${base}#${n}`\n\t\tn += 1\n\t}\n\ttaken.add(candidate)\n\treturn candidate\n}\n\nfunction findSplitById(root: LayoutNode, splitId: string): SplitNode | null {\n\tlet found: SplitNode | null = null\n\tconst walk = (n: LayoutNode): void => {\n\t\tif (found) return\n\t\tif (n.kind === 'split') {\n\t\t\tif (n.id === splitId) found = n\n\t\t\tn.children.forEach(walk)\n\t\t}\n\t}\n\twalk(root)\n\treturn found\n}\n\n/**\n * Rebuild the tree, replacing the node with matching id by `replacement`\n * (which may be null to delete — handled by collapse). This produces a fresh\n * object graph (clone-on-write); untouched branches are still rebuilt so the\n * input is never shared.\n */\nfunction replaceNode(node: LayoutNode, targetId: string, replacement: LayoutNode): LayoutNode {\n\tif (node.id === targetId) return replacement\n\tif (node.kind === 'tabs') return tg(node.id, [...node.panels], node.active)\n\tconst out: SplitNode = {\n\t\tkind: 'split',\n\t\tid: node.id,\n\t\torientation: node.orientation,\n\t\tchildren: node.children.map(c => replaceNode(c, targetId, replacement)),\n\t\tsizes: [...node.sizes],\n\t}\n\treturn node.constraints !== undefined ? { ...out, constraints: [...node.constraints] } : out\n}\n\nfunction cloneNode(node: LayoutNode): LayoutNode {\n\tif (node.kind === 'tabs') return tg(node.id, [...node.panels], node.active)\n\tconst out: SplitNode = {\n\t\tkind: 'split',\n\t\tid: node.id,\n\t\torientation: node.orientation,\n\t\tchildren: node.children.map(cloneNode),\n\t\tsizes: [...node.sizes],\n\t}\n\treturn node.constraints !== undefined ? { ...out, constraints: [...node.constraints] } : out\n}\n\nfunction wrap(root: LayoutNode): LayoutTree {\n\treturn { version: 1, root }\n}\n\n// ───────────────────────── mutations ─────────────────────────\n\nexport function setSizes(t: LayoutTree, splitId: string, sizes: readonly number[]): LayoutTree {\n\tconst target = findSplitById(t.root, splitId)\n\tif (!target) throw new Error(`setSizes: split not found: ${splitId}`)\n\tif (sizes.length !== target.children.length) {\n\t\tthrow new Error(\n\t\t\t`setSizes: sizes length ${sizes.length} != children length ${target.children.length}`,\n\t\t)\n\t}\n\tif (!sizes.every(s => Number.isFinite(s))) {\n\t\tthrow new Error(`setSizes: every size must be a finite number, got [${sizes.join(', ')}]`)\n\t}\n\tconst replacement: SplitNode = {\n\t\tkind: 'split',\n\t\tid: target.id,\n\t\torientation: target.orientation,\n\t\tchildren: target.children.map(cloneNode),\n\t\tsizes: [...sizes],\n\t}\n\t// setSizes must NOT touch constraints — carry them through unchanged.\n\tconst withConstraints: SplitNode = target.constraints !== undefined\n\t\t? { ...replacement, constraints: [...target.constraints] }\n\t\t: replacement\n\treturn wrap(replaceNode(t.root, splitId, withConstraints))\n}\n\n/**\n * Set (or clear) the fixed-px constraint on child `childIndex` of split\n * `splitId`. Pure (clone-on-write). Lazily materializes an all-null constraints\n * array aligned with `children` when absent, then writes the slot. Clearing\n * (`constraint === null`) keeps the array even if all entries become null.\n */\nexport function setConstraint(\n\tt: LayoutTree,\n\tsplitId: string,\n\tchildIndex: number,\n\tconstraint: SizeConstraint | null,\n): LayoutTree {\n\tconst target = findSplitById(t.root, splitId)\n\tif (!target) throw new Error(`setConstraint: split not found: ${splitId}`)\n\tif (!Number.isInteger(childIndex)) {\n\t\tthrow new Error(`setConstraint: childIndex must be an integer, got ${childIndex}`)\n\t}\n\tif (childIndex < 0 || childIndex >= target.children.length) {\n\t\tthrow new Error(\n\t\t\t`setConstraint: childIndex ${childIndex} out of range [0, ${target.children.length})`,\n\t\t)\n\t}\n\tconst base: (SizeConstraint | null)[] = target.constraints !== undefined\n\t\t? [...target.constraints]\n\t\t: target.children.map(() => null)\n\tbase[childIndex] = constraint\n\t// M3 guard: never produce an all-FIXED-px split. If applying this constraint\n\t// would leave EVERY child `fixedPx`-locked (0 weight-sized children),\n\t// `validateTree`/`parseLayout` would later reject the serialized tree (rrp\n\t// requires >= 1 weight-sized child). You cannot fixedPx-pin the LAST flexible\n\t// child — treat it as a NO-OP. A `minPx` child is FLEXIBLE (weight-sized with a\n\t// floor), so it does NOT count toward \"all fixed\" — pinning a `minPx` floor is\n\t// always allowed.\n\tif (base.length > 0 && base.every(c => c !== null)) {\n\t\treturn t\n\t}\n\tconst replacement: SplitNode = {\n\t\tkind: 'split',\n\t\tid: target.id,\n\t\torientation: target.orientation,\n\t\tchildren: target.children.map(cloneNode),\n\t\tsizes: [...target.sizes],\n\t\tconstraints: base,\n\t}\n\treturn wrap(replaceNode(t.root, splitId, replacement))\n}\n\nexport function setActive(t: LayoutTree, groupId: string, panelId: string): LayoutTree {\n\tconst group = findGroupById(t.root, groupId)\n\tif (!group) throw new Error(`setActive: group not found: ${groupId}`)\n\tif (!group.panels.includes(panelId)) {\n\t\tthrow new Error(`setActive: panel ${panelId} not in group ${groupId}`)\n\t}\n\tconst replacement = tg(group.id, [...group.panels], panelId)\n\treturn wrap(replaceNode(t.root, groupId, replacement))\n}\n\n/** Remove a panel from whichever group holds it; collapse. */\nfunction removePanel(root: LayoutNode, panelId: string): LayoutNode {\n\tconst located = findGroupContaining(root, panelId)\n\tif (!located) throw new Error(`panel not found: ${panelId}`)\n\tconst { group } = located\n\tconst removedIndex = group.panels.indexOf(panelId)\n\tconst panels = group.panels.filter(p => p !== panelId)\n\tconst replacement = tg(group.id, panels, deriveActive(panels, group.active, removedIndex))\n\tconst replaced = replaceNode(root, group.id, replacement)\n\treturn normalizeRoot(replaced)\n}\n\nexport function closePanel(t: LayoutTree, panelId: string): LayoutTree {\n\t// Last-panel no-op guard: closing the SOLE panel in the whole tree would\n\t// empty the layout (normalizeRoot throws). Instead return the tree unchanged\n\t// so hosts can wire a close button without special-casing the final panel.\n\t// An UNKNOWN id is NOT \"the last panel\" — only short-circuit when the panel\n\t// actually EXISTS and is the only one; otherwise fall through to removePanel,\n\t// which throws \"panel not found\" for an unknown id (behavior preserved).\n\tconst ids = collectPanelIds(t.root)\n\tif (ids.size === 1 && ids.has(panelId)) return t\n\treturn wrap(removePanel(t.root, panelId))\n}\n\nexport function extractPanel(\n\tt: LayoutTree,\n\tpanelId: string,\n): { tree: LayoutTree; extracted: string } {\n\tconst located = findGroupContaining(t.root, panelId)\n\tif (!located) throw new Error(`extractPanel: panel not found: ${panelId}`)\n\treturn { tree: wrap(removePanel(t.root, panelId)), extracted: panelId }\n}\n\nexport function insertPanel(\n\tt: LayoutTree,\n\tpanelId: string,\n\tdest: { groupId: string; index?: number },\n): LayoutTree {\n\tif (hasPanel(t.root, panelId)) {\n\t\tthrow new Error(`insertPanel: panel already exists in the tree: ${panelId}`)\n\t}\n\tconst group = findGroupById(t.root, dest.groupId)\n\tif (!group) throw new Error(`insertPanel: dest group not found: ${dest.groupId}`)\n\tconst panels = [...group.panels]\n\tconst idx = clampInsertIndex(dest.index, panels.length)\n\tpanels.splice(idx, 0, panelId)\n\tconst replacement = tg(group.id, panels, group.active)\n\treturn wrap(normalizeRoot(replaceNode(t.root, group.id, replacement)))\n}\n\nfunction clampInsertIndex(index: number | undefined, len: number): number {\n\tif (index === undefined) return len\n\tif (index < 0) return 0\n\tif (index > len) return len\n\treturn index\n}\n\nexport function movePanel(\n\tt: LayoutTree,\n\tpanelId: string,\n\tdest: { groupId: string; index?: number },\n): LayoutTree {\n\tconst src = findGroupContaining(t.root, panelId)\n\tif (!src) throw new Error(`movePanel: panel not found: ${panelId}`)\n\tconst destGroup = findGroupById(t.root, dest.groupId)\n\tif (!destGroup) throw new Error(`movePanel: dest group not found: ${dest.groupId}`)\n\n\tif (src.group.id === destGroup.id) {\n\t\t// Same-group reorder: remove then re-insert at the clamped index.\n\t\tconst without = src.group.panels.filter(p => p !== panelId)\n\t\tconst idx = clampInsertIndex(dest.index, without.length)\n\t\tconst panels = [...without]\n\t\tpanels.splice(idx, 0, panelId)\n\t\tconst replacement = tg(src.group.id, panels, src.group.active)\n\t\treturn wrap(normalizeRoot(replaceNode(t.root, src.group.id, replacement)))\n\t}\n\n\t// Cross-group: remove from source (re-derive active + maybe collapse), then\n\t// insert into dest. Compute the destination panels against the ORIGINAL dest\n\t// group so the clamp matches its pre-move length.\n\tconst removedIndex = src.group.panels.indexOf(panelId)\n\tconst srcPanels = src.group.panels.filter(p => p !== panelId)\n\tconst srcReplacement = tg(src.group.id, srcPanels, deriveActive(srcPanels, src.group.active, removedIndex))\n\n\tconst destPanels = [...destGroup.panels]\n\tconst idx = clampInsertIndex(dest.index, destPanels.length)\n\tdestPanels.splice(idx, 0, panelId)\n\tconst destReplacement = tg(destGroup.id, destPanels, destGroup.active)\n\n\tlet root = replaceNode(t.root, src.group.id, srcReplacement)\n\troot = replaceNode(root, destGroup.id, destReplacement)\n\treturn wrap(normalizeRoot(root))\n}\n\nexport function splitPanel(\n\tt: LayoutTree,\n\tatPanelId: string,\n\tdir: Orientation,\n\tnewPanelId: string,\n\tside: 'before' | 'after',\n): LayoutTree {\n\tif (hasPanel(t.root, newPanelId)) {\n\t\tthrow new Error(`splitPanel: new panel already exists in the tree: ${newPanelId}`)\n\t}\n\tconst located = findGroupContaining(t.root, atPanelId)\n\tif (!located) throw new Error(`splitPanel: panel not found: ${atPanelId}`)\n\tconst { group } = located\n\n\t// Generated node ids must be unique within the current tree: a node literally\n\t// named `${origGroupId}__sp` could already exist and produce a duplicate-id\n\t// tree. Mint collision-free ids off the deterministic bases.\n\tconst taken = collectNodeIds(t.root)\n\n\t// Extract atPanelId into its own new group.\n\tconst origGroupId = group.id\n\tconst extractedGroup = tg(freshId(taken, `${origGroupId}__split`), [atPanelId], atPanelId)\n\tconst newGroup = tg(freshId(taken, `${origGroupId}__new`), [newPanelId], newPanelId)\n\n\tconst children: LayoutNode[] = side === 'after'\n\t\t? [extractedGroup, newGroup]\n\t\t: [newGroup, extractedGroup]\n\tconst newSplit: SplitNode = {\n\t\tkind: 'split',\n\t\tid: freshId(taken, `${origGroupId}__sp`),\n\t\torientation: dir,\n\t\tchildren,\n\t\tsizes: [1, 1],\n\t}\n\n\tif (group.panels.length === 1) {\n\t\t// Replace the whole group with the new split.\n\t\treturn wrap(normalizeRoot(replaceNode(t.root, origGroupId, newSplit)))\n\t}\n\n\t// Multi-panel group: pull atPanelId out, keep the rest in the orig group,\n\t// and place the new split where the orig group was... but the orig group must\n\t// survive too. So: replace orig group with a split [remainingGroup, newSplit]?\n\t// The contract only requires: atPanel + newPanel live in different groups, and\n\t// the orig group keeps the remaining panels. Replace orig group with a split\n\t// of [origRemaining, newSplit] using `dir` orientation.\n\tconst removedIndex = group.panels.indexOf(atPanelId)\n\tconst remaining = group.panels.filter(p => p !== atPanelId)\n\tconst origRemainingGroup = tg(origGroupId, remaining, deriveActive(remaining, group.active, removedIndex))\n\n\tconst outerChildren: LayoutNode[] = side === 'after'\n\t\t? [origRemainingGroup, newSplit]\n\t\t: [newSplit, origRemainingGroup]\n\tconst outerSplit: SplitNode = {\n\t\tkind: 'split',\n\t\tid: freshId(taken, `${origGroupId}__outer`),\n\t\torientation: dir,\n\t\tchildren: outerChildren,\n\t\tsizes: [1, 1],\n\t}\n\treturn wrap(normalizeRoot(replaceNode(t.root, origGroupId, outerSplit)))\n}\n","/**\n * Observable layout model — single-writer, synchronous, call-ordered.\n *\n * INV-3: mutations assume an already-validated (acyclic, integrity-checked) tree\n * — callers feeding hand-built trees should parseLayout/validateTree first.\n *\n * Conventions (pinned by model.test.ts):\n * - revision starts at 0 for the initial tree.\n * - subscribe() does NOT replay; first emission is on the first successful\n * apply, carrying revision 1.\n * - +1 per successful apply; a throwing mutation does NOT advance revision and\n * does NOT notify (the throw propagates to the caller).\n */\nimport type { LayoutModel, LayoutSnapshot, LayoutTree } from './types.js'\n\nexport function createLayoutModel(initial: LayoutTree): LayoutModel {\n\t// Own the initial tree: take a structural snapshot so a later external mutation\n\t// of the caller's `initial` object can't leak into model state. The tree is\n\t// readonly-typed / treated as immutable, so a deep clone is the cheapest\n\t// correct ownership stance. (The `next` from each apply is already a fresh tree\n\t// produced by the pure mutations, so it needs no further clone.)\n\tlet current: LayoutTree = structuredClone(initial)\n\tlet revision = 0\n\tconst subscribers = new Set<(snap: LayoutSnapshot) => void>()\n\n\t// Re-entrancy guard: a subscriber may call apply() during notification. We\n\t// must not re-enter and bump `revision` mid-delivery (subscribers would then\n\t// observe a non-monotonic sequence). Instead, enqueue and drain FIFO so each\n\t// apply fully commits + notifies before the next begins.\n\tlet applying = false\n\tconst queue: ((t: LayoutTree) => LayoutTree)[] = []\n\n\tconst commitAndNotify = (mut: (t: LayoutTree) => LayoutTree): void => {\n\t\t// Compute first; if it throws, nothing below runs — revision stays put\n\t\t// and no subscriber is notified.\n\t\tconst next = mut(current)\n\t\tcurrent = next\n\t\trevision += 1\n\t\tconst snap: LayoutSnapshot = { tree: current, revision }\n\t\t// Snapshot the subscriber set so unsubscribe during delivery is safe.\n\t\t// Isolate subscriber errors: a throw from one must not abort delivery to\n\t\t// the rest, nor make apply() throw after a committed state. The model's\n\t\t// job is delivery, not subscriber correctness — swallow and continue.\n\t\tfor (const fn of [...subscribers]) {\n\t\t\ttry {\n\t\t\t\tfn(snap)\n\t\t\t}\n\t\t\tcatch {\n\t\t\t\t// intentionally swallowed — see above.\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tget(): LayoutTree {\n\t\t\treturn current\n\t\t},\n\t\t/**\n\t\t * `mut` MUST be pure (clone-on-write); the built-in mutations satisfy this.\n\t\t */\n\t\tapply(mut: (t: LayoutTree) => LayoutTree): void {\n\t\t\tif (applying) {\n\t\t\t\t// Re-entrant call (a subscriber applied during notification): queue it\n\t\t\t\t// and let the in-flight drain pass process it after the current pass.\n\t\t\t\tqueue.push(mut)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tapplying = true\n\t\t\ttry {\n\t\t\t\t// Top-level (non-re-entrant) apply: a throw here propagates to the\n\t\t\t\t// caller and does NOT advance revision / notify. (`finally` below\n\t\t\t\t// still resets `applying`, so a throw can't wedge the model.)\n\t\t\t\tcommitAndNotify(mut)\n\t\t\t\t// Drain re-entrant mutations enqueued during notification. Each queued\n\t\t\t\t// apply was deferred and is therefore fire-and-forget: its original\n\t\t\t\t// caller already returned, so it cannot deliver a synchronous throw\n\t\t\t\t// back. Run each as an INDEPENDENT transaction — a throw skips that\n\t\t\t\t// item (no revision bump, no notify) and we continue draining the rest\n\t\t\t\t// in FIFO order. This keeps a thrown queued mut from bubbling into the\n\t\t\t\t// already-committed outer apply() and from disrupting the order /\n\t\t\t\t// processing of later queued items.\n\t\t\t\twhile (queue.length > 0) {\n\t\t\t\t\tconst queuedMut = queue.shift()!\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcommitAndNotify(queuedMut)\n\t\t\t\t\t}\n\t\t\t\t\tcatch {\n\t\t\t\t\t\t// Deferred apply: swallow + continue — see above.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tapplying = false\n\t\t\t}\n\t\t},\n\t\tsubscribe(fn: (snap: LayoutSnapshot) => void): () => void {\n\t\t\tsubscribers.add(fn)\n\t\t\treturn (): void => {\n\t\t\t\tsubscribers.delete(fn)\n\t\t\t}\n\t\t},\n\t}\n}\n","/**\n * Capability-aware layout operations for user-triggered interactions.\n *\n * Keep these separate from the pure mutations: callers may still use\n * `closePanel` for restore/migration/programmatic layout transforms, while UI\n * entry points consistently honor registry capabilities.\n */\nimport { closePanel } from './mutations.js'\nimport type { LayoutTree, PanelRegistry } from './types.js'\n\n/**\n * Close a panel only when its descriptor permits user closure.\n *\n * Missing descriptors and an omitted `closable` capability retain the historic\n * close behavior. Only an explicit `closable:false` blocks the operation.\n */\nexport function closePanelForUser(\n\ttree: LayoutTree,\n\tpanelId: string,\n\tregistry: PanelRegistry,\n): LayoutTree {\n\treturn registry.get(panelId)?.closable === false ? tree : closePanel(tree, panelId)\n}\n","// Level-triggered reconciler that converges a host's native-view mount state\n// toward a renderer-declared desired placement. The renderer is the single\n// source of truth: it publishes a window-level snapshot (one monotonic epoch\n// per commit tick, one generation per renderer lifetime); this pure core diffs\n// the snapshot against the last-applied actual state and emits an ordered op\n// list. A lost or spurious per-view edge is self-correcting because every\n// reconcile re-derives the whole actual state from the desired snapshot, so the\n// worst case degrades from a stuck view to a one-tick flicker.\n//\n// Domain-neutral: view ids are opaque strings and per-view host specifics ride\n// on the `Extra` type parameter (e.g. a simulator's zoom), so the same core\n// serves any electron-deck host. Side-effect free — it only computes ops; a\n// thin host executor applies them. See devtools docs/view-placement-reconciler.md.\n\nimport type { Bounds, Placement } from '@dimina-kit/view-anchor'\n\nexport type { Bounds, Placement }\n\nexport interface DesiredView<Extra = unknown> {\n viewId: string\n placement: Placement\n // z-order; larger paints on top.\n layer: number\n // Host-specific extras carried through to setBounds (e.g. simulator zoom).\n extra?: Extra\n}\n\nexport interface PlacementSnapshot<Extra = unknown> {\n // Renderer lifetime id; a higher generation resets the whole table.\n generation: number\n // Window-level monotonic tick; all views in one commit share one epoch.\n epoch: number\n // The full desired table for this tick (a level, not a delta).\n views: DesiredView<Extra>[]\n}\n\nexport type ViewOp<Extra = unknown> =\n | { kind: 'setBounds'; viewId: string; bounds: Bounds; extra?: Extra }\n | { kind: 'attach'; viewId: string }\n | { kind: 'setVisible'; viewId: string; visible: boolean }\n | { kind: 'detach'; viewId: string }\n | { kind: 'reorder'; order: string[] }\n\nexport interface ActualView<Extra = unknown> {\n attached: boolean\n visible: boolean\n bounds?: Bounds\n extra?: Extra\n}\n\nexport interface ReconcilerState<Extra = unknown> {\n generation: number\n lastEpoch: number\n desired: Map<string, DesiredView<Extra>>\n actual: Map<string, ActualView<Extra>>\n}\n\nexport function createInitialState<Extra = unknown>(): ReconcilerState<Extra> {\n return {\n generation: 0,\n lastEpoch: -1,\n desired: new Map(),\n actual: new Map(),\n }\n}\n\n// Integer-snap so sub-pixel jitter (100.4 vs 100.6) never emits a setBounds op.\nfunction roundBounds(b: Bounds): Bounds {\n return {\n x: Math.round(b.x),\n y: Math.round(b.y),\n width: Math.round(b.width),\n height: Math.round(b.height),\n }\n}\n\nfunction sameBounds(a: Bounds | undefined, b: Bounds | undefined): boolean {\n if (a === undefined || b === undefined) return a === b\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height\n}\n\nfunction sameExtra<Extra>(a: Extra | undefined, b: Extra | undefined): boolean {\n if (a === b) return true\n if (a === undefined || b === undefined) return false\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\nfunction visibleBounds<Extra>(dv: DesiredView<Extra>): Bounds {\n // Caller guarantees dv.placement.visible === true.\n return roundBounds((dv.placement as { visible: true; bounds: Bounds }).bounds)\n}\n\nfunction setBoundsOp<Extra>(id: string, dv: DesiredView<Extra>): ViewOp<Extra> {\n return {\n kind: 'setBounds',\n viewId: id,\n bounds: visibleBounds(dv),\n ...(dv.extra !== undefined ? { extra: dv.extra } : {}),\n }\n}\n\n// Attached views ordered bottom→top by layer; id is a stable tie-break so a\n// same-layer set never reorders spuriously.\nfunction computeOrder<Extra>(\n actual: Map<string, ActualView<Extra>>,\n desired: Map<string, DesiredView<Extra>>,\n): string[] {\n const attached = [...actual.entries()]\n .filter(([, a]) => a.attached)\n .map(([id]) => id)\n attached.sort((id1, id2) => {\n const l1 = desired.get(id1)?.layer ?? 0\n const l2 = desired.get(id2)?.layer ?? 0\n if (l1 !== l2) return l1 - l2\n return id1 < id2 ? -1 : id1 > id2 ? 1 : 0\n })\n return attached\n}\n\n// A view declared in the snapshot no longer being present means it must be\n// removed: detach any still-attached actual whose id left the desired table.\nfunction scanDetached<Extra>(\n actual: Map<string, ActualView<Extra>>,\n desired: Map<string, DesiredView<Extra>>,\n ops: ViewOp<Extra>[],\n): boolean {\n let attachSetChanged = false\n for (const id of [...actual.keys()]) {\n if (desired.has(id)) continue\n if (actual.get(id)?.attached) {\n ops.push({ kind: 'detach', viewId: id })\n attachSetChanged = true\n }\n actual.delete(id)\n }\n return attachSetChanged\n}\n\ninterface Buckets {\n hides: string[]\n shows: string[]\n restores: string[]\n updates: string[]\n}\n\n// Classify one desired view against the current actual, WRITE its next actual,\n// and record which op bucket(s) it lands in. Returns whether it newly attached\n// (which changes the attach set → forces a reorder).\nfunction classifyView<Extra>(\n id: string,\n dv: DesiredView<Extra>,\n actual: Map<string, ActualView<Extra>>,\n buckets: Buckets,\n): boolean {\n const a = actual.get(id)\n if (!dv.placement.visible) {\n if (a?.attached && a.visible) buckets.hides.push(id)\n actual.set(id, { attached: a?.attached ?? false, visible: false, bounds: a?.bounds, extra: a?.extra })\n return false\n }\n const bounds = visibleBounds(dv)\n if (!a || !a.attached) {\n buckets.shows.push(id)\n actual.set(id, { attached: true, visible: true, bounds, extra: dv.extra })\n return true\n }\n if (!a.visible) buckets.restores.push(id)\n if (!sameBounds(a.bounds, bounds) || !sameExtra(a.extra, dv.extra)) buckets.updates.push(id)\n actual.set(id, { attached: true, visible: true, bounds, extra: dv.extra })\n return false\n}\n\n// Fixed op order avoids attach-then-resize / squashed-toolbar flicker:\n// detach → hide → (setBounds→attach per new view) → restore visibility →\n// update bounds of already-visible views → one reorder if the attach set moved.\nfunction emitOps<Extra>(\n desired: Map<string, DesiredView<Extra>>,\n actual: Map<string, ActualView<Extra>>,\n buckets: Buckets,\n ops: ViewOp<Extra>[],\n attachSetChanged: boolean,\n): void {\n for (const id of buckets.hides) ops.push({ kind: 'setVisible', viewId: id, visible: false })\n buckets.shows.sort((a, b) => (desired.get(a)?.layer ?? 0) - (desired.get(b)?.layer ?? 0))\n for (const id of buckets.shows) {\n ops.push(setBoundsOp(id, desired.get(id)!))\n ops.push({ kind: 'attach', viewId: id })\n }\n for (const id of buckets.restores) ops.push({ kind: 'setVisible', viewId: id, visible: true })\n for (const id of buckets.updates) ops.push(setBoundsOp(id, desired.get(id)!))\n if (attachSetChanged) ops.push({ kind: 'reorder', order: computeOrder(actual, desired) })\n}\n\nexport function reconcile<Extra = unknown>(\n prev: ReconcilerState<Extra>,\n snapshot: PlacementSnapshot<Extra>,\n): { state: ReconcilerState<Extra>; ops: ViewOp<Extra>[] } {\n // Reject a snapshot from an OLDER generation outright. Main assigns strictly\n // monotonic per-renderer generations, so a lower generation is a late in-flight\n // snapshot from a previous renderer lifetime; honoring it would let it poison\n // lastEpoch/actual AFTER a higher-generation reset already landed (e.g. a\n // pre-reload frame arriving on the same wc after reload bumped the generation).\n if (snapshot.generation < prev.generation) {\n return { state: { ...prev }, ops: [] }\n }\n\n // Reject a stale snapshot (same generation, epoch not advanced) without\n // touching desired/actual. New object so callers never alias prev.\n if (snapshot.generation === prev.generation && snapshot.epoch <= prev.lastEpoch) {\n return { state: { ...prev }, ops: [] }\n }\n\n // A higher generation is a fresh renderer: forget the old actual entirely so\n // stale ids can't linger, then rebuild from the snapshot.\n const resetGeneration = snapshot.generation > prev.generation\n const actual = new Map<string, ActualView<Extra>>()\n if (!resetGeneration) {\n for (const [id, a] of prev.actual) actual.set(id, { ...a })\n }\n\n const desired = new Map<string, DesiredView<Extra>>()\n for (const v of snapshot.views) desired.set(v.viewId, v)\n\n const ops: ViewOp<Extra>[] = []\n let attachSetChanged = scanDetached(actual, desired, ops)\n\n const buckets: Buckets = { hides: [], shows: [], restores: [], updates: [] }\n for (const [id, dv] of desired) {\n if (classifyView(id, dv, actual, buckets)) attachSetChanged = true\n }\n\n emitOps(desired, actual, buckets, ops, attachSetChanged)\n\n return {\n state: {\n generation: snapshot.generation,\n lastEpoch: snapshot.epoch,\n desired,\n actual,\n },\n ops,\n }\n}\n","// Main-side glue between an untrusted renderer snapshot and the level-triggered\n// reconcile core. Two pure steps, each independently testable:\n//\n// 1. cleanSnapshot(raw, authorize) — validate + AUTHORIZE a raw wire payload\n// into a trusted CleanSnapshot (or reject it whole). The renderer publishes\n// a window-level table keyed by opaque slot tokens; this derives each view's\n// real identity from the token registry (never trusting the renderer-reported\n// viewId) and drops anything it can't authorize.\n//\n// 2. dispatchOps(ops, state, resolveApply) — collapse the reconciler's rich op\n// stream onto a host whose per-view sink is the two-state\n// `applyPlacement({visible:true,bounds} | {visible:false})` (an electron-deck\n// ViewHandle). z-order is compositor zone-fixed here, so reorder ops are\n// skipped; final geometry is read from the reconciled `actual` so a bare\n// restore (visibility flipped, bounds unchanged) still carries its bounds.\n//\n// Domain note: this file IS electron-deck-specific (it knows the ViewHandle sink\n// shape), unlike the domain-neutral reconcile core it sits on top of.\n\nimport type { Bounds, Placement, ReconcilerState, ViewOp } from './placement-reconcile.js'\n\nexport interface CleanView {\n viewId: string\n placement: Placement\n layer: number\n}\n\nexport interface CleanSnapshot {\n generation: number\n epoch: number\n views: CleanView[]\n}\n\n// Resolve a renderer-supplied slot token to the identity the host granted it.\n// Returns null when the token is unknown OR not authorized to this sender — the\n// caller builds an authorizer that has already bound the check to a senderId, so\n// anti-spoof (a valid token replayed by a different wc) lives here as a null.\nexport type Authorizer = (slotToken: string) => { viewId: string; layer: number } | null\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === 'number' && Number.isFinite(v)\n}\n\nfunction isValidBounds(v: unknown): v is Bounds {\n if (v === null || typeof v !== 'object') return false\n const b = v as Record<string, unknown>\n // x/y may be ANY finite number (a negative origin is legitimate scroll-follow);\n // width/height must be finite AND non-negative (0 is a valid empty view; a\n // negative extent is garbage).\n return (\n isFiniteNumber(b.x) &&\n isFiniteNumber(b.y) &&\n isFiniteNumber(b.width) && b.width >= 0 &&\n isFiniteNumber(b.height) && b.height >= 0\n )\n}\n\nfunction isValidPlacement(v: unknown): v is Placement {\n if (v === null || typeof v !== 'object') return false\n const p = v as Record<string, unknown>\n if (p.visible === true) return isValidBounds(p.bounds)\n if (p.visible === false) return true\n return false\n}\n\nfunction isNonNegInt(v: unknown): v is number {\n return typeof v === 'number' && Number.isInteger(v) && v >= 0\n}\n\n// Read a view's slot token from the raw wire shape. The renderer stamps it into\n// `extra.slotToken` (the reconcile core's per-view Extra channel).\nfunction readSlotToken(rawView: Record<string, unknown>): string | null {\n const extra = rawView.extra\n if (extra === null || typeof extra !== 'object') return null\n const token = (extra as Record<string, unknown>).slotToken\n return typeof token === 'string' && token.length > 0 ? token : null\n}\n\n/**\n * Validate + authorize a raw renderer snapshot into a trusted CleanSnapshot.\n *\n * Returns null (REJECT — caller must NOT reconcile) when:\n * - the payload is malformed (missing/!int generation/epoch, views not an array);\n * - `views` is NON-EMPTY but every entry is dropped by authorization/validation\n * — a fully-rejected snapshot must never be read as \"the renderer wants\n * everything detached\" (that would tear down live views on a transient where\n * tokens momentarily fail to resolve).\n *\n * Returns an EMPTY CleanSnapshot (not null) when `views` is genuinely empty — the\n * renderer removed all its views, a legitimate signal the caller reconciles into\n * detach ops.\n *\n * Each surviving view takes its `viewId`/`layer` from the AUTHORIZER, never from\n * the renderer-reported fields (a valid token must not be splice-able onto a\n * forged viewId to poison the reconciler key). Duplicate viewIds keep the first.\n */\n// Authorize + validate ONE raw view into a CleanView, or null to drop it (not an\n// object, no/unknown/unauthorized token, malformed placement, or a duplicate\n// viewId already seen). Kept separate so cleanSnapshot stays a simple collect loop.\nfunction cleanOneView(rawView: unknown, authorize: Authorizer, seen: Set<string>): CleanView | null {\n if (rawView === null || typeof rawView !== 'object' || Array.isArray(rawView)) return null\n const rv = rawView as Record<string, unknown>\n const token = readSlotToken(rv)\n if (token === null) return null\n const grant = authorize(token)\n if (grant === null) return null // unknown / unauthorized token\n if (!isValidPlacement(rv.placement)) return null\n if (seen.has(grant.viewId)) return null // duplicate viewId → keep the first\n seen.add(grant.viewId)\n return { viewId: grant.viewId, placement: rv.placement, layer: grant.layer }\n}\n\nexport function cleanSnapshot(raw: unknown, authorize: Authorizer): CleanSnapshot | null {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return null\n const snap = raw as Record<string, unknown>\n if (!isNonNegInt(snap.generation) || !isNonNegInt(snap.epoch)) return null\n if (!Array.isArray(snap.views)) return null\n\n const rawViews = snap.views\n const views: CleanView[] = []\n const seen = new Set<string>()\n for (const rawView of rawViews) {\n const cleaned = cleanOneView(rawView, authorize, seen)\n if (cleaned) views.push(cleaned)\n }\n\n // A non-empty raw that fully fails authorization is rejected wholesale.\n if (rawViews.length > 0 && views.length === 0) return null\n\n return { generation: snap.generation, epoch: snap.epoch, views }\n}\n\n/**\n * Collapse reconcile ops onto a two-state per-view sink. Gathers the viewIds any\n * op touched (reorder excluded — deck z-order is compositor zone-fixed), then for\n * each reads the reconciled end-state from `state.actual`: attached+visible+bounds\n * → apply({visible:true,bounds}); otherwise apply({visible:false}). Reading from\n * `actual` (not the op itself) means a bare restore still carries its bounds.\n *\n * Each apply runs in its own try/catch: one throwing sink (e.g. a destroyed native\n * view) must not abort the rest of the dispatch.\n */\nexport function dispatchOps(\n ops: ViewOp[],\n state: ReconcilerState,\n resolveApply: (viewId: string) => ((p: Placement) => void) | null,\n): void {\n const touched = new Set<string>()\n for (const op of ops) {\n if (op.kind === 'reorder') continue\n touched.add(op.viewId)\n }\n for (const viewId of touched) {\n const apply = resolveApply(viewId)\n if (!apply) continue\n const a = state.actual.get(viewId)\n const placement: Placement =\n a && a.attached && a.visible && a.bounds\n ? { visible: true, bounds: a.bounds }\n : { visible: false }\n try {\n apply(placement)\n } catch (err) {\n console.error(`[electron-deck] applyPlacement for view \"${viewId}\" threw:`, err)\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/main/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as createLogger, c as toDisposable, i as createScope, n as CommitError, o as setLogLevel, r as createCompositor, s as DisposableRegistry, t as createViewHandle } from "../view-handle-
|
|
1
|
+
import { a as createLogger, c as toDisposable, i as createScope, n as CommitError, o as setLogLevel, r as createCompositor, s as DisposableRegistry, t as createViewHandle } from "../view-handle-DDhpBuW-.js";
|
|
2
2
|
//#region src/main/connection.ts
|
|
3
3
|
var log = createLogger("connection");
|
|
4
4
|
var NOOP_DISPOSABLE = { dispose() {} };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-handle.d.ts","sourceRoot":"","sources":["../../src/main/view-handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEhE;iFACiF;AACjF,MAAM,WAAW,MAAM;IACrB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,CAAA;AAE9E;;;;;;;;;kDASkD;AAClD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAA;IAC3B,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,OAAO,CAAC,IAAI,IAAI,CAAA;IAChB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,WAAW,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CACjC;AAED;yEACyE;AACzE,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,CAAA;IACtB,WAAW,EAAE,KAAK,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB;+EAC2E;IAC3E,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,CAAA;IACjE;;uEAEmE;IACnE,cAAc,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnF;qEACiE;IACjE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB;+EAC2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B;;gFAE4E;IAC5E,MAAM,IAAI,MAAM,GAAG,IAAI,CAAA;IACvB,oEAAoE;IACpE,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,KAAK,EAAE,KAAK,CAAA;IACZ;;;;qFAIiF;IACjF,SAAS,CAAC,IAAI,IAAI,CAAA;CACnB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"view-handle.d.ts","sourceRoot":"","sources":["../../src/main/view-handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEhE;iFACiF;AACjF,MAAM,WAAW,MAAM;IACrB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,CAAA;AAE9E;;;;;;;;;kDASkD;AAClD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAA;IAC3B,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,OAAO,CAAC,IAAI,IAAI,CAAA;IAChB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,WAAW,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CACjC;AAED;yEACyE;AACzE,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,CAAA;IACtB,WAAW,EAAE,KAAK,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB;+EAC2E;IAC3E,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,CAAA;IACjE;;uEAEmE;IACnE,cAAc,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnF;qEACiE;IACjE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB;+EAC2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B;;gFAE4E;IAC5E,MAAM,IAAI,MAAM,GAAG,IAAI,CAAA;IACvB,oEAAoE;IACpE,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,KAAK,EAAE,KAAK,CAAA;IACZ;;;;qFAIiF;IACjF,SAAS,CAAC,IAAI,IAAI,CAAA;CACnB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAwUjE"}
|
package/dist/preload/index.cjs
CHANGED
|
@@ -15,7 +15,7 @@ var DeckChannel = {
|
|
|
15
15
|
Invoke: "__electron-deck:invoke",
|
|
16
16
|
Event: "__electron-deck:event",
|
|
17
17
|
Probe: "__electron-deck:probe",
|
|
18
|
-
|
|
18
|
+
Snapshot: "__electron-deck:snapshot",
|
|
19
19
|
SlotGrant: "__electron-deck:slot-grant",
|
|
20
20
|
LayoutSubscribe: "__electron-deck:layout-subscribe"
|
|
21
21
|
};
|
|
@@ -59,7 +59,7 @@ function exposeDeckBridge(options) {
|
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* 在 host preload 内调用,把三条 slot-token LAYOUT channel(`slot-grant` PUSH /
|
|
62
|
-
* `
|
|
62
|
+
* `snapshot` send / `layout-subscribe` invoke)封装成一个 `LayoutBridge`-shaped
|
|
63
63
|
* 对象暴露到 webview window,供 renderer:
|
|
64
64
|
*
|
|
65
65
|
* ```ts
|
|
@@ -86,8 +86,8 @@ function exposeDeckLayoutBridge(options) {
|
|
|
86
86
|
electron.ipcRenderer.removeListener(DeckChannel.SlotGrant, listener);
|
|
87
87
|
};
|
|
88
88
|
},
|
|
89
|
-
|
|
90
|
-
electron.ipcRenderer.invoke(DeckChannel.
|
|
89
|
+
sendSnapshot(snapshot) {
|
|
90
|
+
electron.ipcRenderer.invoke(DeckChannel.Snapshot, snapshot).catch(() => {});
|
|
91
91
|
},
|
|
92
92
|
subscribe() {
|
|
93
93
|
electron.ipcRenderer.invoke(DeckChannel.LayoutSubscribe).catch(() => {});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/shared/protocol.ts","../../src/preload/index.ts"],"sourcesContent":["/**\n * Deck framework wire protocol —— main ↔ webview 之间的 channel 名 + 帧\n * 形态。SoT 在此,preload / client / main runtime 都从这里 import。\n *\n * 设计:把 declared `hostServices` / `simulatorApis` / `events` 三类全部走\n * **两个** 统一 channel,避免 channel name 爆炸 / 难以加 senderPolicy 白名单。\n * - `__electron-deck:invoke` — webview → main RPC (ipcRenderer.invoke)\n * - `__electron-deck:event` — main → webview event push (webContents.send)\n * - `__electron-deck:probe` — webview → main 探活,bridge ready 检查\n *\n * 帧用 JSON 对象一层 envelope,便于扩展 + 校验。\n *\n * @internal\n */\n\nimport type { JsonValue } from '../types.js'\n\n/** Bridge global 默认挂的全局名(contextBridge.exposeInMainWorld) */\nexport const DEFAULT_BRIDGE_GLOBAL = '__electronDeckBridge'\n\n/**\n * Slot-token LAYOUT bridge 默认挂的全局名。`exposeDeckLayoutBridge()` 默认暴露到\n * 此名,renderer 的 `createDeckLayoutClient({ bridge: window.__electronDeckLayoutBridge })`\n * 读取同名。单一来源避免 preload helper 与 client 之间字符串漂移。\n */\nexport const DEFAULT_LAYOUT_BRIDGE_GLOBAL = '__electronDeckLayoutBridge'\n\n/** Bridge protocol semver;client 在 ready() 时校验 major 一致 */\nexport const BRIDGE_PROTOCOL_VERSION = '1.0.0'\n\nexport const DeckChannel = {\n\tInvoke: '__electron-deck:invoke',\n\tEvent: '__electron-deck:event',\n\tProbe: '__electron-deck:probe',\n\
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/shared/protocol.ts","../../src/preload/index.ts"],"sourcesContent":["/**\n * Deck framework wire protocol —— main ↔ webview 之间的 channel 名 + 帧\n * 形态。SoT 在此,preload / client / main runtime 都从这里 import。\n *\n * 设计:把 declared `hostServices` / `simulatorApis` / `events` 三类全部走\n * **两个** 统一 channel,避免 channel name 爆炸 / 难以加 senderPolicy 白名单。\n * - `__electron-deck:invoke` — webview → main RPC (ipcRenderer.invoke)\n * - `__electron-deck:event` — main → webview event push (webContents.send)\n * - `__electron-deck:probe` — webview → main 探活,bridge ready 检查\n *\n * 帧用 JSON 对象一层 envelope,便于扩展 + 校验。\n *\n * @internal\n */\n\nimport type { JsonValue } from '../types.js'\n\n/** Bridge global 默认挂的全局名(contextBridge.exposeInMainWorld) */\nexport const DEFAULT_BRIDGE_GLOBAL = '__electronDeckBridge'\n\n/**\n * Slot-token LAYOUT bridge 默认挂的全局名。`exposeDeckLayoutBridge()` 默认暴露到\n * 此名,renderer 的 `createDeckLayoutClient({ bridge: window.__electronDeckLayoutBridge })`\n * 读取同名。单一来源避免 preload helper 与 client 之间字符串漂移。\n */\nexport const DEFAULT_LAYOUT_BRIDGE_GLOBAL = '__electronDeckLayoutBridge'\n\n/** Bridge protocol semver;client 在 ready() 时校验 major 一致 */\nexport const BRIDGE_PROTOCOL_VERSION = '1.0.0'\n\nexport const DeckChannel = {\n\tInvoke: '__electron-deck:invoke',\n\tEvent: '__electron-deck:event',\n\tProbe: '__electron-deck:probe',\n\tSnapshot: '__electron-deck:snapshot',\n\tSlotGrant: '__electron-deck:slot-grant',\n\tLayoutSubscribe: '__electron-deck:layout-subscribe',\n} as const\n\nexport type InvokeKind = 'host' | 'simulator'\n\nexport interface InvokeRequest {\n\treadonly kind: InvokeKind\n\treadonly name: string\n\treadonly args: readonly JsonValue[]\n}\n\nexport interface InvokeSuccess<R extends JsonValue = JsonValue> {\n\treadonly ok: true\n\treadonly result: R\n}\n\nexport interface InvokeFailure {\n\treadonly ok: false\n\treadonly error: {\n\t\treadonly remoteName: string\n\t\treadonly message: string\n\t\treadonly code?: string\n\t}\n}\n\nexport type InvokeResponse<R extends JsonValue = JsonValue> =\n\t| InvokeSuccess<R>\n\t| InvokeFailure\n\nexport interface EventEnvelope<P extends JsonValue = JsonValue> {\n\treadonly name: string\n\treadonly payload: P\n}\n\nexport interface ProbeResponse {\n\treadonly ready: true\n\treadonly version: typeof BRIDGE_PROTOCOL_VERSION\n}\n\n/**\n * Bridge global 暴露到 webview window 的 shape。preload 把它通过\n * `contextBridge.exposeInMainWorld(globalName, bridge)` 注入;webview-side\n * `createDeckClient()` 通过 `globalThis[globalName]` 读取。\n *\n * 注意所有方法必须是 contextBridge-friendly(plain values + serializable\n * arguments),不要把 Map / Set / Date / Promise.race 等 leak 进 bridge 接口。\n */\nexport interface DeckBridge {\n\treadonly version: typeof BRIDGE_PROTOCOL_VERSION\n\tprobe(): Promise<ProbeResponse>\n\tinvoke(req: InvokeRequest): Promise<InvokeResponse>\n\t/** 订阅 event channel;返回 unsubscribe 函数(不是 Disposable,因为要跨 contextBridge) */\n\tonEvent(listener: (env: EventEnvelope) => void): () => void\n}\n","import { contextBridge, ipcRenderer } from 'electron'\nimport {\n\tBRIDGE_PROTOCOL_VERSION,\n\tDEFAULT_BRIDGE_GLOBAL,\n\tDEFAULT_LAYOUT_BRIDGE_GLOBAL,\n\tDeckChannel,\n} from '../shared/protocol.js'\nimport type {\n\tEventEnvelope,\n\tInvokeRequest,\n\tInvokeResponse,\n\tProbeResponse,\n\tDeckBridge,\n} from '../shared/protocol.js'\nimport type { LayoutBridge, SlotGrant } from '../client/layout-client.js'\n\nexport interface ExposeBridgeOptions {\n\t/** 暴露到 window 的全局名,默认 `__electronDeckBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用,把 framework typed RPC + event push bridge 暴露到\n * webview window:\n *\n * ```ts\n * import { contextBridge, ipcRenderer } from 'electron'\n * import { exposeDeckBridge } from '@dimina-kit/electron-deck/preload'\n * exposeDeckBridge()\n * ```\n *\n * 见 `DeckBridge` 接口(`shared/protocol.ts`)。\n */\nexport function exposeDeckBridge(options?: ExposeBridgeOptions): void {\n\tif (typeof contextBridge?.exposeInMainWorld !== 'function' || typeof ipcRenderer?.invoke !== 'function') {\n\t\tthrow new Error('exposeDeckBridge: must be called from a preload script (electron contextBridge / ipcRenderer unavailable)')\n\t}\n\n\tconst globalName = options?.globalName ?? DEFAULT_BRIDGE_GLOBAL\n\t// 自检 globalThis —— contextBridge 内部也维护去重,但我们抢先抛更明确的诊断\n\tconst g = globalThis as unknown as Record<string, unknown>\n\tif (g[globalName] !== undefined) {\n\t\tthrow new Error(`Deck bridge already exposed at \"${globalName}\"`)\n\t}\n\n\tconst bridge: DeckBridge = {\n\t\tversion: BRIDGE_PROTOCOL_VERSION,\n\t\tprobe(): Promise<ProbeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Probe) as Promise<ProbeResponse>\n\t\t},\n\t\tinvoke(req: InvokeRequest): Promise<InvokeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Invoke, req) as Promise<InvokeResponse>\n\t\t},\n\t\tonEvent(listener: (env: EventEnvelope) => void): () => void {\n\t\t\tconst wrapped = (_event: unknown, env: EventEnvelope): void => {\n\t\t\t\tlistener(env)\n\t\t\t}\n\t\t\tipcRenderer.on(DeckChannel.Event, wrapped)\n\t\t\treturn () => {\n\t\t\t\tipcRenderer.removeListener(DeckChannel.Event, wrapped)\n\t\t\t}\n\t\t},\n\t}\n\n\tcontextBridge.exposeInMainWorld(globalName, bridge)\n}\n\nexport interface ExposeLayoutBridgeOptions {\n\t/** 暴露到 window 的全局名,默认 `__electronDeckLayoutBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用,把三条 slot-token LAYOUT channel(`slot-grant` PUSH /\n * `snapshot` send / `layout-subscribe` invoke)封装成一个 `LayoutBridge`-shaped\n * 对象暴露到 webview window,供 renderer:\n *\n * ```ts\n * import { exposeDeckLayoutBridge } from '@dimina-kit/electron-deck/preload'\n * exposeDeckLayoutBridge()\n * // renderer:\n * createDeckLayoutClient({ bridge: window.__electronDeckLayoutBridge })\n * ```\n *\n * channel 名一律取自框架 `DeckChannel`(不手抄字符串)。`onSlotGrant` 返回一个\n * 纯 unsubscribe 函数(可跨 contextBridge),不是 Disposable 对象。\n */\nexport function exposeDeckLayoutBridge(options?: ExposeLayoutBridgeOptions): void {\n\tif (typeof contextBridge?.exposeInMainWorld !== 'function' || typeof ipcRenderer?.on !== 'function') {\n\t\tthrow new Error('exposeDeckLayoutBridge: must be called from a preload script (electron contextBridge / ipcRenderer unavailable)')\n\t}\n\n\tconst globalName = options?.globalName ?? DEFAULT_LAYOUT_BRIDGE_GLOBAL\n\tconst g = globalThis as unknown as Record<string, unknown>\n\tif (g[globalName] !== undefined) {\n\t\tthrow new Error(`Deck layout bridge already exposed at \"${globalName}\"`)\n\t}\n\n\tconst bridge: LayoutBridge = {\n\t\tonSlotGrant(cb: (grant: SlotGrant) => void): () => void {\n\t\t\tconst listener = (_event: unknown, grant: SlotGrant): void => {\n\t\t\t\tcb(grant)\n\t\t\t}\n\t\t\tipcRenderer.on(DeckChannel.SlotGrant, listener)\n\t\t\treturn () => {\n\t\t\t\tipcRenderer.removeListener(DeckChannel.SlotGrant, listener)\n\t\t\t}\n\t\t},\n\t\tsendSnapshot(snapshot): void {\n\t\t\tvoid ipcRenderer.invoke(DeckChannel.Snapshot, snapshot).catch(() => {})\n\t\t},\n\t\tsubscribe(): void {\n\t\t\tvoid ipcRenderer.invoke(DeckChannel.LayoutSubscribe).catch(() => {})\n\t\t},\n\t}\n\n\tcontextBridge.exposeInMainWorld(globalName, bridge)\n}\n\nexport type {\n\tEventEnvelope,\n\tInvokeRequest,\n\tInvokeResponse,\n\tProbeResponse,\n\tDeckBridge,\n\tLayoutBridge,\n\tSlotGrant,\n}\nexport { BRIDGE_PROTOCOL_VERSION, DEFAULT_BRIDGE_GLOBAL, DEFAULT_LAYOUT_BRIDGE_GLOBAL, DeckChannel }\n"],"mappings":";;;;AAkBA,IAAa,wBAAwB;;;;;;AAOrC,IAAa,+BAA+B;;AAG5C,IAAa,0BAA0B;AAEvC,IAAa,cAAc;CAC1B,QAAQ;CACR,OAAO;CACP,OAAO;CACP,UAAU;CACV,WAAW;CACX,iBAAiB;AAClB;;;;;;;;;;;;;;;ACJA,SAAgB,iBAAiB,SAAqC;CACrE,IAAI,OAAO,SAAA,eAAe,sBAAsB,cAAc,OAAO,SAAA,aAAa,WAAW,YAC5F,MAAM,IAAI,MAAM,2GAA2G;CAG5H,MAAM,aAAa,SAAS,cAAA;CAG5B,IAAI,WAAE,gBAAgB,KAAA,GACrB,MAAM,IAAI,MAAM,mCAAmC,WAAW,EAAE;CAGjE,MAAM,SAAqB;EAC1B,SAAS;EACT,QAAgC;GAC/B,OAAO,SAAA,YAAY,OAAO,YAAY,KAAK;EAC5C;EACA,OAAO,KAA6C;GACnD,OAAO,SAAA,YAAY,OAAO,YAAY,QAAQ,GAAG;EAClD;EACA,QAAQ,UAAoD;GAC3D,MAAM,WAAW,QAAiB,QAA6B;IAC9D,SAAS,GAAG;GACb;GACA,SAAA,YAAY,GAAG,YAAY,OAAO,OAAO;GACzC,aAAa;IACZ,SAAA,YAAY,eAAe,YAAY,OAAO,OAAO;GACtD;EACD;CACD;CAEA,SAAA,cAAc,kBAAkB,YAAY,MAAM;AACnD;;;;;;;;;;;;;;;;AAsBA,SAAgB,uBAAuB,SAA2C;CACjF,IAAI,OAAO,SAAA,eAAe,sBAAsB,cAAc,OAAO,SAAA,aAAa,OAAO,YACxF,MAAM,IAAI,MAAM,iHAAiH;CAGlI,MAAM,aAAa,SAAS,cAAA;CAE5B,IAAI,WAAE,gBAAgB,KAAA,GACrB,MAAM,IAAI,MAAM,0CAA0C,WAAW,EAAE;CAqBxE,SAAA,cAAc,kBAAkB,YAAY;EAjB3C,YAAY,IAA4C;GACvD,MAAM,YAAY,QAAiB,UAA2B;IAC7D,GAAG,KAAK;GACT;GACA,SAAA,YAAY,GAAG,YAAY,WAAW,QAAQ;GAC9C,aAAa;IACZ,SAAA,YAAY,eAAe,YAAY,WAAW,QAAQ;GAC3D;EACD;EACA,aAAa,UAAgB;GAC5B,SAAK,YAAY,OAAO,YAAY,UAAU,QAAQ,EAAE,YAAY,CAAC,CAAC;EACvE;EACA,YAAkB;GACjB,SAAK,YAAY,OAAO,YAAY,eAAe,EAAE,YAAY,CAAC,CAAC;EACpE;CAG2C,CAAM;AACnD"}
|
package/dist/preload/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface ExposeLayoutBridgeOptions {
|
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* 在 host preload 内调用,把三条 slot-token LAYOUT channel(`slot-grant` PUSH /
|
|
27
|
-
* `
|
|
27
|
+
* `snapshot` send / `layout-subscribe` invoke)封装成一个 `LayoutBridge`-shaped
|
|
28
28
|
* 对象暴露到 webview window,供 renderer:
|
|
29
29
|
*
|
|
30
30
|
* ```ts
|
package/dist/preload/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as DeckChannel, n as DEFAULT_BRIDGE_GLOBAL, r as DEFAULT_LAYOUT_BRIDGE_GLOBAL, t as BRIDGE_PROTOCOL_VERSION } from "../protocol-
|
|
1
|
+
import { i as DeckChannel, n as DEFAULT_BRIDGE_GLOBAL, r as DEFAULT_LAYOUT_BRIDGE_GLOBAL, t as BRIDGE_PROTOCOL_VERSION } from "../protocol-B6TOT9AP.js";
|
|
2
2
|
import { contextBridge, ipcRenderer } from "electron";
|
|
3
3
|
//#region src/preload/index.ts
|
|
4
4
|
/**
|
|
@@ -39,7 +39,7 @@ function exposeDeckBridge(options) {
|
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* 在 host preload 内调用,把三条 slot-token LAYOUT channel(`slot-grant` PUSH /
|
|
42
|
-
* `
|
|
42
|
+
* `snapshot` send / `layout-subscribe` invoke)封装成一个 `LayoutBridge`-shaped
|
|
43
43
|
* 对象暴露到 webview window,供 renderer:
|
|
44
44
|
*
|
|
45
45
|
* ```ts
|
|
@@ -66,8 +66,8 @@ function exposeDeckLayoutBridge(options) {
|
|
|
66
66
|
ipcRenderer.removeListener(DeckChannel.SlotGrant, listener);
|
|
67
67
|
};
|
|
68
68
|
},
|
|
69
|
-
|
|
70
|
-
ipcRenderer.invoke(DeckChannel.
|
|
69
|
+
sendSnapshot(snapshot) {
|
|
70
|
+
ipcRenderer.invoke(DeckChannel.Snapshot, snapshot).catch(() => {});
|
|
71
71
|
},
|
|
72
72
|
subscribe() {
|
|
73
73
|
ipcRenderer.invoke(DeckChannel.LayoutSubscribe).catch(() => {});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/preload/index.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron'\nimport {\n\tBRIDGE_PROTOCOL_VERSION,\n\tDEFAULT_BRIDGE_GLOBAL,\n\tDEFAULT_LAYOUT_BRIDGE_GLOBAL,\n\tDeckChannel,\n} from '../shared/protocol.js'\nimport type {\n\tEventEnvelope,\n\tInvokeRequest,\n\tInvokeResponse,\n\tProbeResponse,\n\tDeckBridge,\n} from '../shared/protocol.js'\nimport type { LayoutBridge, SlotGrant } from '../client/layout-client.js'\n\nexport interface ExposeBridgeOptions {\n\t/** 暴露到 window 的全局名,默认 `__electronDeckBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用,把 framework typed RPC + event push bridge 暴露到\n * webview window:\n *\n * ```ts\n * import { contextBridge, ipcRenderer } from 'electron'\n * import { exposeDeckBridge } from '@dimina-kit/electron-deck/preload'\n * exposeDeckBridge()\n * ```\n *\n * 见 `DeckBridge` 接口(`shared/protocol.ts`)。\n */\nexport function exposeDeckBridge(options?: ExposeBridgeOptions): void {\n\tif (typeof contextBridge?.exposeInMainWorld !== 'function' || typeof ipcRenderer?.invoke !== 'function') {\n\t\tthrow new Error('exposeDeckBridge: must be called from a preload script (electron contextBridge / ipcRenderer unavailable)')\n\t}\n\n\tconst globalName = options?.globalName ?? DEFAULT_BRIDGE_GLOBAL\n\t// 自检 globalThis —— contextBridge 内部也维护去重,但我们抢先抛更明确的诊断\n\tconst g = globalThis as unknown as Record<string, unknown>\n\tif (g[globalName] !== undefined) {\n\t\tthrow new Error(`Deck bridge already exposed at \"${globalName}\"`)\n\t}\n\n\tconst bridge: DeckBridge = {\n\t\tversion: BRIDGE_PROTOCOL_VERSION,\n\t\tprobe(): Promise<ProbeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Probe) as Promise<ProbeResponse>\n\t\t},\n\t\tinvoke(req: InvokeRequest): Promise<InvokeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Invoke, req) as Promise<InvokeResponse>\n\t\t},\n\t\tonEvent(listener: (env: EventEnvelope) => void): () => void {\n\t\t\tconst wrapped = (_event: unknown, env: EventEnvelope): void => {\n\t\t\t\tlistener(env)\n\t\t\t}\n\t\t\tipcRenderer.on(DeckChannel.Event, wrapped)\n\t\t\treturn () => {\n\t\t\t\tipcRenderer.removeListener(DeckChannel.Event, wrapped)\n\t\t\t}\n\t\t},\n\t}\n\n\tcontextBridge.exposeInMainWorld(globalName, bridge)\n}\n\nexport interface ExposeLayoutBridgeOptions {\n\t/** 暴露到 window 的全局名,默认 `__electronDeckLayoutBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用,把三条 slot-token LAYOUT channel(`slot-grant` PUSH /\n * `
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/preload/index.ts"],"sourcesContent":["import { contextBridge, ipcRenderer } from 'electron'\nimport {\n\tBRIDGE_PROTOCOL_VERSION,\n\tDEFAULT_BRIDGE_GLOBAL,\n\tDEFAULT_LAYOUT_BRIDGE_GLOBAL,\n\tDeckChannel,\n} from '../shared/protocol.js'\nimport type {\n\tEventEnvelope,\n\tInvokeRequest,\n\tInvokeResponse,\n\tProbeResponse,\n\tDeckBridge,\n} from '../shared/protocol.js'\nimport type { LayoutBridge, SlotGrant } from '../client/layout-client.js'\n\nexport interface ExposeBridgeOptions {\n\t/** 暴露到 window 的全局名,默认 `__electronDeckBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用,把 framework typed RPC + event push bridge 暴露到\n * webview window:\n *\n * ```ts\n * import { contextBridge, ipcRenderer } from 'electron'\n * import { exposeDeckBridge } from '@dimina-kit/electron-deck/preload'\n * exposeDeckBridge()\n * ```\n *\n * 见 `DeckBridge` 接口(`shared/protocol.ts`)。\n */\nexport function exposeDeckBridge(options?: ExposeBridgeOptions): void {\n\tif (typeof contextBridge?.exposeInMainWorld !== 'function' || typeof ipcRenderer?.invoke !== 'function') {\n\t\tthrow new Error('exposeDeckBridge: must be called from a preload script (electron contextBridge / ipcRenderer unavailable)')\n\t}\n\n\tconst globalName = options?.globalName ?? DEFAULT_BRIDGE_GLOBAL\n\t// 自检 globalThis —— contextBridge 内部也维护去重,但我们抢先抛更明确的诊断\n\tconst g = globalThis as unknown as Record<string, unknown>\n\tif (g[globalName] !== undefined) {\n\t\tthrow new Error(`Deck bridge already exposed at \"${globalName}\"`)\n\t}\n\n\tconst bridge: DeckBridge = {\n\t\tversion: BRIDGE_PROTOCOL_VERSION,\n\t\tprobe(): Promise<ProbeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Probe) as Promise<ProbeResponse>\n\t\t},\n\t\tinvoke(req: InvokeRequest): Promise<InvokeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Invoke, req) as Promise<InvokeResponse>\n\t\t},\n\t\tonEvent(listener: (env: EventEnvelope) => void): () => void {\n\t\t\tconst wrapped = (_event: unknown, env: EventEnvelope): void => {\n\t\t\t\tlistener(env)\n\t\t\t}\n\t\t\tipcRenderer.on(DeckChannel.Event, wrapped)\n\t\t\treturn () => {\n\t\t\t\tipcRenderer.removeListener(DeckChannel.Event, wrapped)\n\t\t\t}\n\t\t},\n\t}\n\n\tcontextBridge.exposeInMainWorld(globalName, bridge)\n}\n\nexport interface ExposeLayoutBridgeOptions {\n\t/** 暴露到 window 的全局名,默认 `__electronDeckLayoutBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用,把三条 slot-token LAYOUT channel(`slot-grant` PUSH /\n * `snapshot` send / `layout-subscribe` invoke)封装成一个 `LayoutBridge`-shaped\n * 对象暴露到 webview window,供 renderer:\n *\n * ```ts\n * import { exposeDeckLayoutBridge } from '@dimina-kit/electron-deck/preload'\n * exposeDeckLayoutBridge()\n * // renderer:\n * createDeckLayoutClient({ bridge: window.__electronDeckLayoutBridge })\n * ```\n *\n * channel 名一律取自框架 `DeckChannel`(不手抄字符串)。`onSlotGrant` 返回一个\n * 纯 unsubscribe 函数(可跨 contextBridge),不是 Disposable 对象。\n */\nexport function exposeDeckLayoutBridge(options?: ExposeLayoutBridgeOptions): void {\n\tif (typeof contextBridge?.exposeInMainWorld !== 'function' || typeof ipcRenderer?.on !== 'function') {\n\t\tthrow new Error('exposeDeckLayoutBridge: must be called from a preload script (electron contextBridge / ipcRenderer unavailable)')\n\t}\n\n\tconst globalName = options?.globalName ?? DEFAULT_LAYOUT_BRIDGE_GLOBAL\n\tconst g = globalThis as unknown as Record<string, unknown>\n\tif (g[globalName] !== undefined) {\n\t\tthrow new Error(`Deck layout bridge already exposed at \"${globalName}\"`)\n\t}\n\n\tconst bridge: LayoutBridge = {\n\t\tonSlotGrant(cb: (grant: SlotGrant) => void): () => void {\n\t\t\tconst listener = (_event: unknown, grant: SlotGrant): void => {\n\t\t\t\tcb(grant)\n\t\t\t}\n\t\t\tipcRenderer.on(DeckChannel.SlotGrant, listener)\n\t\t\treturn () => {\n\t\t\t\tipcRenderer.removeListener(DeckChannel.SlotGrant, listener)\n\t\t\t}\n\t\t},\n\t\tsendSnapshot(snapshot): void {\n\t\t\tvoid ipcRenderer.invoke(DeckChannel.Snapshot, snapshot).catch(() => {})\n\t\t},\n\t\tsubscribe(): void {\n\t\t\tvoid ipcRenderer.invoke(DeckChannel.LayoutSubscribe).catch(() => {})\n\t\t},\n\t}\n\n\tcontextBridge.exposeInMainWorld(globalName, bridge)\n}\n\nexport type {\n\tEventEnvelope,\n\tInvokeRequest,\n\tInvokeResponse,\n\tProbeResponse,\n\tDeckBridge,\n\tLayoutBridge,\n\tSlotGrant,\n}\nexport { BRIDGE_PROTOCOL_VERSION, DEFAULT_BRIDGE_GLOBAL, DEFAULT_LAYOUT_BRIDGE_GLOBAL, DeckChannel }\n"],"mappings":";;;;;;;;;;;;;;;AAiCA,SAAgB,iBAAiB,SAAqC;CACrE,IAAI,OAAO,eAAe,sBAAsB,cAAc,OAAO,aAAa,WAAW,YAC5F,MAAM,IAAI,MAAM,2GAA2G;CAG5H,MAAM,aAAa,SAAS,cAAA;CAG5B,IAAI,WAAE,gBAAgB,KAAA,GACrB,MAAM,IAAI,MAAM,mCAAmC,WAAW,EAAE;CAGjE,MAAM,SAAqB;EAC1B,SAAS;EACT,QAAgC;GAC/B,OAAO,YAAY,OAAO,YAAY,KAAK;EAC5C;EACA,OAAO,KAA6C;GACnD,OAAO,YAAY,OAAO,YAAY,QAAQ,GAAG;EAClD;EACA,QAAQ,UAAoD;GAC3D,MAAM,WAAW,QAAiB,QAA6B;IAC9D,SAAS,GAAG;GACb;GACA,YAAY,GAAG,YAAY,OAAO,OAAO;GACzC,aAAa;IACZ,YAAY,eAAe,YAAY,OAAO,OAAO;GACtD;EACD;CACD;CAEA,cAAc,kBAAkB,YAAY,MAAM;AACnD;;;;;;;;;;;;;;;;AAsBA,SAAgB,uBAAuB,SAA2C;CACjF,IAAI,OAAO,eAAe,sBAAsB,cAAc,OAAO,aAAa,OAAO,YACxF,MAAM,IAAI,MAAM,iHAAiH;CAGlI,MAAM,aAAa,SAAS,cAAA;CAE5B,IAAI,WAAE,gBAAgB,KAAA,GACrB,MAAM,IAAI,MAAM,0CAA0C,WAAW,EAAE;CAqBxE,cAAc,kBAAkB,YAAY;EAjB3C,YAAY,IAA4C;GACvD,MAAM,YAAY,QAAiB,UAA2B;IAC7D,GAAG,KAAK;GACT;GACA,YAAY,GAAG,YAAY,WAAW,QAAQ;GAC9C,aAAa;IACZ,YAAY,eAAe,YAAY,WAAW,QAAQ;GAC3D;EACD;EACA,aAAa,UAAgB;GAC5B,YAAiB,OAAO,YAAY,UAAU,QAAQ,EAAE,YAAY,CAAC,CAAC;EACvE;EACA,YAAkB;GACjB,YAAiB,OAAO,YAAY,eAAe,EAAE,YAAY,CAAC,CAAC;EACpE;CAG2C,CAAM;AACnD"}
|
|
@@ -13,11 +13,11 @@ var DeckChannel = {
|
|
|
13
13
|
Invoke: "__electron-deck:invoke",
|
|
14
14
|
Event: "__electron-deck:event",
|
|
15
15
|
Probe: "__electron-deck:probe",
|
|
16
|
-
|
|
16
|
+
Snapshot: "__electron-deck:snapshot",
|
|
17
17
|
SlotGrant: "__electron-deck:slot-grant",
|
|
18
18
|
LayoutSubscribe: "__electron-deck:layout-subscribe"
|
|
19
19
|
};
|
|
20
20
|
//#endregion
|
|
21
21
|
export { DeckChannel as i, DEFAULT_BRIDGE_GLOBAL as n, DEFAULT_LAYOUT_BRIDGE_GLOBAL as r, BRIDGE_PROTOCOL_VERSION as t };
|
|
22
22
|
|
|
23
|
-
//# sourceMappingURL=protocol-
|
|
23
|
+
//# sourceMappingURL=protocol-B6TOT9AP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-B6TOT9AP.js","names":[],"sources":["../src/shared/protocol.ts"],"sourcesContent":["/**\n * Deck framework wire protocol —— main ↔ webview 之间的 channel 名 + 帧\n * 形态。SoT 在此,preload / client / main runtime 都从这里 import。\n *\n * 设计:把 declared `hostServices` / `simulatorApis` / `events` 三类全部走\n * **两个** 统一 channel,避免 channel name 爆炸 / 难以加 senderPolicy 白名单。\n * - `__electron-deck:invoke` — webview → main RPC (ipcRenderer.invoke)\n * - `__electron-deck:event` — main → webview event push (webContents.send)\n * - `__electron-deck:probe` — webview → main 探活,bridge ready 检查\n *\n * 帧用 JSON 对象一层 envelope,便于扩展 + 校验。\n *\n * @internal\n */\n\nimport type { JsonValue } from '../types.js'\n\n/** Bridge global 默认挂的全局名(contextBridge.exposeInMainWorld) */\nexport const DEFAULT_BRIDGE_GLOBAL = '__electronDeckBridge'\n\n/**\n * Slot-token LAYOUT bridge 默认挂的全局名。`exposeDeckLayoutBridge()` 默认暴露到\n * 此名,renderer 的 `createDeckLayoutClient({ bridge: window.__electronDeckLayoutBridge })`\n * 读取同名。单一来源避免 preload helper 与 client 之间字符串漂移。\n */\nexport const DEFAULT_LAYOUT_BRIDGE_GLOBAL = '__electronDeckLayoutBridge'\n\n/** Bridge protocol semver;client 在 ready() 时校验 major 一致 */\nexport const BRIDGE_PROTOCOL_VERSION = '1.0.0'\n\nexport const DeckChannel = {\n\tInvoke: '__electron-deck:invoke',\n\tEvent: '__electron-deck:event',\n\tProbe: '__electron-deck:probe',\n\tSnapshot: '__electron-deck:snapshot',\n\tSlotGrant: '__electron-deck:slot-grant',\n\tLayoutSubscribe: '__electron-deck:layout-subscribe',\n} as const\n\nexport type InvokeKind = 'host' | 'simulator'\n\nexport interface InvokeRequest {\n\treadonly kind: InvokeKind\n\treadonly name: string\n\treadonly args: readonly JsonValue[]\n}\n\nexport interface InvokeSuccess<R extends JsonValue = JsonValue> {\n\treadonly ok: true\n\treadonly result: R\n}\n\nexport interface InvokeFailure {\n\treadonly ok: false\n\treadonly error: {\n\t\treadonly remoteName: string\n\t\treadonly message: string\n\t\treadonly code?: string\n\t}\n}\n\nexport type InvokeResponse<R extends JsonValue = JsonValue> =\n\t| InvokeSuccess<R>\n\t| InvokeFailure\n\nexport interface EventEnvelope<P extends JsonValue = JsonValue> {\n\treadonly name: string\n\treadonly payload: P\n}\n\nexport interface ProbeResponse {\n\treadonly ready: true\n\treadonly version: typeof BRIDGE_PROTOCOL_VERSION\n}\n\n/**\n * Bridge global 暴露到 webview window 的 shape。preload 把它通过\n * `contextBridge.exposeInMainWorld(globalName, bridge)` 注入;webview-side\n * `createDeckClient()` 通过 `globalThis[globalName]` 读取。\n *\n * 注意所有方法必须是 contextBridge-friendly(plain values + serializable\n * arguments),不要把 Map / Set / Date / Promise.race 等 leak 进 bridge 接口。\n */\nexport interface DeckBridge {\n\treadonly version: typeof BRIDGE_PROTOCOL_VERSION\n\tprobe(): Promise<ProbeResponse>\n\tinvoke(req: InvokeRequest): Promise<InvokeResponse>\n\t/** 订阅 event channel;返回 unsubscribe 函数(不是 Disposable,因为要跨 contextBridge) */\n\tonEvent(listener: (env: EventEnvelope) => void): () => void\n}\n"],"mappings":";;AAkBA,IAAa,wBAAwB;;;;;;AAOrC,IAAa,+BAA+B;;AAG5C,IAAa,0BAA0B;AAEvC,IAAa,cAAc;CAC1B,QAAQ;CACR,OAAO;CACP,OAAO;CACP,UAAU;CACV,WAAW;CACX,iBAAiB;AAClB"}
|
|
@@ -27,7 +27,7 @@ export declare const DeckChannel: {
|
|
|
27
27
|
readonly Invoke: "__electron-deck:invoke";
|
|
28
28
|
readonly Event: "__electron-deck:event";
|
|
29
29
|
readonly Probe: "__electron-deck:probe";
|
|
30
|
-
readonly
|
|
30
|
+
readonly Snapshot: "__electron-deck:snapshot";
|
|
31
31
|
readonly SlotGrant: "__electron-deck:slot-grant";
|
|
32
32
|
readonly LayoutSubscribe: "__electron-deck:layout-subscribe";
|
|
33
33
|
};
|
|
@@ -654,8 +654,8 @@ function createViewHandle(deps) {
|
|
|
654
654
|
if (!active || !current) return;
|
|
655
655
|
if (migrating) return;
|
|
656
656
|
if (p.visible) {
|
|
657
|
-
ensureMounted();
|
|
658
657
|
nativeView.setBounds(p.bounds);
|
|
658
|
+
ensureMounted();
|
|
659
659
|
visibleBounds = {
|
|
660
660
|
x: p.bounds.x,
|
|
661
661
|
y: p.bounds.y,
|
|
@@ -698,4 +698,4 @@ function createViewHandle(deps) {
|
|
|
698
698
|
//#endregion
|
|
699
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
700
|
|
|
701
|
-
//# sourceMappingURL=view-handle-
|
|
701
|
+
//# sourceMappingURL=view-handle-DDhpBuW-.js.map
|