/**
 * Native Element and Component Registry
 *
 * Central registry for all UI elements and components registered with UI Bridge Native.
 * Adapted from ui-bridge for React Native environments.
 */
import type { RegisteredNativeElement, RegisteredNativeComponent, NativeElementState, NativeElementType, NativeStandardAction, NativeCustomAction, NativeAppInfo, NativeBridgeSnapshot, NativeElementBbox, NativeElementRef, NativeRegistrationCoverage, NativeRouteProviderLike, NativeSnapshotEnrichers, NativeSnapshotEnricher, Workflow, BridgeEventType, BridgeEventListener } from './types';
/**
 * Options for registering an element
 */
export interface RegisterElementOptions {
    type?: NativeElementType;
    label?: string;
    actions?: NativeStandardAction[];
    customActions?: Record<string, NativeCustomAction>;
    props?: Record<string, unknown>;
    treePath?: string;
    testId?: string;
    accessibilityLabel?: string;
    /** Route path where the element was registered (for page-scoped filtering) */
    registrationRoute?: string | null;
    /** Flattened RN style (from StyleSheet.flatten) for design review */
    flatStyle?: Record<string, unknown>;
    /** State-specific style overrides for design review */
    stateStyles?: {
        pressed?: Record<string, unknown>;
        focused?: Record<string, unknown>;
        disabled?: Record<string, unknown>;
    };
}
/**
 * Options for registering a component
 */
export interface RegisterComponentOptions {
    name: string;
    description?: string;
    actions?: Array<{
        id: string;
        label?: string;
        description?: string;
        handler: (params?: unknown) => unknown | Promise<unknown>;
    }>;
    elementIds?: string[];
}
/**
 * Registry configuration
 */
export interface NativeRegistryConfig {
    verbose?: boolean;
    onEvent?: BridgeEventListener;
}
/**
 * Extract handler function names from an element's props.
 * Returns names of props whose values are functions (e.g. ['onPress', 'onChangeText']).
 */
export declare function extractHandlerNames(props?: Record<string, unknown>): string[];
/**
 * `currentRouteOnly` filter predicate for `registrationRoute`.
 *
 * Returns `true` when an element should be INCLUDED in a current-route-only
 * view. Two classes of elements pass the filter:
 *
 *   1. Elements registered on the current route
 *      (`registrationRoute === currentRoute`).
 *   2. Route-agnostic elements (`registrationRoute == null` or `''`) — these
 *      are typically registered at the app root (tab bars, persistent
 *      headers, modal hosts) and apply to every route. Excluding them by
 *      default makes `currentRouteOnly=true` return empty on mobile where
 *      most apps register chrome at the root and screens via routed stacks.
 *
 * The filter only DROPS elements whose `registrationRoute` is set to a
 * non-matching route — i.e. they belong to a different screen. This matches
 * the runner-side semantics: route-tagged DOM nodes that don't match the
 * active URL are scoped out; untagged nodes are always in scope.
 *
 * Surfaced 2026-05-23 (item 4 of the 0.6.6 robustness pass) — the prior
 * strict equality returned 0 elements on mobile screens that register tabs
 * at the root, because tab elements had `registrationRoute: null`.
 */
