import { JSX, Accessor } from 'solid-js';
import { CompareSelection, ResponseCompareData } from './response-compare-types';
export type { CompareCandidate, ComparePair, CompareCollapse, ResponseCompareData, CompareSelection, } from './response-compare-types';
export { normalizeCandidates, buildSelection, isAnyStreaming } from './response-compare-types';
export type CompareLayout = 'auto' | 'columns' | 'tabs';
/** Imperative handle exposed via `controllerRef` — surfaces the compare's latent
 *  capabilities (commit a pick by candidate id, focus the roving tab stop) so the
 *  `<kai-compare>` facade can forward them as instance methods (Pattern C). */
export interface ResponseCompareController {
    /** Commit a pick by candidate id — same path as the "Pick this" button: emits
     *  onSelect + optimistically collapses (single-shot; inert while streaming or
     *  already resolved). No-op for an unknown id. */
    select(candidateId: string): void;
    /** Focus the current roving tab stop (the focused candidate's "Pick this" radio). */
    focus(options?: FocusOptions): void;
}
export interface ResolvedController<T> {
    value: Accessor<T | undefined>;
    isResolved: Accessor<boolean>;
    isOptimistic: Accessor<boolean>;
    setLocal: (v: T) => void;
}
export declare function useResolved<T>(opts: {
    prop: Accessor<T | undefined>;
    data: Accessor<unknown>;
}): ResolvedController<T>;
export interface ResponseCompareProps {
    /** The compare definition (prompt + the two candidates). */
    data?: ResponseCompareData;
    /** Stable id correlating every emitted event. */
    compareId?: string;
    /** Re-hydrate / control the selection. Renders the collapsed winner. Set as a
     *  JS property: `el.selection = { chosenId, rejectedIds }`. */
    selection?: CompareSelection;
    /** Layout. `'columns'` = side-by-side; `'tabs'` = one candidate at a time with
     *  pills to switch; `'auto'` (default) uses a CONTAINER query — columns when the
     *  component is ≥640px wide, tabs when narrower (e.g. on a phone). */
    layout?: CompareLayout;
    class?: string;
    /** Receive the imperative controller once mounted. The `<kai-compare>` facade
     *  forwards these as element methods (select/focus). */
    controllerRef?: (controller: ResponseCompareController) => void;
    /** Fired when the user commits a pick. */
    onSelect?: (sel: CompareSelection) => void;
    /** Fired once both candidates have settled (stopped streaming) and a valid
     *  definition is shown. */
    onReady?: () => void;
    /** Fired for an unusable definition (inline error state). */
    onError?: (message: string) => void;
}
/**
 * `ResponseCompare` — a dual-response comparison. Two assistant candidates for the
 * same prompt render side-by-side (or as tabs), each via `MessageBody` so a
 * candidate reads exactly like an assistant message (reasoning + tools +
 * attachments + markdown). The pick is a COMMIT, not a Submit: clicking "Pick this"
 * (or Enter on the focused column) computes `{ chosenId, rejectedIds:[other], at }`,
 * optimistically collapses to the chosen candidate's body, and calls `onSelect` for
 * the consumer to send a `(prompt, chosen, rejected)` preference pair. Single-shot.
 *
 * While EITHER candidate is `streaming`, both pick controls are disabled and the
 * streaming column shows a `TextShimmer`; `onReady` fires once both settle. The two
 * columns are a WAI-ARIA radiogroup with roving tabindex (Arrow keys move A↔B,
 * Enter/Space picks). Emits `onError` for a malformed definition.
 */
export declare function ResponseCompare(props: ResponseCompareProps): JSX.Element;
