@kitn.ai/ui
Version:
Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.
67 lines (66 loc) • 3.12 kB
TypeScript
import { ToolPart } from './tool';
import { AttachmentData } from './attachments';
/** One assistant response in a comparison. Rendered identically to an assistant
* message (reasoning + tools + attachments + markdown content) via `MessageBody`. */
export interface CompareCandidate {
/** Stable id, unique within the pair; echoed back in the selection. */
id: string;
/** The response text/markdown. */
content: string;
/** Optional collapsible reasoning block. */
reasoning?: {
text: string;
label?: string;
};
/** Tool-call parts rendered above the content. */
tools?: ToolPart[];
/** Inline attachment previews rendered above the content. */
attachments?: AttachmentData[];
/** Optional short column label (e.g. "A" / "B" / "Concise"). */
label?: string;
/** Optional model name shown as a sub-label. */
model?: string;
/** When true this candidate is still streaming — its pick control is disabled
* and a per-column shimmer shows until it (and its sibling) settle. */
streaming?: boolean;
}
/** The two-candidate tuple. Enforced at the type level; validated at runtime by
* `normalizeCandidates`. */
export type ComparePair = [CompareCandidate, CompareCandidate];
/** How the card collapses once the user picks. `'winner'` (default) shows just the
* chosen candidate's body; `'none'` keeps both columns but marks the choice. */
export type CompareCollapse = 'winner' | 'none';
/** The compare definition (set as the `data` JS property on `<kai-compare>`). */
export interface ResponseCompareData {
/** Optional prompt/question shown above the two columns. */
prompt?: string;
/** Exactly two candidates: [A, B]. */
candidates: ComparePair;
/** Collapse behaviour after a pick. Default `'winner'`. */
collapse?: CompareCollapse;
}
/** Emitted (and re-hydratable) when the user picks. The consumer pairs
* `(prompt, chosenId, rejectedIds)` for preference capture / RLHF. */
export interface CompareSelection {
/** The id of the chosen candidate. */
chosenId: string;
/** The id(s) of the rejected candidate(s) — for a pair, the other one. */
rejectedIds: string[];
/** Epoch ms when the pick was made. */
at?: number;
}
/**
* Validate + normalize the candidate list. A compare requires EXACTLY two
* candidates with unique, non-empty string ids. Returns the usable pair, or an
* error message when the definition is unusable (rendered as an inline error /
* emitted as a `kai-error`). Modeled on `normalizeOptions`.
*/
export declare function normalizeCandidates(candidates: unknown): {
candidates: ComparePair | null;
error?: string;
};
/** Build the selection payload for choosing `chosenId` out of the pair (the other
* candidate is the rejected one). Stamps `at` with the current time. */
export declare function buildSelection(pair: ComparePair, chosenId: string): CompareSelection;
/** Whether any candidate in the pair is still streaming (pick stays disabled). */
export declare function isAnyStreaming(pair: ComparePair | null): boolean;