/** * `TemplatedTextField` — the ONE control that authors a {@link TemplatedText} * value: an authored-text field that is either inline `content` (text written in * place) or a stored template `id` (a template resource fetched and rendered at * run time), with the render `kwargs` that apply in BOTH cases. * * The two sources are mutually exclusive by CONSTRUCTION: the mode toggle only ever * writes `content` XOR `id`, so a value can never carry both. "Neither" is a `null` * value, allowed only when the field is optional; a `required` field emits its * empty source instead so the schema's non-empty guard (and the field error) refuse * the save. The kwargs editor is shared across both modes. * * It seeds its editing state from `value` ONCE and drives itself thereafter, * emitting a normalized value on every change (a caller resets it by remounting * under a fresh `key`). The inline editor is a plain multiline textarea by default; * a caller authoring jq or another rich field passes `renderInline` to supply its * own editor. The stored-template list, its loading/error state, the verbatim * server message for an id that no longer resolves, and whether storage is present * are passed in by the caller — this control holds no data edge of its own. * * When storage is absent (a supported configuration in which stored templates * cannot be fetched or resolved) the stored source is not offered: an inline or * empty value edits inline; a value already carrying an `id` is shown read-only with * its kwargs, never converted or dropped. See `storageAbsent` / * `storagePresenceLoading`. */ import type { TemplatedText } from '@tai42/api-client'; import { type ReactNode, useState } from 'react'; import { Field } from './field'; import { initialMode, type KwargRow, type Mode, objectToRows, templatedValueFrom, } from './templated-text-kwargs'; import { KwargsEditor, ReadOnlyKwargs } from './templated-text-kwargs-editor'; import { TemplatedTextSource } from './templated-text-source'; /** One stored template the id picker offers. */ export interface TemplatedTextTemplateOption { readonly id: string; /** The label shown for the id; defaults to the id itself. */ readonly label?: string; } /** The props `renderInline` receives to draw the inline-content editor. */ export interface TemplatedTextInlineProps { readonly label: string; readonly value: string; readonly onChange: (value: string) => void; readonly error?: string; /** * The field renders its own group header (label + description) above the source * toggle, so the inline editor's OWN visible label is redundant: an editor honours * this by rendering that label visually hidden while keeping it as its accessible * name (its `