export declare function matchesCurrentRoute(registrationRoute: string | null | undefined, currentRoute: string): boolean;
/**
 * Project an element's runtime state into a runner-native pixel-space `bbox`.
 *
 * Returns `{x, y, w, h}` in PHYSICAL pixels (RN `state.layout` is logical dp;
 * we multiply by `pixelRatio` — `PixelRatio.get()`, injected via
 * `registry.setPixelRatio` — so the box aligns with the runner's adb screencap
 * frame). See {@link NativeElementBbox} for why the shape is `{x,y,w,h}` and
 * not the web SDK's `{x,y,width,height}`.
 *
 * `pixelRatio` is INJECTED rather than read from `react-native` here: this is a
 * non-React core module, and a static `import { PixelRatio } from 'react-native'`
 * breaks both the vitest suite ("Flow is not supported") and risks the
 * Metro/Hermes `unknownModuleError` that bit `design-handlers`/`page-health`.
 * The provider wires the real ratio via `setPixelRatio`; callers/tests that
 * never wire it get the `1` default (dp ≈ px, still a usable box).
 *
 * Gating (avoid poisoning the snapshot — the flat registry mixes routes and
 * some elements carry stale off-screen coords):
 *   - returns `undefined` unless `visibility === 'visible'` (visible + measured)
 *   - returns `undefined` when `state.layout` is null/absent
 * Clamps every component to a non-negative integer.
 *
 * Uses `measureInWindow`'s absolute `pageX`/`pageY` for the origin. Falls back
 * to the layout-relative `x`/`y` only when `pageX`/`pageY` weren't measured
 * (rare; keeps a best-effort box rather than dropping the element).
 */
export declare function projectBbox(state: NativeElementState, visibility: 'visible' | 'likely-visible' | 'hidden', pixelRatio?: number): NativeElementBbox | undefined;
/**
 * Native UI Bridge Registry
 *
 * Manages registration and lookup of native UI elements and components.
 */
