# Undo and redo actions are available for message edits

- **Pattern:** `ab-000338` (`ai-ux-patterns.feedback-control.undo-redo-actions`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000338
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000338)

## Why it matters

One mis-click on a delete button that wipes a forty-message research conversation is the kind of moment that ends a subscription. Destructive actions without undo treat every user gesture as final, which is wrong for a surface where the data is valuable, the buttons are dense, and mobile accidental taps are routine. A simple 5-second undo toast costs almost nothing to build and prevents the single worst user experience your product can produce.

## Severity rationale

Medium because the loss is recoverable only if you have backups, and most chat apps do not expose them.

## Remediation

Wrap every destructive action in an optimistic state update plus a toast with an "Undo" action and a 5-second duration. Keep the deleted record in memory during the window and re-insert at its original index on undo. For heavier flows (bulk clear), move to a two-step confirm dialog. Implement in `src/lib/hooks/use-undoable-delete.ts`.

```tsx
toast('Message deleted', { action: { label: 'Undo', onClick: () => restore(deleted) }, duration: 5000 })
```

## Detection

- **ID:** `undo-redo-actions`
- **Severity:** `medium`
- **What to look for:** Count all destructive conversation actions (message deletion, conversation clearing, edit-and-regenerate). For each, enumerate whether an undo mechanism exists: Cmd+Z handler, "restore" toast, undo button, or conversation version history. Look for state history arrays or a history management hook (e.g., `useReducer` with an action history, a zustand store with undo middleware, or a custom history stack). At least 1 destructive action must have an undo path.
- **Pass criteria:** Users can undo at least the most recent destructive action in a conversation (message deletion or edit). A "restore" toast with an undo button after deletion is an acceptable implementation. At least 1 undo mechanism must be wired to a destructive action. Report on pass: "X of Y destructive actions have undo support."
- **Fail criteria:** Destructive actions (deleting messages, clearing a conversation) are permanent with no undo option. No history management detected.
- **Skip (N/A) when:** Same as `regeneration-button`.
- **Detail on fail:** `"No undo mechanism for message operations — deletions appear permanent. No history stack or undo toast pattern detected."`.
- **Remediation:** Accidental message deletions are a common frustration. A simple undo-toast pattern is low effort and high value:

  ```tsx
  const handleDeleteMessage = (messageId: string) => {
    const deletedMessage = messages.find(m => m.id === messageId)
    const newMessages = messages.filter(m => m.id !== messageId)
    setMessages(newMessages)

    toast("Message deleted", {
      action: {
        label: "Undo",
        onClick: () => setMessages(prev => {
          const index = newMessages.findIndex(/* insertion point */)
          return [...prev.slice(0, index), deletedMessage, ...prev.slice(index)]
        })
      },
      duration: 5000,
    })
  }
  ```

---

Taxons: user-experience

HTML version: https://auditbuffet.com/patterns/ab-000338
