/** * The sheet engine — the SharedValue/geometry core both sheet frontends * drive: the standalone `` component and lynx-navigation's * `presentation: 'sheet'` adapter. Ported mechanically from * lynx-navigation's inline `BottomSheet.tsx` internals (the #743/#744 * live-geometry pipeline, the `openToLift` main-thread capture, and the * lift composition) so the tuned WhatsApp-composer behaviors survive * verbatim. * * ## Ownership contracts (the load-bearing invariants) * * - `reveal` (px visible height) is **MT-owned**: the pan writes it per * frame (auto-flushed, #681), `withTiming` animates it, `syncGeom` * clamps it. The BG never reads it for logic. It is *injected* by the * navigator for route sheets (their layer bindings already target it) * and engine-allocated for inline sheets. * - Geometry flows BG→MT **only** through `syncGeom` into `geomRef` — a * main-thread ref the worklets read live. Never capture geometry as BG * lexicals in a worklet: a `runOnMainThread` closure captures its * referenced lexicals when the expression evaluates, freezing them at * setup (the exact bug class #743 fixed). New worklet-visible state * extends `geomRef` + `syncGeom`, never fresh captures. * - `combined = max(reveal, floor + liftSV)` — the effective reveal also * clears an external (keyboard) lift. Derived-SV identities are stable * across re-registration, so downstream bindings stay bound while the * floor changes at runtime. */ import { type MainThreadRef, type SharedValue } from '@sigx/lynx'; /** Release-snap / open-close tween duration (unified across frontends). */ export declare const SNAP_SEC = 0.2; /** * Pre-computed ms for BG-side timers — module-level so both MT worklets * and BG callback closures can see it (locals inside an MT worklet body * are MT-only). */ export declare const SNAP_MS: number; /** Live geometry, resolved by the frontend on every render. */ export interface SheetGeometry { /** Collapsed floor (px) — `detents[0]`. */ floor: number; /** The detent `open` targets. */ open: number; /** The largest detent. */ top: number; /** All detents, ascending px. */ detents: number[]; } export interface SheetEngineConfig { /** * Live geometry accessor — evaluated at every render/sync, NEVER * snapshotted at setup: a composer accessory (the headline use case) * has a floor that changes at runtime — an attachment chip row * appears, the text input grows from 1 to N lines. Freezing geometry * at setup would leave the sheet at its mount-time size while its * content grew underneath it (#743). */ geometry: () => SheetGeometry; /** * Live fixed panel height (px) — the panel is laid out this tall and * slid down by `translateY = panelHeight - combined` (a TRANSFORM, * safe to drive from the main thread every frame, unlike `height`). */ panelHeight: () => number; /** * The reveal SV to drive. The navigator INJECTS its dedicated sheet * SV (layer/backdrop bindings already target it); inline sheets omit * this and the engine allocates one seeded at the floor. */ reveal?: SharedValue; /** * External lift (px) under the collapsed reveal — a keyboard lift SV. * Effective reveal is `max(reveal, floor + liftSV)`. */ liftSV?: SharedValue | null; /** * Snap to the live lifted position on open instead of the BG detent * (mount-constant; requires `liftSV`). See `setOpen`'s capture path. */ openToLift?: boolean; /** * Fires on the BG thread when a drag settles at a snap candidate. * Debounced by the engine: a quick re-grab+release must not let an * earlier settle timer still fire a stale index — only the latest * release emits. */ onSnap?: (candidateIndex: number) => void; } /** Worklet-visible geometry + flags, pushed from render via `syncGeom`. */ export interface SheetWorkletGeometry { min: number; max: number; detents: number[]; /** `1` = releases below the dismiss line settle at reveal 0. */ dismissible: number; /** * `1` = the pan may claim; `0` = drag frozen (`dragEnabled: false`). * Lives HERE (not in a render-written SharedValue) because a BG-side * `sv.value =` write is a read-only no-op — the only way a render-time * flag reaches a worklet is this syncGeom push (the bug noted in #758: * post-mount dragEnabled changes silently never arrived). */ gate: number; /** * Page-coord Y of the sheet's bottom edge (`sheetTop = bottomEdge - * combined` in the surface-drag arbitration). The screen height for a * screen-anchored sheet; `screenH - bottomOffset` when an ancestor * pads the bottom safe area. Same syncGeom-only rule as `gate`. */ bottomEdge: number; } /** Per-gesture transient state (main-thread ref). */ export interface SheetDragTransient { startX: number; startY: number; /** Baseline for reveal mapping — set at CLAIM, not touch start. */ claimY: number; startReveal: number; prevY: number; prevT: number; /** px/sec, positive = downward. */ vel: number; /** `OWNER_*` from math.ts. */ owner: number; active: number; gen: number; } export interface SheetEngine { reveal: SharedValue; /** `max(reveal, floor + lift)` — bind panels/backdrops/siblings to this. */ combined: SharedValue; /** `panelHeight - combined` — bind the panel's translateY to this. */ translateY: SharedValue; geomRef: MainThreadRef; /** The open REST reveal — the captured lift position under `openToLift`. */ openRestRef: MainThreadRef<{ rest: number; }>; drag: MainThreadRef; /** Mount-constant: `openToLift && liftSV` was configured. */ openToLift: boolean; /** * Push current geometry + flags to the worklets (render calls this * after diffing — including on a bare `gate`/`dismissible` flip). * Clamps stranded MT state when detents shrank. */ syncGeom: (min: number, max: number, ds: number[], dismissible: number, gate: number, bottomEdge: number) => unknown; /** * Move to a target reveal. `capture === 1` (the `openToLift` path) * snaps to the CURRENT lifted position read live on the MT instead — * see the port note inside. */ setReveal: (target: number, animate: number, capture: number, openFloor: number) => unknown; /** BG-side debounced settle emit — the pan's release hop calls this. */ scheduleSnap: (candidateIndex: number) => void; } export declare function useSheetEngine(cfg: SheetEngineConfig): SheetEngine; //# sourceMappingURL=engine.d.ts.map