import { type VariantProps } from 'class-variance-authority'; import * as React from 'react'; /** * Compose or build? The wrapper is built, the field's type treatment and the send * button are composed. * * `Input` already renders an addon container with a focus-within boundary * (`has-[input:focus]:outline-border-accent`) and a has-value boundary * (`has-[input:enabled]:has-[input:not(:placeholder-shown)]`), which is very nearly the * source's `active = focused || hasValue`. It is not close enough to use, for three * reasons, each of which would have been a real defect: * * 1. **`:placeholder-shown` is not `value.trim()`.** The source — and the submit gate * below — treat `" "` as empty. CSS cannot trim, so an `Input`-driven wrapper would * light up its accent border for whitespace while the send button stayed inert and * Enter did nothing. The border state has to be driven by the same trimmed boolean * that gates submission, which only React knows. * 2. **Wrong radius, and no seam to fix it at the right layer.** `addonContainer` is * `rounded-sm` (8px) because a text field is a control. This is a composite bar, the * middle step of cr-web-demo's three-step hierarchy (16px shell / 12px nested panel / * 8px control): `rounded-xl` = 12px = `rounded-med-sm`. * 3. **`addonContainer` draws with `outline` plus `mx-0.5`, and carries the `state` * (error/success/warning) axis.** A chat composer has no validation states, and * inheriting an outline-based boundary means the disabled treatment has to be * expressed as `outline-surface-disabled!` — an `!important` override reaching into * another component's internals. * * What *is* composed: * * - `inputStyles({ variant: 'unstyled' })` for the field, so the type ramp, placeholder * ink and disabled ink are literally `Input`'s and cannot drift from it. * - `Button` for send, so focus-visible ring, `:disabled` handling and the `render` seam * come for free rather than being re-implemented on a bare `