export declare class NativeUIBridgeRegistry {
    private elements;
    private components;
    private workflows;
    private eventListeners;
    private config;
    private enrichers;
    private snapshotExtras;
    /**
     * App identification metadata (appId/appName/appType/framework). Set via
     * `setAppInfo` — typically wired in the server constructor from the
     * `NativeUIBridgeConfig` the host passes to `UIBridgeNativeProvider`.
     *
     * When set, `createSnapshot` includes it on the resulting snapshot so
     * agents can identify the app from snapshot bytes alone (parity with the
     * `uiBridge` block on the `health` response). Snapshots created before
     * this is wired simply omit the field.
     */
    private appInfo;
    /**
     * Optional route provider. When set, `createSnapshot` reads
     * `currentRoute`/`segments` from it whenever the caller does not pass an
     * explicit `routeInfo` argument. This lets the default `getSnapshot` HTTP
     * handler — which has no direct reference to the `NativeUIBridgeServer`
     * instance — still emit a populated `currentRoute` field.
     *
     * Wired by `NativeUIBridgeServer.setRouteProvider`, which forwards the
     * provider into the registry alongside the per-handler overrides it
     * already installs.
     */
    private routeProvider;
    /**
     * Device pixel ratio used to project `state.layout` (logical dp) into the
     * physical-pixel `bbox` emitted on each snapshot element. Defaults to `1`
     * (dp ≈ px) so callers/tests that never inject a ratio still get a usable
     * box; the React provider wires the real `PixelRatio.get()` via
     * {@link setPixelRatio}. Read from a non-React core module on purpose — see
     * the `projectBbox` doc comment for why we don't import `react-native` here.
     */
    private pixelRatio;
    /**
     * Sticky flag: flips `true` the first time any element is registered and
     * stays `true` even after elements are unregistered. Lets agents distinguish
     * "this route never wired any elements" from "this route registered then
     * unmounted".
     */
    private everHadRegistrations;
    constructor(config?: NativeRegistryConfig);
    /**
     * Set the app identification metadata returned alongside snapshots.
     * Idempotent — re-calling with the same shape is a no-op.
     * Pass `null` (or omit a previous call) to clear.
     */
    setAppInfo(info: NativeAppInfo | null | undefined): void;
    /** Read-only accessor for the registered app info, if any. */
    getAppInfo(): NativeAppInfo | null;
    /**
     * Set a route provider whose `getCurrentRoute`/`getSegments` are read by
     * `createSnapshot` when no explicit `routeInfo` argument is supplied.
     *
     * Call with `null` to detach (e.g. when the host swaps providers on HMR).
     */
    setRouteProvider(provider: NativeRouteProviderLike | null | undefined): void;
    /** Read-only accessor for the registered route provider, if any. */
    getRouteProvider(): NativeRouteProviderLike | null;
    /**
     * Set the device pixel ratio used to project per-element `bbox` geometry
     * into physical pixels. The React provider passes `PixelRatio.get()` here
     * once at startup. Ignores non-finite / non-positive values (keeps the
     * previous ratio) so a bad call can't zero out every bbox.
     */
    setPixelRatio(ratio: number): void;
    /** Read-only accessor for the device pixel ratio used for bbox projection. */
    getPixelRatio(): number;
    /**
     * Register/replace canonical enrichers (modal/toast/undo). HMR-safe — calling
     * with a partial set merges into existing slots instead of clobbering them.
     */
    setEnrichers(e: Partial<NativeSnapshotEnrichers>): void;
    /**
     * Read-only accessor for the canonical enrichers slot. Returned object is
     * the live reference, so callers can detect "no detector wired" via
     * `getEnrichers().modalDetector === undefined`. Used by the test-hook HTTP
     * handlers (`control/modal/push`, `control/modal/dismiss/:id`) which need
     * to drive the ModalDetector directly without re-instantiating it.
     */
    getEnrichers(): Readonly<NativeSnapshotEnrichers>;
    /**
     * Register a custom snapshot enricher. The returned object will be
     * `Object.assign`ed onto the snapshot, so use unique top-level keys to avoid
     * clobbering canonical fields. Returns a disposer.
     */
    registerSnapshotEnricher(name: string, fn: NativeSnapshotEnricher): () => void;
    /** Remove a custom snapshot enricher by name */
    unregisterSnapshotEnricher(name: string): void;
    /**
     * Register a native element
     */
    registerElement(id: string, ref: React.RefObject<NativeElementRef>, options?: RegisterElementOptions): RegisteredNativeElement;
    /**
     * Unregister an element
     */
    unregisterElement(id: string): void;
    /**
     * Get a registered element
     */
    getElement(id: string): RegisteredNativeElement | undefined;
    /**
     * Get all registered elements
     */
    getAllElements(): RegisteredNativeElement[];
    /**
     * Get all registered elements that are visible AND have a measured layout.
     *
     * This is the *strict* visibility filter: requires `state.visible === true`
     * AND `state.layout !== null`. Use this when you need both signals
     * (e.g. coord-based tap, where you need an actual rect to hit-test against).
     *
     * Callers driving an LLM agent typically want
     * {@link getMountedVisibleElements} instead — that variant accepts
     * elements that *should* be on screen (visible:true) but whose first
     * `onLayout` has not yet fired. Without it, a snapshot taken in the gap
     * between mount and the first layout pass returns 0 elements on a fully
     * populated screen.
     */
    getVisibleElements(): RegisteredNativeElement[];
    /**
     * Get all registered elements marked as visible (regardless of layout).
     *
     * Looser than {@link getVisibleElements}: includes elements whose
     * `onLayout` callback has not yet fired (so `layout === null`). React
     * Native's `onLayout` is async — there's a one-tick window after mount
     * where elements are in the tree, the user *sees* them, but the
     * registry hasn't received their measurements yet. Excluding those
     * elements makes `getSnapshot?visibleOnly=true` return 0 on a populated
     * screen, which is a worse failure mode than including a few elements
     * we can't yet locate spatially.
     *
     * Used by `createSnapshot`'s `visibleOnly` filter and by the
     * `getSnapshot` / `getElements` handlers. Each emitted element carries a
     * `visibility` discriminator (`"visible"` / `"likely-visible"`) so
     * agents that *do* require a known rect can branch downstream.
     */
    getMountedVisibleElements(): RegisteredNativeElement[];
    /**
     * Get elements registered on a specific route (for page-scoped filtering)
     */
    getElementsForRoute(route: string): RegisteredNativeElement[];
    /**
     * Mark elements registered on a route as off-screen (visible: false, layout: null).
     *
     * Use this when a screen loses focus but stays mounted — common in React Navigation
     * tab navigators where inactive tabs remain in the tree. Without this call, stale
     * `layout` data lingers in snapshots and makes off-screen elements look rendered.
     *
     * Does NOT unregister the elements — they stay registered so the user's next visit
     * re-measures them via `onLayout` without re-mount cost. Elements without a
     * `registrationRoute` (app-wide registrations) are untouched.
     */
    markRouteOffscreen(route: string): void;
    /**
     * Re-measure every mounted-visible element's geometry via its stored React
     * ref and write the fresh coordinates into the registry ("measure-on-
     * snapshot").
     *
     * Why: element geometry is otherwise captured ONCE at mount time
     * (`onLayout` + `measureInWindow` in `useUIElement`) and RN never re-fires
     * `onLayout` on pure translation — scrolling a virtualized list
     * (FlashList), recycling a cell, a keyboard shift or an orientation change
     * all move elements WITHOUT updating the stored `state.layout`. Snapshot
     * consumers (vision analyzers hit-testing `bbox`, coord-based tap) then
     * operate on stale positions. Re-measuring at READ time is correct
     * regardless of what moved the element and needs zero per-consumer wiring:
     * every registry entry already stores its ref (see `registerElement`).
     *
     * Per-element outcomes:
     *   - non-zero dims → write a fresh full layout (same state shape as the
     *     mount-time measure loop in `useUIElement`); counted as `measured`.
     *   - zero dims AND the element previously had a non-null `state.layout` →
     *     the view collapsed / left the screen; clear to
     *     `{ visible: false, layout: null }` (same shape `markRouteOffscreen`
     *     writes); counted as `cleared`.
     *   - zero dims and it never had a layout → leave state untouched (element
     *     in the mount→first-onLayout gap; don't demote `likely-visible`);
     *     counted as `skipped`.
     *   - no ref / no callable `measureInWindow` (test fixtures, web) → leave
     *     untouched; counted as `skipped`.
     *
     * The whole sweep races an overall timeout (default 250 ms) so a dead ref
     * whose `measureInWindow` never calls back can never hang a snapshot: the
     * returned promise resolves at the deadline with whatever counts have
     * accumulated. Late callbacks may still write state afterwards — harmless,
     * freshest data wins. Never throws.
     *
     * Non-React core module on purpose — `measureInWindow` is duck-typed off
     * the stored ref rather than imported from `react-native` (see the
     * `projectBbox` doc comment for why this file must not import RN).
     */
    refreshMeasurements(options?: {
        timeoutMs?: number;
    }): Promise<{
        measured: number;
        cleared: number;
        skipped: number;
    }>;
    /**
     * Update element state
     */
    updateElementState(id: string, state: Partial<NativeElementState>): void;
    /**
     * Update an element's descriptive metadata (label / accessibilityLabel /
     * testId). Sister to {@link updateElementState} (which carries layout +
     * visibility) and {@link updateElementProps} (which carries event handlers).
     *
     * Idempotent — if every passed field equals the existing value, the call
     * returns without mutating the entry or emitting an event. This keeps
     * `useUIElement().updateLabel(...)` cheap when consumers re-publish from a
     * render path that produces the same string on every tick.
     *
     * Returns `true` when the entry was mutated, `false` otherwise. Callers
     * that want to gate a `console.warn` on no-op invocations can use the
     * return value instead of recomputing equality.
     */
    updateElementMeta(id: string, meta: {
        label?: string;
        accessibilityLabel?: string;
        testId?: string;
    }): boolean;
    /**
     * Update element props (for action execution).
     *
     * Side effect for `type: 'input'` elements: when `props.value` is a string,
     * mirror it into `state.value` so AI drivers reading the registry don't see
     * `state.value === undefined` until the user types. Before this mirror,
     * controlled-input consumers (`<TextInput value={x} onChangeText={setX} />`
     * with `captureProps({ value: x, onChangeText: setX })`) registered an
     * input whose `state.value` only became defined after the FIRST
     * `onChangeText` call — bridge consumers couldn't read the default value
     * (e.g. a pre-populated API-URL input) until someone typed into it.
     *
     * Re-runs on every `updateElementProps` so a parent passing a fresh
     * `value` prop (controlled re-render) keeps the registry in sync. The
     * mirror is gated on `type === 'input'` to avoid touching non-input
     * elements that may carry an unrelated `value` prop (e.g. a Switch's
     * boolean value, which lives on `state.checked`).
     */
    updateElementProps(id: string, props: Record<string, unknown>): void;
    /**
     * Update element style for design review
     */
    updateElementStyle(id: string, flatStyle: Record<string, unknown>, stateStyles?: {
        pressed?: Record<string, unknown>;
        focused?: Record<string, unknown>;
        disabled?: Record<string, unknown>;
    }): void;
    /**
     * Get element style for design review
     */
    getElementStyle(id: string): Record<string, unknown> | null;
    /**
     * Find element by testID
     */
    findByTestId(testId: string): RegisteredNativeElement | undefined;
    /**
     * Find elements by type
     */
    findByType(type: NativeElementType): RegisteredNativeElement[];
    /**
     * Register a component
     */
    registerComponent(id: string, options: RegisterComponentOptions): RegisteredNativeComponent;
    /**
     * Unregister a component
     */
    unregisterComponent(id: string): void;
    /**
     * Get a registered component
     */
    getComponent(id: string): RegisteredNativeComponent | undefined;
    /**
     * Get all registered components
     */
    getAllComponents(): RegisteredNativeComponent[];
    /**
     * Register a workflow
     */
    registerWorkflow(workflow: Workflow): void;
    /**
     * Unregister a workflow
     */
    unregisterWorkflow(id: string): void;
    /**
     * Get a workflow
     */
    getWorkflow(id: string): Workflow | undefined;
    /**
     * Get all workflows
     */
    getAllWorkflows(): Workflow[];
    /**
     * Subscribe to events
     */
    on<T = unknown>(type: BridgeEventType, listener: BridgeEventListener<T>): () => void;
    /**
     * Unsubscribe from events
     */
    off<T = unknown>(type: BridgeEventType, listener: BridgeEventListener<T>): void;
    /**
     * Emit an event
     */
    emit(type: BridgeEventType, data: unknown): void;
    /**
     * Create a snapshot of the current state.
     *
     * Route resolution: `routeInfo` wins when provided. Otherwise — and this
     * is the path the default `getSnapshot` HTTP handler takes — we fall back
     * to the registered `routeProvider`. This means callers that never wire a
     * server-level override (the bare-server / cloud-relay path) still get a
     * populated `currentRoute` field as long as `setRouteProvider` was called
     * on the registry.
     */
    createSnapshot(routeInfo?: {
        currentRoute?: string | null;
        segments?: string[];
    }, options?: {
        visibleOnly?: boolean;
        currentRouteOnly?: boolean;
    }): NativeBridgeSnapshot;
    /**
     * Get registry statistics
     */
    getStats(): {
        elements: number;
        components: number;
        workflows: number;
    };
    /**
     * Compute registration coverage metadata for the current registry state.
     *
     * Groups currently-registered elements by `registrationRoute`, bucketing
     * elements without a route under `'?'`. `everHadRegistrations` is sticky —
     * once true, it stays true for the lifetime of the registry instance.
     */
    getRegistrationCoverage(): NativeRegistrationCoverage;
    /**
     * Clear all registrations
     */
    clear(): void;
}
/**
 * Set the global registry
 */
export declare function setGlobalRegistry(registry: NativeUIBridgeRegistry): void;
/**
 * Get the global registry
 */
export declare function getGlobalRegistry(): NativeUIBridgeRegistry | null;
/**
 * Reset the global registry
 */
export declare function resetGlobalRegistry(): void;
//# sourceMappingURL=registry.d.ts.map