# User message actions

The row under the person's own message: Copy, and Edit, which hands the app the message's words to change and send again. Edit doesn't change the message: the app puts the words back in the composer (or its own editor), and what the person sends is a new message, with this one left in the thread, so the app keeps the versions and pages through them with `MessageBranches`, given as `children`. Each action appears only when its handler is given; nothing is chosen for the person.

It goes where Message draws the line under a person's own message, `meta`, beside the app's own name and time:

```
<Message message={m} meta={<UserMessageActions text={wordsOf(m)} onEdit={(text) => composer.edit(m.id, text)} />} />
```

- Registry name: `user-message-actions` (`@candor/user-message-actions`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/user-message-actions

## Install

```bash
npx candor-ui add user-message-actions
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/user-message-actions`.
It brings in: locale, message-actions, tokens, tooltip, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import { Message } from "@/components/candor/message"
import { MessageBranches } from "@/components/candor/message-branches"
import { UserMessageActions } from "@/components/candor/user-message-actions"

/*
 * The app keeps the versions: Edit hands back the words (here they'd go to the composer), and what the person sends
 * is a new message. MessageBranches pages through the versions the app kept.
 */
const versions = ["Draft the launch note", "Draft the launch note for the EU team"]

export default function Example() {
  const [index, setIndex] = React.useState(versions.length - 1)
  const [editing, setEditing] = React.useState<string | null>(null)
  const text = versions[index]!
  return (
    <div className="flex max-w-md flex-col gap-2 text-sm">
      <Message
        message={{ id: "m1", author: { role: "user" }, status: "complete", blocks: [{ kind: "plain", text }] }}
        meta={
          <UserMessageActions text={text} onEdit={setEditing}>
            <MessageBranches index={index} count={versions.length} onValueChange={setIndex} />
          </UserMessageActions>
        }
      />
      <p className="text-xs text-muted-foreground">{editing === null ? "Choose Edit to put the words back in the composer." : `In the composer: “${editing}”`}</p>
    </div>
  )
}
```

## <UserMessageActions>

Props:

- `text?: string | undefined`: The message's words: what Copy copies, and what Edit hands back. Without it there's no Copy.
- `onEdit?: ((text: string) => void) | undefined`: Shows Edit, with `text`: called with it when the person chooses Edit, to put in the composer for a new message.
- `children?: React.ReactNode | undefined`: After the actions, e.g. `MessageBranches` for the versions the app keeps, or its own name and time.
- `className?: string | undefined`: Classes for the row, merged with Candor's.

## Strings (`userMessageActions`)

The fixed words, English by default. Override any of them under `userMessageActions` in `<LocaleProvider strings={{ userMessageActions: { … } }}>`; the table is exported as `userMessageActionsStrings`.

- `edit`: Edit and send again
- `editHint`: Sends your edited words as a new message. This one stays as it is.

## Motion

- **tint** (tint): Each button's background and text change when the pointer is over it.

Set with `--cd-motion-user-message-actions-<moment>-*` in your brand file; the families are in the design reference.

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
