/** * The optional state-binding editor — one shape on every door (a preset, a channel * route, a schedule, a hook) and, in the flow engine, per node. It attaches one or * more states, each with the templates it attaches on save, a subject/scope, input * injections evaluated before the run, and updates applied after it. * * It lives in the Studio SDK so every plugin door reuses it; it holds NO edge to jq * (the jq fields render through the host's ambient expression door, else a plain * textarea) and paints from SDK tokens only. */ import type { ReactNode } from 'react'; import { Button, EmptyState, ErrorState, Skeleton } from '../components/primitives'; import { StateAttachRow } from './StateAttachRow'; import type { BindingSourceSchemas, BindingStateOption, BindingTemplateOption, StateAttach, StateBinding, TemplatedTextCatalog, } from './types'; export interface StateBindingEditorProps { /** The binding, or `null` when the door binds no state. */ readonly value: StateBinding | null; readonly onChange: (value: StateBinding | null) => void; /** The states this door may bind, each with its attached templates and fields. */ readonly statesCatalog: readonly BindingStateOption[]; /** The templates the editor may attach (attach-on-use), each with its template jq. */ readonly templatesCatalog: readonly BindingTemplateOption[]; /** The run's output/input field paths a picker offers when a schema is known. */ readonly sources?: BindingSourceSchemas; /** * The TARGET's own binding (e.g. the preset a route dispatches), passed by the door * screen — never fetched here. A state this binding names that the inherited one * also names shows a precedence warning on its card. */ readonly inherited?: StateBinding | null; /** The catalog fetch is in flight. */ readonly loading?: boolean; /** The catalog fetch failed. */ readonly error?: string; /** The stored templates a templated-text field's id picker offers, resolved by the door. */ readonly templatedTextTemplates?: TemplatedTextCatalog; } function blankAttach(): StateAttach { return { state: '', templates: [], subject_expr: { content: '' }, scope_expr: null, input_injections: [], updates: [], }; } export function StateBindingEditor({ value, onChange, statesCatalog, templatesCatalog, sources, inherited, loading = false, error, templatedTextTemplates, }: StateBindingEditorProps): ReactNode { if (loading) { return (