# Actions

Actions [#actions]

The **Actions** primitives are message-level controls. `Actions` is the row (with optional hover-reveal); `Action` is an accessible icon button with a tooltip; `CopyAction` handles clipboard + copied state; `RegenerateAction` and `ReadAloudAction` (wire to local Kokoro TTS) cover common cases; `ActionsMenu` groups secondary actions behind a "more" menu; and `FeedbackBar` is an on-device thumbs up/down that stays local — no telemetry.

Preview [#preview]

```tsx
'use client';

/**
 * @file actions-demo.tsx
 * @description Docs preview for `Actions`. Copy to clipboard (with copied
 * state), regenerate, read-aloud, an overflow menu, and an on-device feedback
 * bar — all local, no telemetry.
 */
import * as React from 'react';
import { Flag, Share2 } from 'lucide-react';
import {
  Actions,
  ActionsMenu,
  CopyAction,
  FeedbackBar,
  ReadAloudAction,
  RegenerateAction,
} from '@/components/actions';

export default function ActionsDemo() {
  const [feedback, setFeedback] = React.useState<string | null>(null);
  const text = 'LocalMode runs AI models entirely in your browser - no servers.';

  return (
    <div className="group flex w-full max-w-md flex-col gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground">
      <p className="text-sm">{text}</p>
      <Actions>
        <CopyAction text={text} />
        <RegenerateAction onRegenerate={() => setFeedback('regenerated')} />
        <ReadAloudAction onReadAloud={() => setFeedback('read aloud')} />
        <FeedbackBar onFeedback={(v) => setFeedback(`feedback: ${v}`)} />
        <ActionsMenu
          items={[
            { id: 'share', label: 'Share', icon: <Share2 className="size-4" />, onSelect: () => setFeedback('share') },
            { id: 'report', label: 'Report', icon: <Flag className="size-4" />, onSelect: () => setFeedback('report') },
          ]}
        />
      </Actions>
      {feedback && (
        <p className="text-xs text-muted-foreground">Last action: {feedback}</p>
      )}
    </div>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/conversation/actions
```

Data source & dependencies [#data-source--dependencies]

**Data source:** renders plain props and emits callbacks (`onRegenerate`, read-aloud) — works with any backend. Recommended producer: your chat + `useSynthesizeSpeech` from `@localmode/react` (on-device, optional).

* `clsx` + `tailwind-merge` — via the shared `cn()` util (installed automatically as a registry dependency)

Files installed [#files-installed]

* `actions.tsx` — `Actions`, `Action`, `CopyAction`, `RegenerateAction`, `ReadAloudAction`, `ActionsMenu`, `FeedbackBar`
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**CopyAction**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | — | **Required.** Text to copy to the clipboard. |
| `label` | `string` | `"Copy"` | Tooltip label. |
| `variant` | `"ghost" \| "secondary" \| "outline" \| "destructive" \| "default" \| "link" \| null` | — | — |
| `size` | `"icon-lg" \| "icon-sm" \| "icon-xs" \| "icon" \| "lg" \| "sm" \| "xs" \| "default" \| null` | — | — |
| `asChild` | `boolean` | — | — |

Examples [#examples]

A message action row [#a-message-action-row]

```tsx
import { Actions, CopyAction, FeedbackBar, RegenerateAction } from '@/components/actions';

<Actions hoverReveal>
  <CopyAction text={message.text} />
  <RegenerateAction onRegenerate={() => regenerate(message.id)} />
  <FeedbackBar onFeedback={(v) => saveFeedbackLocally(message.id, v)} />
</Actions>
```

Customization [#customization]

`Action` wraps a `TooltipProvider` so it works standalone. `FeedbackBar` reports via callback only — persist it to local IndexedDB if you want it to survive reloads. Read-aloud is presentational: drive `onReadAloud` from `useSynthesizeSpeech`.

These primitives are presentational and hook-driven: they render props and emit callbacks, holding only local view state. The orchestration state (e.g. `useChat`) lives in your app. Every surface uses shadcn/ui CSS-variable utilities, so it inherits your theme — restyle the copied file freely.