/** * `ExtensionComboBuilder` — a controlled editor for a preset/tool's LIST OF * extension combos. A combo is an ordered list of extension ELEMENTS, each a bare * extension NAME or a `{ name, config }` mapping carrying author config; the value * is the list of combos (`PresetExtensionElement[][]`), the SAME shape the * preset/tool extensions wire carries. It is PRESENTATIONAL: the caller fetches the * extension catalog (`available`) and owns the `value`; this component fetches nothing. * * The surface has two parts: * - the current combos, each an ordered row of name `Badge`s with a per-combo Edit * (pull it back into the draft) and Remove; * - a combo UNDER CONSTRUCTION, authored with the shared `ExtensionPicker` (grouped * by kind, single-select for the non-stackable `backend` kind). When the draft * includes the `output_schema` extension, its config is authored inline with a * `SchemaEditor` (no title required); an "Add combo" / "Update combo" action * composes the elements — `output_schema` as `{ name, config: { schema } }`, every * other extension as a bare name — and writes it into the value. * * The builder only blocks the client-trivial invalids: an EMPTY combo (Add is * disabled until the draft has ≥1 member), a DUPLICATE of a combo already in the * value (by name sequence; flagged inline, Add blocked), an INVALID `output_schema` * config (the `SchemaEditor` shows the parse/lint error and Add is blocked), and an * UNKNOWN extension name in an existing combo (a name absent from `available`, which * happens when a plugin is removed after a preset was authored) — flagged inline on * that combo row and reported through `onValidityChange` so the caller can block * submit. Every deeper rule — the registry's one-per-non-stackable-kind constraint, * meta-schema validity, kind/ordering — is SERVER-AUTHORITATIVE: the write route * validates each combo and its 400 message is surfaced verbatim by the caller. * * Loading is signaled by `availableReady`: while the catalog is still loading the * builder reports VALID and suppresses the unknown-name notes (no danger-note flash * while submit stays enabled — the server backstops), so a stale-seeded combo does * not flicker invalid before the catalog resolves. * * SAFETY: an extension name is server-supplied, so every name renders as TEXT through * the DS `Badge`/`Checkbox` (React escapes it) — never an HTML sink. */ import type { Extension, PresetExtensionElement } from '@tai42/api-client'; import { type ReactNode } from 'react'; export interface ExtensionComboBuilderProps { /** The extension catalog the combos are drawn from (caller-fetched). */ readonly available: readonly Extension[]; /** The current list of combos, each an ordered list of extension elements. */ readonly value: readonly PresetExtensionElement[][]; /** Fired with the next list of combos on every add/update/remove. */ readonly onChange: (next: PresetExtensionElement[][]) => void; readonly disabled?: boolean; readonly idPrefix?: string; /** * Fired (effect-driven) on mount and whenever `value`, `available`, or * `availableReady` changes with whether every combo's names are known. A caller * blocks submit while this is `false`. The `onChange` payload shape is unchanged; * this is a SECOND callback so the prop surface stays backward-compatible. */ readonly onValidityChange?: (valid: boolean) => void; /** * Whether the `available` catalog has finished loading. Default `true` (existing * consumers keep today's behavior). While `false` the builder reports VALID and * suppresses the unknown-name notes, so a stale-seeded combo does not flash invalid * before the catalog resolves. */ readonly availableReady?: boolean; } export declare function ExtensionComboBuilder({ available, value, onChange, disabled, idPrefix, onValidityChange, availableReady, }: ExtensionComboBuilderProps): ReactNode; //# sourceMappingURL=extension-combo-builder.d.ts.map