/** * design-gauntlet.ts — design_gauntlet implementation * * Compare a subject page to a reference (benchmark) page from live computed * CSS, across the nine dimensions that decide perceived polish: surfaces, * hairlines, text roles, letter spacing, accent, type scale, radii, * elevation, rhythm. The first four account for most of the perceived * difference. Output is a measured diff, a checkable bar derived from the * reference, fixes split mechanical vs needs-a-decision, and a binary * on_par verdict — the exit gate for the gauntlet loop the response embeds. * * Measurement rules (from the teardown protocol): * - Measure, never recall: every number comes off the live DOM. * - Zero-height trap: wait for layout, verify a healthy visible count. * - Lazy-load trap: scroll the full page once, return to top, then probe. * - Long-tail trap: vocabulary is read from the top of each tally * (smallest set of values covering 90% of occurrences), never raw counts. * - Webfont trap: wait on document.fonts.ready, report fonts_status. * - Color-scheme trap: the scheme is emulated explicitly and reported. */ import { CaptureUnavailableError } from "./capture.js"; export { CaptureUnavailableError }; export type TallyEntry = { value: string; count: number; }; export type GauntletMeasurement = { url: string; viewport: string; device_scale_factor: number; color_scheme: string; visible_elements: number; fonts_status: string; surfaces: { canvas: string; tally: TallyEntry[]; }; borders: { tally: TallyEntry[]; }; text: { tally: TallyEntry[]; }; tracking: { display: TallyEntry[]; body: TallyEntry[]; }; accent: { candidates: TallyEntry[]; usesInFirstViewport: number; }; type: { families: TallyEntry[]; sizes: TallyEntry[]; weights: TallyEntry[]; }; radii: { tally: TallyEntry[]; }; elevation: { shadows: TallyEntry[]; insetOnly: number; }; rhythm: { containers: TallyEntry[]; sectionPadding: TallyEntry[]; }; warnings: string[]; }; export type GauntletDiffRow = { dimension: string; metric: string; subject: string; reference: string; subject_worse: boolean; note: string; }; export type GauntletBarCheck = { id: string; mechanism: string; check: string; }; export type GauntletFix = { fix: string; mechanism: string; effect: "high" | "medium" | "low"; }; export type GauntletComparison = { diff: GauntletDiffRow[]; bar: GauntletBarCheck[]; fixes: { mechanical: GauntletFix[]; needs_a_decision: GauntletFix[]; }; verdict: { on_par: boolean; failing_mechanisms: string[]; biggest_gap: string | null; }; }; /** * The real vocabulary of a tally: the smallest number of top values that * covers `coverage` of all occurrences. Third-party embeds, cookie banners * and chat widgets contribute a long tail of one-off values that are not * part of the site's design system — a raw distinct count reads that tail * as if it were the system. */ export declare function vocabularyCount(tally: TallyEntry[], coverage?: number): number; /** Parse the em value out of a tracking tally entry ("−0.32px = -0.020em" or "normal (0em)"). */ export declare function parseTrackingEm(value: string): number | null; /** The most common tracking value's em, or null when nothing was measured. */ export declare function primaryTrackingEm(tally: TallyEntry[]): number | null; export declare function compareGauntletMeasurements(subject: GauntletMeasurement, reference: GauntletMeasurement): GauntletComparison; export declare const GAUNTLET_LOOP_PROTOCOL: string[]; export declare const GAUNTLET_DISCIPLINE_NOTICE: string; export type GauntletMeasureOptions = { viewport?: { width: number; height: number; }; color_scheme?: "light" | "dark"; device_scale_factor?: number; timeout_ms?: number; }; export declare function measureGauntletPage(url: string, opts?: GauntletMeasureOptions): Promise;