Messages
Conversation turns, with no avatar and no meta row. The name sits inside the bubble on the first message of a run, and the time plus delivery glyph sit in its bottom-right corner.
A conversation
Consecutive turns from one sender collapse into a run: one name, one squared tail corner on the last bubble. A day divider replaces per-message relative time.
Activity events
System events sit quietly between messages without pretending to be messages — one centred line, no box, border or shadow. Exact-timestamp activities share one line and time, while each linked record keeps its own leading 12px icon. The time reads "at 17:39" for something that happened today and "on 13 Sep" otherwise.
Delivery states
Success states are glyphs (a hollow clock, one tick, two ticks, two accent ticks) because they are glanced at, not read. Failure is the one state that takes words, next to the bubble that failed, because it is the only one needing action.
Failure and delivery details
A failed send shows the reason and, when it is available, the actions that unblock it — Try again, Send a template once the window has closed, and Copy text, always. Every action dispatches an intent event (koala-message-retry, koala-message-send-template, koala-message-copy) with the message id; the caller decides what happens next. Clicking a delivered message's time opens the exact sent/delivered/read times in an inline row.
Reactions
An inbound reaction renders as a chip overlapping the bubble's lower edge, with a count once more than one person has reacted the same way. Clicking a chip reveals who reacted. Staff cannot react yet (decision D7) — only inbound reactions render.
A sent template
A sent template names itself on the sender line — "Priya · Template: Quote ready" — rather than a generic tag, and keeps naming itself on every bubble of a run, even the ones a plain message would fold the name away from. It renders the way the client actually received it: bold header or a media stub, the body, a muted footer, and its buttons as full-width rows. A template sent before the snapshot existed still reads as a template, just with no name. The snapshot is what the client saw even if the template is edited afterwards.
Jumbo emoji
A message of one to three emoji and nothing else renders at display size with no bubble fill — the emoji reads as the whole message, not as decoration inside one.
Seen by
Who has read the latest inbound message, and when — the first viewer named with a time, the rest listed by name. Built entirely from per-user read state; it carries no data of its own beyond who and when.
An unverified name
A name the sender chose for themselves and we could not match to any record. Rendered as a quotation after the address we do trust, never in place of it. Presenting an unverified name as fact is how a stranger gets to look like a colleague.
Dividers
A calendar day, and the first unread message. Both resolve client-side, so a day divider can't say Today the morning after.
The assistant
Same component, same geometry. Joey is identified by name without an avatar or animated glyph; the corner carries the time alone, since an assistant answer has no delivery state.
Linear layout
Every turn on the left, for a log where direction carries no meaning.
Message preview
A chosen template waits at the latest edge of the conversation, where the next reply would appear. Each value is typed straight into the sentence; a button's own reference sits inline on its row, showing only the reference itself, never the underlying link. Suggestions follow whichever slot is still empty, and Send stays where it always is, in the composer.
Message actions
Messages can carry compact links to the record or task they refer to. The action row stays inside the bubble and below the message body.
API
MessageView (koala-message)SendingSentDeliveredReadFailedTone? — shared surface vocabulary for a message bubble.Caller-authored rendered attachment markup.MessageGrouping (set by KoalaThreadTagHelper.Group)string? (unverified, self-declared name)MessageContextView? (label + icon inside the bubble)string?TwoSidedLinear (koala-thread)string? (koala-thread)DateTimeOffset? (koala-thread-divider)string? (koala-thread-divider)NeutralPrimary (koala-thread-divider)CenterStart (koala-thread-divider) — Start over a single column such as notes.MessageActivityView (koala-thread-activity)Caller-authored rendered link markup (MessageActivityItemView).string (koala-message-input)string? (koala-message-input)Slot — caller-authored message actions.string (koala-message-preview)IReadOnlyList<ReactionView>? — emoji, count, reactor names. Inbound only (D7).MessageTemplateView? — name, header text/media kind, footer, buttons. Supersedes TemplateButtons.IReadOnlyList<MessageDeliveryTimelineEntry>? — opens the inline details row when set.MessageFailureView? — reason, CanRetry, CanSendTemplate.MessageSeenView (koala-message-seen)