

# ChatComposer

Bordered composer chrome around the message input and trailing actions. Pair with `ChatComposerInput` for the auto-resizing textarea and `ChatSendButton` for send/stop.

## Examples

### Default

There is no `@var-ui/astro` ChatComposer — Astro and HTML previews show static core-recipe chrome; Enter-to-submit is React-only.

```tsx
import { useState } from 'react';
import { ChatComposer, ChatComposerInput, ChatSendButton } from '@var-ui/react';

function Composer() {
  const [value, setValue] = useState('');
  return (
    <ChatComposer actions={<ChatSendButton onPress={() => setValue('')} />}>
      <ChatComposerInput
        value={value}
        onChange={setValue}
        onSubmit={() => setValue('')}
        placeholder="Type a message…"
      />
    </ChatComposer>
  );
}
```

```astro
---
// No @var-ui/astro ChatComposer — static core-recipe chrome (non-interactive).
import { button, chatComposer, resolveButtonProps } from '@var-ui/core';

const c = chatComposer();
const send = button(resolveButtonProps({ intent: 'primary' }));
---

<div class:list={[c.root]}>
  <div class:list={[c.inputRow]}>
    <textarea class:list={[c.input]} rows="1" placeholder="Type a message…" disabled></textarea>
  </div>
  <div class:list={[c.actions]}>
    <button type="button" class:list={[send]} aria-label="Send message" disabled>↑</button>
  </div>
</div>
```

```html
<div data-appearance="default" class="var-ui-chat-composer"><div data-appearance="default" class="var-ui-chat-composer__inputRow"><textarea data-appearance="default" class="var-ui-chat-composer__input" rows="1" placeholder="Type a message…" aria-label="Message" disabled></textarea></div><div data-appearance="default" class="var-ui-chat-composer__actions"><button type="button" data-tone="accent" data-appearance="filled" data-size="md" data-layout="default" class="var-ui-button" aria-label="Send message" disabled><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"></path></svg></button></div></div>
```

## ChatComposerInput

| Prop          | Type                      | Default             |
| ------------- | ------------------------- | ------------------- |
| `value`       | `string`                  | (required)          |
| `onChange`    | `(value: string) => void` | (required)          |
| `onSubmit`    | `(value: string) => void` | (required)          |
| `placeholder` | `string`                  | `'Type a message…'` |
| `maxRows`     | `number`                  | `8`                 |
| `isDisabled`  | `boolean`                 | `false`             |
| `className`   | `string`                  | —                   |

Enter submits (trimmed, non-empty only); Shift+Enter inserts a newline.

## Props

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| actions | ReactNode | no |  | Trailing actions — typically `ChatSendButton`. |
| children | ReactNode | no |  | Composer input — typically `ChatComposerInput`. |
| className | string \| undefined | no |  | Additional CSS class names merged onto the root element. |

## Accessibility

Built on React Aria `TextField` / `TextArea`.
