Skip to content
RunsAgent notificationMenu

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.

Needs input, done and failed toasts that stack, fan out on hover and swipe away, plus desktop notifications.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/agent-notification.json

Usage

tsx
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

tsx
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

PropTypeNotes
kindneeds-input | done | failed | info
title, description, runReactNodeDescription clamps to two lines
action, secondaryAction{ label, onClick }Clicking dismisses
durationnumberMs. Infinity keeps it
positionbottom-right | bottom-left | bottom-center | top-*Viewport prop
visible, gap, expand, offset, hotkeyViewport props. Defaults 3, 14, false, 24, Alt+T

Keyboard

KeyAction
Alt+TMove focus to the notifications
TabActions and Dismiss. Focus pauses every timer

Accessibility

  • Needs input uses role=alert; the others use role=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.