Skip to content
Sign in
Chat layoutsNotch chatMenu

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.

ProA calendar with the island at the top: it listens, moves a meeting, shrinks back to a summary, then takes a follow up.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/notch-chat

Usage

tsx
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

ProIdle, listening, thinking, answering while collapsed, finished with a summary, failed, and open.

Props

PropTypeNotes
messagesChatcnMessage[]The thread. Pass useChatcnChat().chatcnMessages
status"ready" | "submitted" | "streaming" | "error"Same as AI SDK useChat
errorReactNodeShown with a retry. Say how to fix it
onSend, onStop, onRetry(text) => void, () => void, () => voidWire to your chat
listening, transcript, levelboolean, string, numberVoice input. Level from 0 to 1
onListenChange(listening) => voidAdds the microphone and the stop button
summarystringFinished pill text. Defaults to the first sentence
defaultFinishedbooleanStart on the summary pill
value, defaultValue, onValueChangestringThe input, controlled or not
open, defaultOpen, onOpenChangebooleanOpens on its own when an answer starts
holdMsnumberFinished answer stays open this long. Default 2400
finishedMsnumber | nullSummary pill stays this long. null keeps it. Default 8000
hotkey, hotkeyTargetstring | null, RefObjectDefault mod+j
onPhaseChange(phase: NotchPhase) => voididle, listening, thinking, working, open, finished, error
labelsPartial<NotchChatLabels>Every string, for translation

Keyboard

KeyAction
Cmd J or Ctrl JOpen or collapse the island
EnterSend
EscapeStop 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.