Agent notification
Toasts for needs your input, done and failed that stack, fan out on hover and swipe away, plus a hook for desktop notifications while the tab is hidden.
On this page
Needs input, done and failed toasts that stack, fan out on hover and swipe away, plus desktop notifications.
Installation
Usage
import { AgentNotifications, notifyAgent } from "@/components/ui/chatcn/agent-notification"
// Once, near the root
<AgentNotifications />
// Anywhere
notifyAgent({
kind: "needs-input",
title: "Needs your input",
description: "Fix flaky checkout test wants to run pnpm db:migrate.",
action: { label: "Review", onClick: () => openApproval(id) },
})Needs input stays until handled; done leaves after 6 s and failed after 8 s. Pass an existing id to update a notification in place.
Desktop notifications
import { useChatcnRunNotification } from "@/hooks/use-chatcn-run-notification"
const system = useChatcnRunNotification()
// From a click: system.request()
system.notify({ title: "Agent finished", body: "4 files changed", tag: run.id })The hook notifies only while the tab is hidden, counts pending notifications in the tab title and focuses the tab on click.
Examples
Kinds
The four notification kinds as static cards.
Props
| Prop | Type | Notes |
|---|---|---|
kind | needs-input | done | failed | info | |
title, description, run | ReactNode | Description clamps to two lines |
action, secondaryAction | { label, onClick } | Clicking dismisses |
duration | number | Ms. Infinity keeps it |
position | bottom-right | bottom-left | bottom-center | top-* | Viewport prop |
visible, gap, expand, offset, hotkey | Viewport props. Defaults 3, 14, false, 24, Alt+T |
Keyboard
| Key | Action |
|---|---|
Alt+T | Move focus to the notifications |
Tab | Actions and Dismiss. Focus pauses every timer |
Accessibility
- Needs input uses
role=alert; the others userole=status. - Every swipe has a button: Dismiss is always reachable by keyboard and always visible on touch.
- Timers pause while hovered, focused or while the tab is hidden, so nobody misses a request.
Motion
- Cards enter with translateY and opacity over 400 ms
ease, stack 14 px apart with 5 percent less scale each, and fan out on hover. - A swipe tracks the finger after 10 px and dismisses past 45 px or 0.11 px per ms, leaving the way it was thrown.
- Under reduced motion only opacity transitions.