AGENTS.md@packages/client/src/components/interactive-renderers · git:20261019.0129fec · 2026-10-19 · sha256 6707e1e80c55286c
AGENTS.md@packages/client/src/components/interactive-renderers git:20261019.0129fecA
Immutable. This exact content never changes and is served at /api/v1/blob/6707e1e80c55286c.
# DOX — packages/client/src/components/interactive-renderers
Files in this directory. One row per source file.
| File | Purpose |
|------|---------|
| `AnsweredOption.tsx` | `AnsweredOption` row component. Renders one option in resolved select/multiselect/confirm card. `picked` highlights chosen option (green), others dimmed. Optional `label` prefix marks the custom "Other" row (`data-testid="answered-other-text"`). Shared by `ConfirmRenderer`, `SelectRenderer`, `MultiselectRenderer`. |
| `BatchRenderer.tsx` | `BatchRenderer` — renders `batch` ask_user as wizard card. Stepper header, one sub-question per page (confirm/select/multiselect/input), Review page with per-row Edit, Submit sends single `{answers}` response. `SubQuestion` + `Answer` types, `initialAnswer`, `answerToText`, `ReviewRow`, `StepBody`, `MultiselectStep` helpers. Inline Other: `SubQuestion.allowOther`, parent `otherDraft` state (survives step changes), `composeValues`; Next blocked while Other active + empty on a multiselect step. See change: add-ask-user-inline-other. |
| `ConfirmRenderer.tsx` | `ConfirmRenderer` — renders ask_user `confirm`. Pending: Yes/No/Cancel buttons via `onRespond({confirmed})`/`onCancel`. Resolved: `AnsweredOption` Yes/No with `picked` + `params.message` body. Cancelled/dismissed: compact row. Uses `InlineMarkdown`, `MarkdownContent`. See change: fix-ask-user-card-duplication (resolved shows message). See change: align-ui-with-theme-tokens: Yes = `--accent-solid` primary, No = `--tint-red-*`, Cancel = secondary; buttons `min-h-[44px] sm:min-h-[36px] text-[13px]` + `focus-ring`; header icon `--severity-warning-fg`. Tint/target recipe via `tint-action-*` / `tap-target` utilities (index chunk cap). ConfirmRenderer buttons `min-w-[44px]` (CI "No" was 43px). |
| `EditorRenderer.tsx` | `EditorRenderer` — renders ask_user `editor`. Pending: `<textarea>` prefilled from `params.prefill`, Submit sends `{value:text}`. Resolved: truncated (100 char) preview. Cancelled/dismissed: compact row. Stateful `text`. |
| `GenericInteractiveRenderer.tsx` | `GenericInteractiveRenderer` — fallback for unregistered methods. Shows `method`, status text, first 100 chars of `JSON.stringify(params)`. No interaction. |
| `InlineMarkdown.tsx` | `InlineMarkdown` — `ReactMarkdown` restricted to `strong`, `em`, `code`, `a` only. Block elements unwrapped to text. For single-line compact layouts (titles, option labels). |
| `InputComposer.tsx` | `InputComposer` — shared controlled multiline textarea + clipboard-image-paste body. Used by `InputRenderer` and `BatchRenderer` input step. `Cmd/Ctrl+Enter`→`onSubmit`, `Esc`→`onCancel`. Auto-grow 32–160px. Uses `useImagePaste`, `ImagePreviewStrip`. |
| `InputRenderer.tsx` | `InputRenderer` — renders ask_user `input`. Pending: `InputComposer` + Submit/Cancel, sends `{value,images}`. Resolved: `params.message` body + entered value (or "(left blank)") + image count. Cancelled/dismissed: compact row. Uses `MarkdownContent` for message. See change: fix-ask-user-card-duplication (resolved shows message). |
| `MultiselectRenderer.tsx` | `MultiselectRenderer` — renders ask_user `multiselect`. Pending: synthetic "Select all" row (`data-testid="select-all-row"`) + per-option checkboxes, Submit sends `{values}`. Resolved: `params.message` body + `AnsweredOption` list with "N of M" count. Cancelled/dismissed: compact row. See change: fix-ask-user-card-duplication (resolved shows message). Inline Other: when shared `isOtherAllowed`, `OtherOption` (checkbox) after options; Submit disabled while Other active + empty; values de-duplicated; "Select all" leaves Other alone; resolved shows non-option picks as picked "Other" rows. See change: add-ask-user-inline-other. |
| `NotifyRenderer.tsx` | `NotifyRenderer` — renders a `ctx.ui.notify(...)` chat row through the shared `InlineMessage` primitive. Canonical params `params.message`/`params.level`. Empty message returns null. → see `NotifyRenderer.tsx.AGENTS.md` |
| `OtherOption.tsx` | `OtherOption({variant:"radio"\|"checkbox",active,text,onActiveChange,onTextChange,onSubmit?})` — controlled inline "Other" row + text field. Focus moves only on user activation. Enter commits non-empty trimmed text. Escape collapses + `preventDefault`/`stopPropagation` (field carries `data-other-input`). Text is a plain React text node. i18n `common.other`/`common.otherAnswer`/`common.otherPlaceholder`. Shown only when shared `isOtherAllowed`. See change: add-ask-user-inline-other. |
| `parseOption.ts` | `parseOption(option)` — splits option string on first ` — ` or ` · ` into `{title, description?}`. `isCancelOption(option)` — true when option trims to `/^cancel$/i`. Used by `SelectRenderer`, `BatchRenderer`. |
| `registry.ts` | Renderer registry. `renderers` Map: confirm→`ConfirmRenderer`, select→`SelectRenderer`, input→`InputRenderer`, editor→`EditorRenderer`, multiselect→`MultiselectRenderer`, notify→`NotifyRenderer`, batch→`BatchRenderer`. Exports `registerInteractiveRenderer(method,renderer)` and `getInteractiveRenderer(method)` (falls back to `GenericInteractiveRenderer`). |
| `SelectRenderer.tsx` | `SelectRenderer` — renders ask_user `select`. Pending: option rows via `OptionRow` (parsed title/description), cancel option calls `onCancel`, sends `{value:option}`. Resolved: `params.message` body + `AnsweredOption` list, picked = matches `selectedValue`. Auto-appends Cancel row if none. Cancelled/dismissed: compact row. See change: fix-ask-user-card-duplication (resolved shows message). See change: align-ui-with-theme-tokens: options `min-h-[44px] sm:min-h-[36px]`, title 13 px, description 12 px `--text-secondary`; hover `--tint-blue-*` (cancel `--tint-red-*`); `focus-ring`. Inline Other: when shared `isOtherAllowed`, `OtherOption` (radio) + Submit button after options; Enter/Submit sends `{value:trimmed}`; resolved non-option value → picked "Other" row (plain text); non-string options dropped. See change: add-ask-user-inline-other. |
| `types.ts` | Shared types. `InteractiveRendererProps` — `{requestId, method, params, status, result, onRespond, onCancel}`. `status` union: pending/resolved/cancelled/dismissed. `InteractiveRenderer` — `React.ComponentType<InteractiveRendererProps>`. |