Chat Bubble
The ChatBubble kit is a full commenting/chat system — a message thread, a composer with @-mentions and emoji, quick reactions, a typing indicator, and a failed-turn affordance. It is a much larger surface than the name “ChatBubble” suggests: ChatBubble itself is only the floating avatar “chat head” that opens a conversation; the conversation panel itself is the Chat component.
Like Note and Region, none of these components live inside a CanvasItem by requirement — Chat is a normal floating/docked panel, most often placed inside a CanvasItem so it can sit next to whatever it is commenting on, or rendered outside the Canvas entirely for a docked sidebar.
Chat — the conversation panel
Section titled “Chat — the conversation panel”import { useState } from 'react';import { Guid } from '@cratis/fundamentals';import { Chat, ChatAuthorKind, type ChatMessage } from '@cratis/components/Canvas';
const me = Guid.create();
function Conversation() { const [messages, setMessages] = useState<ChatMessage[]>([ { id: Guid.create(), authorId: Guid.create(), authorName: 'Sample User', authorInitials: 'SU', hasAvatar: false, authorKind: ChatAuthorKind.User, text: 'What do you think of this layout?', timestamp: new Date(), }, ]);
const send = (text: string) => { setMessages(current => [...current, { id: Guid.create(), authorId: me, authorName: 'You', authorInitials: 'Y', hasAvatar: false, authorKind: ChatAuthorKind.User, text, timestamp: new Date(), }]); };
return ( <Chat messages={messages} onSend={send} onClose={() => {}} currentUserId={me} onReact={(messageId, emoji) => console.log('react', messageId.toString(), emoji)} /> );}messages, onSend, and onClose are the only required props — everything else is opt-in, and each opt-in feature is gated on its own prop rather than a single “mode” switch:
| Feature | Enabled by |
|---|---|
| Reactions | currentUserId and onReact both given |
Quick reply (prefills the composer with @name, followed by a space) | Always available once there is more than one author |
| “Turn into an action” button | onAct given (optionally filtered per-message by canAct) |
@-mentions in the composer | mentionCandidates given |
| Typing indicator | typingAuthors given (a list of who’s currently typing/working) |
| Avatar images (instead of initials) | buildAvatarUrl given |
| “Report a bug” link on a failed turn | buildReportUrl given |
variant (ChatVariant.Floating, the default, or ChatVariant.Docked) controls whether Chat renders its own header/close button (Floating) or leaves the frame and heading to whatever contains it (Docked, for a sidebar that already has its own).
Avatars — buildAvatarUrl
Section titled “Avatars — buildAvatarUrl”No component in this kit stores or fetches avatar images itself. Every place an avatar appears (Chat, ChatBubble, ChatComposer’s mention list, the typing indicator) accepts an optional buildAvatarUrl callback:
buildAvatarUrl={({ userId, ownerType, avatarSize, version }) => `/api/avatars/${ownerType}/${userId}?size=${avatarSize}&v=${version ?? 0}`}Omit it and every avatar falls back to the person’s initials on a color deterministically derived from their identifier — the same person always gets the same fallback color, with no image request ever attempted.
Reactions
Section titled “Reactions”Reactions are quick emoji given to a message (ChatMessage.reactions: ChatMessageReaction[], each grouping the users who gave that emoji). The reaction button opens a ReactionPicker: a row of recently-used emoji (remembered per-browser in localStorage by default, or wherever a memory: EmojiMemory you pass points instead) plus a button that opens the full EmojiPicker. Picking the emoji already given is how a reaction is taken back — onReact is called either way and the caller decides give/change/revoke from what was clicked.
Mentions
Section titled “Mentions”Passing mentionCandidates (people and agents who can be @-mentioned) turns on mention suggestions in the composer: typing @ opens a filtered MentionSuggestions list, navigable with the arrow keys and committed with Enter/Tab.
Typing indicator
Section titled “Typing indicator”typingAuthors: ChatTypingAuthor[] renders the classic jumping-dots row with the given authors’ avatars, composing an “X is typing” / “X and Y are typing” / “Several people are typing” label automatically. A person typing and an agent working on its answer are represented the same way on purpose.
Failed turns
Section titled “Failed turns”A message whose failureDetail is set (the turn ended in failure rather than an answer) never renders as a normal bubble — Chat renders a FailedReply line for it instead, saying plainly that the answer never came. It offers a “See error” link that opens the raw failure detail in a dialog, and — only when buildReportUrl is supplied — a “Report a bug” link built from the failure’s details. There is no built-in retry action; re-sending is left to whatever a host’s own composer/send flow already does.