import type { Placement } from '@dimina-kit/view-anchor'; import type { PlacementSnapshot } from '../layout/index.js'; /** Per-view host extra threaded on every DesiredView: the slot's capability * token. Main derives the view's identity + z-order from this token (never from * the renderer-reported viewId), so a valid token can't be spliced onto a forged * viewId. */ export interface SlotExtra { slotToken: string; } /** One main→renderer slot grant: a native view (`viewId`) wants to follow the * DOM slot `slotId`; `slotToken` is the capability the renderer threads back * (as a per-view extra) so main can match a placement to the granted slot. * `generation` is a main-assigned, strictly-monotonic-per-wc renderer-lifetime * id: it is stamped onto every published snapshot so a reload (new grants at a * higher generation) resets main's reconciler regardless of IPC ordering. */ export interface SlotGrant { viewId: string; slotId: string; slotToken: string; generation: number; } /** The renderer-side transport for the slot-token handshake. `onSlotGrant` * registers the grant listener (returns an unsubscribe); `subscribe` asks main * to (re)play buffered grants AFTER the listener is attached; `sendSnapshot` * forwards the whole window-level desired-placement table (one coalesced frame), * each view carrying its slot's token as an extra. */ export interface LayoutBridge { onSlotGrant(cb: (grant: SlotGrant) => void): () => void; sendSnapshot(snapshot: PlacementSnapshot): void; subscribe(): void; } export interface LayoutClientDeps { bridge: LayoutBridge; /** Resolve a grant's `slotId` to its DOM element. Default: * `document.querySelector(slotId)`. Returns `null` when the slot is not * mounted (graceful no-op). */ resolveSlot?(slotId: string): HTMLElement | null; /** Anchor factory. Default: view-anchor's `createPlacementAnchor`. Injected * in tests to capture `(target, opts)` without real RO/IO/RAF. */ createAnchor?: (target: HTMLElement, opts: { visible: boolean; publish: (p: Placement) => void; followScroll?: boolean; followGeometry?: boolean; guardDisplayNone?: boolean; }) => { dispose(): void; }; /** Frame scheduler for the internal placement publisher. Default: * requestAnimationFrame / cancelAnimationFrame. Injected in tests to drive * coalesced publishes deterministically. */ requestFrame?: (cb: () => void) => number; cancelFrame?: (id: number) => void; } /** * Renderer half of the slot-token handshake. Subscribes to main's `slot-grant` * pushes FIRST (so a grant replayed synchronously by `subscribe()` cannot be * missed), then on each grant anchors the granted DOM slot with this session's * hardening opts (followScroll / followGeometry / guardDisplayNone). Each anchor's * measured `Placement` is written into a CENTRAL placement publisher keyed by * `viewId` — NOT sent per-view. The publisher coalesces every anchor's writes into * ONE window-level snapshot per animation frame, so a transient relayout that * momentarily measures 0×0 is overwritten before it is ever published. This is the * producer half of the level-triggered reconcile design (see * ../layout/placement-reconcile.ts); a per-view edge stream cannot self-correct a * lost or spurious frame, which is what caused a stuck detached (white-screen) * view. */ export declare function createDeckLayoutClient(deps: LayoutClientDeps): { dispose(): void; }; //# sourceMappingURL=layout-client.d.ts.map