# ChatComposer

`ChatComposer` provides an accessibly named, growing textarea inside a rounded field with a prominent surface, a focus-colored frame and a circular send control. It sends trimmed nonempty text through a callback.

## Usage

Load `@dg/ui/styles.css` once in the root layout.

```svelte
<script>
  import { ChatComposer } from "@dg/ui";
  let draft = $state("");
  let lastSent = $state("");
</script>

<ChatComposer
  id="question"
  bind:value={draft}
  onsend={(text) => {
    lastSent = text;
    draft = "";
  }}
/>
<p class="feedback" aria-live="polite">{lastSent}</p>
```

## Notes

- While `pending`, the draft stays focusable and read-only; activating stop calls `onstop` and returns focus to the draft.
- The field outline uses `--text-muted`, keeping the 3:1 contrast required for a control boundary in both themes.
- The field has rounded `1rem` corners, unlike square native fields. When the textarea has focus, the entry surface's frame takes `--focus` and thickens inward to `--focus-width`, without an outer outline; forced-colors mode shows a system-colored outline. The send control keeps its own focus indicator.
- The placeholder and single-line draft are vertically centered within the minimum control height. Additional lines grow the textarea normally, including automatic wrapping.
- The composer keeps its height in a flex column while the thread yields space. The textarea grows to 7.5rem (120px at a 16px root), then scrolls internally; maxlength separately limits the character count.
- A translucent, blurred fade above the composer softens messages scrolling underneath without intercepting pointer input. The field and controls remain opaque.
- The send and stop controls use built-in names in the `Page` language.
- Native HTML attributes, DOM callbacks, class and style are forwarded to the form; it owns submission, and `id` names the textarea.
- Forwarding uses native attribute types; unknown props are not supported. Pass class as a string.

## Parameters

| Name          | Type                     | Default     | Description                                                                                                                 |
| ------------- | ------------------------ | ----------- | --------------------------------------------------------------------------------------------------------------------------- |
| `id`          | `string`                 | `Required`  | Unique textarea ID, also used to connect its hint.                                                                          |
| `value`       | `string · bindable`      | `""`        | Draft text. Use bind:value; the parent clears it after a successful send.                                                   |
| `onsend`      | `(text: string) => void` | `undefined` | Receives trimmed, nonempty text on Enter or submit. Shift+Enter adds a newline; IME composition does not send.              |
| `label`       | `string`                 | `undefined` | Accessible textarea name; no visible label is rendered. Defaults to the built-in text in the `Page` language.               |
| `placeholder` | `string`                 | `undefined` | Placeholder shown for an empty draft. Defaults to the built-in text in the `Page` language.                                 |
| `hint`        | `string`                 | `undefined` | Text linked with aria-describedby and set as `.feedback`; an empty string hides it. Defaults to the built-in keyboard hint. |
| `disabled`    | `boolean`                | `false`     | Disable input and sending, for example while a response is loading.                                                         |
| `maxlength`   | `number`                 | `2000`      | Native maximum textarea length in characters.                                                                               |
| `pending`     | `boolean`                | `false`     | Make the draft read-only, keeping its focus, and replace sending with a stop action while a request is pending.             |
| `onstop`      | `() => void`             | `undefined` | Stop action callback; the parent owns cancellation and pending state.                                                       |

## Methods

| Name      | Description                                                  |
| --------- | ------------------------------------------------------------ |
| `focus()` | Focus the textarea without scrolling. Access with bind:this. |

## Accessibility

The label supplies the textarea’s accessible name through aria-label. Use a unique id to connect its hint. Enter sends; Shift+Enter adds a newline; IME composition does not send. Disabled prevents editing and sending. The parent clears the draft and manages request errors.
