Notch chatPro
A compact island at the top center that listens, thinks, opens into a chat while it answers, and shrinks back to a one line summary.
On this page
Installation
Usage
import { NotchChat } from "@/components/ui/chatcn/pro/notch-chat"
<NotchChat
messages={chat.chatcnMessages}
status={chat.status}
onSend={(text) => chat.sendMessage({ text })}
onStop={chat.stop}
listening={voice.listening}
transcript={voice.transcript}
level={voice.level}
onListenChange={voice.toggle}
/>Each phase has its own shape. Idle is a small pill with its shortcut. Listening widens to show the words it hears, with the oldest fading out at the start edge and a meter driven by level (nothing loops on its own). Thinking narrows to one word and a seconds count. When the answer starts streaming the island opens into the thread; once it finishes, it holds for holdMs and shrinks back to a pill with a one line summary (the first sentence of the reply, or summary). Pointing at the island or typing in it keeps it open. Click the summary to open the thread again. The island keeps a dark scope in both themes, like the hardware it borrows from.
Examples
States
Props
| Prop | Type | Notes |
|---|---|---|
messages | ChatcnMessage[] | The thread. Pass useChatcnChat().chatcnMessages |
status | "ready" | "submitted" | "streaming" | "error" | Same as AI SDK useChat |
error | ReactNode | Shown with a retry. Say how to fix it |
onSend, onStop, onRetry | (text) => void, () => void, () => void | Wire to your chat |
listening, transcript, level | boolean, string, number | Voice input. Level from 0 to 1 |
onListenChange | (listening) => void | Adds the microphone and the stop button |
summary | string | Finished pill text. Defaults to the first sentence |
defaultFinished | boolean | Start on the summary pill |
value, defaultValue, onValueChange | string | The input, controlled or not |
open, defaultOpen, onOpenChange | boolean | Opens on its own when an answer starts |
holdMs | number | Finished answer stays open this long. Default 2400 |
finishedMs | number | null | Summary pill stays this long. null keeps it. Default 8000 |
hotkey, hotkeyTarget | string | null, RefObject | Default mod+j |
onPhaseChange | (phase: NotchPhase) => void | idle, listening, thinking, working, open, finished, error |
labels | Partial<NotchChatLabels> | Every string, for translation |
Keyboard
| Key | Action |
|---|---|
| Cmd J or Ctrl J | Open or collapse the island |
| Enter | Send |
| Escape | Stop the answer while it streams, otherwise collapse |
Accessibility
- A polite live region announces listening, thinking, the finished summary and failures, never token by token.
- The open thread is a labelled log; the collapse button reports
aria-expanded. - Phases carry a word or an icon with text, never color alone.
- In forced colors the island keeps a solid outline.
Motion
Width, height and corner radius move together on a critically damped spring sampled into a CSS linear() curve (300 ms, no overshoot), so a phase change midway retargets from where the island is. Contents crossfade over 150 ms once the shape has moved. Reduced motion snaps the shape and keeps the fades.