# Editor

> Long writing in a field's frame, with a glass toolbar over selected words and pasting that keeps only clean HTML.

- Name: `editor`
- Status: beta
- Categories: forms
- File: the install writes `editor.tsx` to your `ui` alias, `@/components/ui` by default.
- Other registry items: `@arbokui/field-frame`, `@arbokui/tool-button`. The install adds them.
- Gallery: https://arbokui.com/components/editor
- Registry item: https://arbokui.com/r/editor.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/editor
   ```

## Use it for

- Newsletters, minutes, reports, and announcements: writing with headings, lists, and links.
- Save from onChange. It gets clean HTML, "" when empty, and the words.
- Show saving with status, like a StatusText that says Saved 18:04. It ends the label's line.
- Show the saved HTML anywhere with Prose, in the same look.

## Avoid it for

- A message to people. Use Composer.
- A line or two of plain text. Use Textarea or Input.
- Rich layouts, tables, or images. It keeps paragraphs, headings, lists, bold, italic, and links only.

## Props

- `value`: `string`. HTML. Controlled: the editor only reloads when this differs from what it last sent.
- `defaultValue`: `string`. HTML to start with.
- `onChange`: `(html: string, info: { text: string; words: number }) => void`. Clean HTML ("" when empty) and the words, on every change.
- `label`: `ReactNode`. Names the writing area.
- `hint`: `ReactNode`. A short note under the writing area. It describes it.
- `error`: `ReactNode`. Replaces the hint while set, in red, and marks the writing area invalid.
- `placeholder`: `string`. Shown while it is empty. Never sent.
- `rows`: `number`. Default `6`. The least height, in lines. It grows with its words.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Padding and label size. The words stay 13/18 and headings 16/20.
- `disabled`: `boolean`. Default `false`. Locks the words.
- `showCount`: `boolean`. Default `false`. A word count under the right edge.
- `status`: `ReactNode`. Ends the label's line, like a saving status.
- `ref`: `Ref<HTMLDivElement>`. Reaches the writing area.
- `id`: `string`. Goes on the writing area, so the label still points at it. aria-\*, data-\*, and handlers go there too.
- `className`: `string`. Extra classes on the frame. Later classes win.

## Accessibility

- The writing area is a role="textbox" with aria-multiline, named by the label and described by the hint or error.
- Select words and a toolbar shows over them. Alt+F10 moves into it. It is one Tab stop, and ← → Home and End move along it.
- Each button says its state with aria-pressed. Its tooltip teaches its key: ⌘B, ⌘I, ⌘K, or # - or 1. then Space at the start of a line.
- Esc steps back: out of the link field, then from the toolbar to the selected words, then it hides the toolbar. In a Sheet, that Esc never closes the sheet.
- A link asks for its address in the toolbar. Enter applies it, and empty removes it. Only web and email addresses are linked.
- Under reduced motion the toolbar moves at once.

## Examples

### A newsletter that saves as you write

```tsx
<Editor
  label="Newsletter · October"
  value={html}
  showCount
  status={<StatusText tone="success">Saved 18:04</StatusText>}
  onChange={(next) => save(next)}
/>
```

### Showing it

```tsx
<Prose html={newsletter.html} />
```
