/** * Proposing a design reference per code component, by name. * * Node ids are not discoverable without API access — a design tool's MCP server * exposes only the page a user is looking at, and Code Connect (which would * hand the mapping back directly) is gated behind a paid seat. So the ids come * from the REST API, and something has to guess which one goes with which * component. * * This is that guess, and it is **only** a guess. It writes no mapping file: it * ranks candidates and prints the `reference = "…"` line to paste onto an * annotation. A kit names components by its own taxonomy — `Button - tonal`, * `Connected button group` — which does not always agree with the documented * component names, so a high score is a proposal and not a fact. Keeping the * human in the loop is the point, which is why this lives in `baseline` * (interactive, never on the Action path) rather than in the resolver. * * Pure: the caller does the fetching. */ /** One component the design file publishes, as a candidate. */ export interface KitCandidate { name: string; nodeId: string; /** The frame or page trail it sits under; often carries the word the name drops. */ containing: string; } /** A candidate with its score, after weighting. */ export interface RankedCandidate extends KitCandidate { score: number; /** How many of the subject's words this candidate's name actually accounts for. */ shared: number; } /** How much to trust the top proposal. */ export type Confidence = "GOOD" | "MAYBE" | "LOW"; /** True for a Material Symbols glyph: snake_case with no spaces. */ export declare const isIcon: (name: string) => boolean; /** The internal parts a component set is assembled from — real nodes, wrong altitude. */ export declare const isBuildingBlock: (name: string, containing: string) => boolean; /** Another platform's component: a phone/desktop catalog should not resolve to one. */ export declare const isXr: (name: string, containing: string) => boolean; /** Leading-dot names are a kit's own private components (`.Tonal palettes`). */ export declare const isPrivate: (name: string) => boolean; /** * Multiplier applied to a candidate's raw name score. `0` drops it entirely. * * Icons are dropped rather than demoted because nothing here should EVER * resolve to a glyph — a design catalog compares components. The rest are * demoted, so they can still win when genuinely nothing else fits. */ export declare function candidateWeight(name: string, containing: string): number; /** * Token-overlap score in [0, 1]: how much of the SHORTER name the two share. * * Shorter rather than longer, so a candidate whose name is a superset — * `Button segment` against `Button` — is not punished for its extra words. */ export declare function score(a: string, b: string): number; export declare function confidenceOf(best: RankedCandidate | undefined): Confidence; export interface RankOptions { /** How many proposals to keep. */ limit?: number; } /** * The best candidates for one code component, best first. * * `subject` should carry the component's group as well as its id — a kit names * by its own taxonomy, and the group often carries the word the leaf drops * (`Button/Tonal` is `Button - tonal` there). * * Each candidate is scored against its bare name AND against name-plus-trail, * taking the better: the trail rescues a component whose own name is generic * (`Button segment`) and would hurt one whose name is already exact, since * every extra token dilutes the overlap. */ export declare function rankCandidates(subject: string, candidates: readonly KitCandidate[], opts?: RankOptions): RankedCandidate[]; /** The subject string to rank against: the group plus the id, de-slashed. */ export declare function subjectFor(componentId: string, group?: string): string; /** One thing to propose a reference for. */ export interface ProposalSubject { /** How the repo names it — what a human will paste the reference next to. */ label: string; /** The words to match on, which may carry a group the label doesn't. */ text: string; } /** * The subset of a compose-preview manifest this reads. Structural on purpose: * the manifest is compose-ai-tools' artifact, and pinning its whole shape here * would make an unrelated field of theirs a breaking change here. */ export interface PreviewManifestLike { previews?: { catalog?: { role?: string; componentId?: string; group?: string; }; }[]; } /** * Subjects from a compose-preview manifest, sorted by component id. * * `COMPONENT`-role previews only — a manifest also carries screens and gallery * entries, and neither has a component-level reference to propose. First * preview per component id wins: several previews picturing one component are * variants of a single reference, not several references. */ export declare function subjectsFromPreviewManifest(manifest: PreviewManifestLike): ProposalSubject[]; /** * The structural shape a tree walk needs, so this module stays independent of * any one adapter's node type. A Figma `FigmaNodeDoc` satisfies it. */ export interface CandidateNode { id: string; name: string; type: string; children?: CandidateNode[]; } /** * Every component reachable from `root`, with the trail of frames it sits under. * * Stops at the first component-ish node on each branch: descending into a * component set would yield its variants (`Button/Size=Small`), which are not * candidates for a component-level reference and would each outscore the set * they belong to on any name that happens to mention a size. * * `root` is normally a page, and its name heads every trail: a kit that names a * page `Navigation` is the only thing telling you that the `Rail` on it is a * navigation rail. */ export declare function candidatesFromTree(root: CandidateNode): KitCandidate[]; //# sourceMappingURL=ref-proposals.d.ts.map