/** * `Compositor` — engine-agnostic z-order planner for a window's native child * views (foundation: the spikes in `.repro/electron-deck-spikes/`). * * It separates INTENT (mount / unmount / reorder a view into a zone, at a * relative position) from APPLICATION (`commit()` computes the minimal sequence * of host add/remove calls that transforms the host's current child order into * the target order). * * The host's observable z-semantics this planner is built on (from the spikes): * - `addChildView` of an ALREADY-mounted child raises it to the top WITHOUT a * remove first and WITHOUT reloading it. * - `addChildView` of a NEW child appends it to the end (= topmost). * - A batch of remove/add in ONE synchronous tick re-lands at the target order * with zero renderer reloads. * - `addChildView` into a destroyed contentView throws synchronously. * * Ordering model. Every mounted view carries `(zone, orderKey, viewId)`. The * total render order is that triple, ascending: lower zone renders BELOW higher * zone (zones stack), `orderKey` orders within a zone, and `viewId` is a pure * tiebreak so the order is deterministic even if two keys collide. `orderKey` is * a FRACTIONAL key: reorder-before(X) sets the moved view's key to the midpoint * between X's key and its in-zone predecessor's key, so a reorder is O(1) and * perturbs no other view's key. When repeated midpoints exhaust float precision * in a gap, the affected zone is RENUMBERED (rebalance) to evenly-spaced integer * keys — invisible, because the renumber preserves the existing relative order. * * mount epoch. A genuinely-new mount gets a fresh monotonically-increasing * `mountSeq` and lands at the top of its zone. Re-mounting a view that is STILL * mounted is a pure no-op (same orderKey/mountSeq, zero host churn). But an * `unmount(id)` followed by `mount(id)` is a NEW instance: it gets a new * mountSeq and lands at the top — it never resumes the old slot. * * commit. We fold the batch of intents into the FINAL target state (last-state, * not a write-log replay), then diff the host's current children against that * target. The longest increasing subsequence (LIS) of views already in correct * relative order — computed over the current∩target intersection — is left * untouched; every other shared view is `removeChildView` + `addChildView`, and * each brand-new view gets one explicit `addChildView`. All in one synchronous * pass. On failure `commit()` throws a typed {@link CommitError}: a destroyed * host with additions pending throws `host-destroyed` BEFORE touching native * (applied:false); a native call that throws mid-apply is caught and the host is * rolled back to its pre-apply snapshot, then `apply-failed` is thrown with * whether the rollback recovered the snapshot. A no-op commit, and a destroyed * host with only removals pending, are SILENT (teardown-friendly). */ /** A handle to a native child view. Identity is by `id`. */ export type NativeViewRef = { readonly id: string; }; /** * Typed failure thrown by {@link Compositor.commit}. `kind` distinguishes a * preflight refusal (`host-destroyed`, native byte-for-byte pre-commit, * `applied:false`) from a mid-apply native throw (`apply-failed`, * `applied:'partial'`); for `apply-failed`, `recovered` reports whether the * best-effort rollback restored the pre-apply snapshot order (`false` ⇒ native * is untrusted and the host must be treated as dead). */ export declare class CommitError extends Error { readonly kind: 'host-destroyed' | 'apply-failed'; readonly applied: false | 'partial'; readonly recovered?: boolean; constructor(args: { kind: 'host-destroyed' | 'apply-failed'; applied: false | 'partial'; recovered?: boolean; message?: string; }); } /** * The native content-view surface the Compositor drives. In production this is * an Electron `contentView` (`addChildView` / `removeChildView`); in tests it is * a faithful fake. `children()` returns the current order, LAST = topmost. */ export interface ContentViewHost { /** Already-mounted ref → raise to top (no remove, no reload); new ref → * append to the end (top). */ addChildView(v: NativeViewRef): void; removeChildView(v: NativeViewRef): void; readonly isDestroyed: boolean; /** Current child order; LAST element is the topmost. */ children(): readonly NativeViewRef[]; } export interface Compositor { /** Idempotent attach. A view STILL mounted → pure no-op (unchanged * orderKey/mountSeq, zero host calls). A new view (or one re-mounted after * unmount) → fresh mountSeq, lands at the top (end) of its zone. */ mount(view: NativeViewRef, opts?: { zone?: number; }): void; /** Detach a view. A subsequent mount of the same id is a NEW instance. */ unmount(viewId: string): void; /** Move a view: `before` slots it immediately before that anchor (midpoint of * the anchor and its predecessor); `before: null` sends it to the end (top) * of the zone. `zone` moves it to a different zone. An illegal `before` * (unknown / unmounted id, or one whose zone conflicts with an explicit * `zone`) throws SYNCHRONOUSLY. */ reorder(viewId: string, opts: { zone?: number; before?: string | null; }): void; /** Apply the folded target state to the host with the minimal add/remove * sequence (LIS-preserving). Returns void on success. Throws a typed * {@link CommitError} on failure: `kind:'host-destroyed'` (applied:false) when * the host is destroyed and there are ADDITIONS to apply — thrown BEFORE * touching native; `kind:'apply-failed'` (applied:'partial') when a native * call throws mid-apply, after a best-effort rollback to the pre-apply * snapshot (`recovered` flags whether the snapshot was restored). A no-op * commit, and a destroyed host with ONLY removals pending, are SILENT. */ commit(): void; /** Fold the intent state to EMPTY and commit, removing every native view * this window's compositor mounted from the host. The resulting commit is * REMOVALS-ONLY, so it reuses {@link commit}'s teardown-friendly * "destroyed host + only removals → silent" path (commit failure semantics): on an * already-destroyed host it makes zero host calls and throws nothing. * * Optional on the interface so a partial test double (or a caller that only * needs mount/unmount/reorder/commit) still structurally satisfies * `Compositor`; the real {@link createCompositor} always implements it. */ detachAll?(): void; } export declare function createCompositor(host: ContentViewHost): Compositor; //# sourceMappingURL=compositor.d.ts.map