/** Closest a panel edge may come to the viewport edge. */ export declare const EDGE_MARGIN = 8; export declare const RESIZE_MIN_W = 320; export declare const RESIZE_MIN_H = 360; export interface Rect { left: number; top: number; width: number; height: number; } export interface Size { width: number; height: number; } export interface Offset { x: number; y: number; } /** Which edges a resize handle moves. The rest stay where they are. */ export interface ResizeEdges { left?: boolean; right?: boolean; top?: boolean; bottom?: boolean; } /** * Clamp a wanted drag offset so the panel stays reachable. * * `rect` is the panel as DRAWN, so it already contains `offset`. That has to * come back out before the limits can be expressed in offset terms, hence * `rest*`, the position the panel would occupy with no drag applied. * * ⚠️ The upper bound is `max(min, max)`, not `max`. A panel larger than the * viewport has `max < min`, and clamping to a range whose ends are inverted * would snap it to the FAR edge, off-screen in the other direction. Pinning to * the near edge instead keeps it grabbable. */ export declare function clampDragOffset(input: { rect: Rect; offset: Offset; want: Offset; viewport: Size; margin?: number; }): Offset; /** * Apply a pointer delta to one resize handle and return the new rect. * * The edge you grabbed follows the pointer; the opposite edge stays put. For a * leading edge that means clamping the EDGE and deriving the size from it, * clamping the size first and deriving the edge lets the opposite edge creep, * which reads as the panel sliding while you resize it. * * Rounded, because sub-pixel left/top values make text reflow on every frame. */ export declare function resizePanelRect(input: { base: Rect; edges: ResizeEdges; dx: number; dy: number; viewport: Size; margin?: number; minWidth?: number; minHeight?: number; }): Rect; export declare const SHEET_MIN_H = 220; /** Ceiling as a share of the viewport: a sheet is never the whole screen. */ export declare const SHEET_MAX_VH = 0.95; /** Pull further than this and the sheet closes instead of springing back. */ export declare const SHEET_DISMISS_PX = 72; /** * Past this the gesture was a drag, so the click it produces must be eaten, * otherwise letting go over the grab handle re-opens what you just dragged. */ export declare const SHEET_CLICK_SUPPRESS_PX = 6; /** Up is taller: `grow` is how far the edge was pulled UPWARD. */ export declare function clampSheetHeight(input: { startHeight: number; grow: number; viewportHeight: number; min?: number; maxRatio?: number; }): number; export type SheetGesture = "dismiss" | "settle-suppressed" | "settle"; /** What a released downward pull of `pullDown` pixels means. */ export declare function resolveSheetGesture(pullDown: number): SheetGesture; export interface Bounds { left: number; right: number; top: number; bottom: number; } /** * Where the "ask about this" pill goes for a given selection. * * Centred on the selection and kept inside the viewport, above it when there is * room and below it when there is not. It also has to dodge the open panel: the * pill and the panel can want the same pixels once a second selection is made * while the assistant is up, and a pill hidden behind the panel is exactly the * silent nothing the feature exists to remove. * * ⚠️ When BOTH sides collide the pill keeps its original side and draws over the * panel, it carries the maximum z-index and is painted after, so it stays * reachable. Withdrawing it would recreate that silent nothing. */ export declare function placeSelectionOffer(input: { offer: { left: number; top: number; bottom: number; }; box: Size; viewport: Size; panel: Bounds | null; margin?: number; }): { left: number; below: boolean; }; /** * The detached panel's inline position, expressed from the edges it is pinned * to rather than from left/top. * * A corner panel must keep its corner: expressed as left/top it drifts away * from the launcher when the viewport resizes, because the distance it was * given is to the wrong two edges. Anything that is not corner-pinned, joined * to an edge, or centred, has no corner to promise, so left/top is right for it. */ export declare function panelAnchorCss(input: { rect: Rect; pinsToCorner: boolean; pinsToLeftEdge: boolean; viewport: Size; }): string; /** * The least room a pushed-aside host page may be left with. * * ⚠️ WHAT THIS EXISTS FOR. A pushing drawer shifts `` by its own width so * the page stays readable beside the answer. The host's layout, though, answers * only to the WINDOW: its media queries cannot see that its box just got * narrower. So between the two the page believes it is wide while it is not, * and renders a desktop layout into a tablet-sized box. Measured on olochat.ca * (assistant v0.22.0, drawer at its default width): * * window 1280 → 860px left → the nav bar overlaps its own brand * window 1536 → 1116px left → everything fits * * 1024 is the width mainstream desktop layouts are built down to (Tailwind * `lg`, Foundation's desktop floor), and it separates those two measurements: * 860 is below it and broke, 1116 is above it and did not. It is a HEURISTIC * calibrated on that pair, not a derived constant, the host's real breakpoints * are unknowable from inside the panel. Treat a change to it as a measurement, * not a preference. */ export declare const PUSH_MIN_HOST_WIDTH = 1024; /** * Whether pushing the page aside still leaves the host enough to lay itself out. * * Below the floor the drawer does what the sheet already does: it lays OVER the * page instead of shoving it. That keeps the behaviour a property of where the * panel stands, the same rule `isModal` and the rest of `pushesNow` follow, * so no embed has to be changed and no host has to write a CSS override. * * A panel width of 0 means the panel is not laid out yet; there is nothing to * push with, so the answer is the whole viewport and the caller's `open` test * decides. Never returns false for a viewport wide enough on its own. */ export declare function hasRoomToPush(input: { viewportWidth: number; panelWidth: number; min?: number; }): boolean;