# Build the "Inline action menu" pattern: `SelectionActionMenu`

## Stack (edit this block to match your project)
- React 18+ with TypeScript: one function component, typed props, no `any`.
- Tailwind CSS, using your design system's tokens if you have them.
- Presentational only: data comes in through props, decisions go out through callbacks. No fetching or global state inside.
- If your codebase already has conventions (naming, shared components, tokens), follow them over this spec.

## What the pattern is for
Select content, get AI verbs scoped to that selection — rewrite, explain, extend, translate.

Use it when: Document, code and canvas editors where the object of the action is whatever is highlighted.

## What to build
A small menu that appears next to a selection in an editor and offers AI actions that fit what was selected. It sends the selection plus its surrounding paragraph and shows the proposed change in place for accept or discard. It renders as a floating toolbar anchored to the selection.

### Props
- `selection`: { text: string; kind: "word" | "phrase" | "sentence" | "paragraph" | "multi" | "code"; context: string; rect: DOMRect }, where `context` is the surrounding paragraph or block.
- `actions`: Array<{ id: string; label: string; kinds: Array<"word" | "phrase" | "sentence" | "paragraph" | "multi" | "code"> }>, each action lists the selection kinds it applies to.
- `allowCustom?`: boolean, shows a free-text instruction field scoped to the selection.
- `status`: "menu" | "running" | "preview" | "error".
- `preview?`: { original: string; proposed: string }, the suggested replacement.
- `onAction(id: string, payload: { text: string; context: string; instruction?: string })`: the component never edits the document itself.
- `onAccept()`, `onDiscard()` and `onClose()`: callbacks for the preview and menu.

### States it must render
- Menu: only actions whose `kinds` include the selection kind, with a label naming the selection ("1 word", "2 sentences").
- Running: the selection stays highlighted with an AI accent and a busy label; the menu offers Cancel.
- Preview: the original struck through or dimmed and the proposed text beside it, with Accept and Discard.
- Error: a short message in the menu with retry; the document is untouched.

### Interactions
- Choosing an action calls `onAction` with the selection text and its context.
- Accept calls `onAccept` and closes; Discard restores the view with the original intact.
- Escape closes the menu (or discards a preview) and returns focus to the editor with the selection kept.
- Changing the selection while the menu is open re-filters the actions.

### Edge cases
- A single word: offer actions like define, synonyms or translate; summarise and extend are hidden because their `kinds` exclude "word".
- A selection across several paragraphs or blocks: kind is "multi" and the preview shows each block's change.
- No action matches the kind: show only the custom instruction field, or nothing if `allowCustom` is false.
- The menu near the viewport edge: flip above or below the selection so it never covers the selected text.

## Must avoid
A verb list that ignores the selection type, offering "summarise" on a single word.

## Engineering constraint
Pass the selection plus surrounding context; users expect awareness of the paragraph, not just the span.

## Accessibility
- The menu is a `menu` with `menuitem`s, reachable by a keyboard shortcut from the selection, with arrow-key navigation.
- In preview, announce "Suggested change ready" and put focus on Accept.

## House rules
- Real `<button>`/`<input>` elements, full keyboard use, visible focus; announce status changes with `aria-live="polite"`; respect `prefers-reduced-motion`.
- All user-facing text comes from props (or a `labels` prop) so it can be translated.
- One consistent accent marks anything the AI made or is doing; amber means "needs a person", red means risky, green means done. Never rely on colour alone.
- Fits a 360 px wide container without horizontal scrolling.

## Acceptance checks
- [ ] Every state above can be rendered by passing props, with no internal fake data.
- [ ] Every interaction and edge case above behaves as described.
- [ ] Nothing in "Must avoid" can happen with this component.

## Deliver
1. The component file with its exported prop types.
2. A short usage example that renders each state.
