AI chat message bubble
Baseline message bubble for any AI chat surface in Koder Stack: user vs assistant variants, mandatory AI disclaimer chip on assistant messages, copy/edit/regenerate/branch actions, multi-modal content hosting (text, code, image, citations, tool invocations).
Spec — AI chat message bubble
Baseline component consumido por todo produto AI Koder. Hosts streaming text (cross
link [`streamingtext.kmd](streaming-text.kmd)), tool invocations (cross-link [mcptoolinvocation.kmd](mcp-tool-invocation.kmd)), code blocks, citations, e disclaimer (cross-link [aidisclaimer.kmd`](aidisclaimer.kmd)).
Princípios
- *wo-role taxonomy*— user vs assistant. Sem variantes "system" ou "tool" no bubble visual (tool calls são inline content do assistant bubble, não bubbles próprios).
- *isclaimer is non-negotiable*— todo assistant bubble carrega disclaimer chip per #119. Sem exceção em produto distribuído.
- *omposable content*— bubble é container; renderers de textcodeimagecitationtool são plug-ins.
- *ctions where the user expects them*— toolbar inline (hover desktop / always-visible mobile) com Copy / Edit / Regenerate / Branch.
R1 — Anatomia
R1.1 — User bubble
┌──────────────────────┐
│ "What's the weather?"│
└──────────────────────┘
14:32 · ✎- Alignment: right (LTR) / left (RTL per
i18n/contract.kmd). - Tint:
primary-container(perthemes/color-roles.kmd). - Shape:
shape-corner-largeexceto trailingbottom corner (`shapecorner-small`) — tail. - Avatar: opcional (off by default; product configurável).
- Footer chips: timestamp · edit pencil (R3.2).
R1.2 — Assistant bubble
[avatar] ┌──────────────────────────────────────────┐
│ "It's 22°C in São Paulo right now." │
│ │
│ [mcp-tool-card if any] │
│ [code-block if any] │
│ [citation[1]] inline │
│ │
│ — Sources │
│ [1] weather.com/sp │
└──────────────────────────────────────────┘
🤖 Generated by AI — verify · 14:32 · ⎘ ✎ ↻ ⑂- Alignment: left (LTR) / right (RTL).
- Tint:
surface(perthemes/color-roles.kmd). - Shape:
shape-corner-largeexceto leading-bottom corner. - Avatar: assistant icon (configurável: model logo OR product logo OR generic ✨).
- Disclaimer chip: SEMPRE presente (cross
link `aidisclaimer.kmd` per tier). - Footer chips: timestamp · Copy ⎘ · Edit ✎ · Regenerate ↻ · Branch ⑂.
R2 — Mandatory disclaimer chip
ai-disclaimer.kmd R1 define 3 tiers (label / label+modal / label+banner+confirmation). Bubble hosts o *abel*form:
| Risk tier | Chip placement | Copy key |
|---|---|---|
| low (default) | Footer chip; subtle (text-muted) |
ai.disclaimer.label.low |
| medium | Footer chip; emphasized (warning) |
ai.disclaimer.label.medium |
| high | Banner ACIMA do bubble (não chip footer) | ai.disclaimer.label.high |
Chip clickable → expand modal explicativo (#119 R1 tier 2 behavior; tier 1 = chip-only).
Per feedback_kds_owner_curated_content: copy editorial não editável por IA autonomamente.
R3 — Actions
R3.1 — User actions
| Action | Behavior |
|---|---|
| Edit ✎ | Re |
R3.2 — Assistant actions
| Action | Behavior |
|---|---|
| Copy ⎘ | Copia content como markdown (preservando code blocks, citations footnotes per citations.kmd R4). |
| Edit ✎ | Permite override do assistant message (rare; admin/debug surfaces only — default OFF). |
| Regenerate ↻ | Re-invoke gateway com same context; nova resposta substitui esta. |
| Branch ⑂ | Forka conversa daqui; cria nova conversation history entry com same prefix (cross |
Hover surface: actions reveal on desktop hover; mobile: actions always visible (touch).
R4 — States
| State | Visual |
|---|---|
| streaming | Cursor visible (streaming-text.kmd R2); footer disabled exceto Stop button |
| complete | Footer actions enabled; disclaimer rendered |
| error | Red border + error icon + "Retry" button (replaces Regenerate); error message in body |
| edited | "✎ Edited" badge no footer + edit history accessible via long |
R5 — Multi-modal content hosting
Bubble body é *ontainer*que renderiza array de content items (mesma estrutura do MCP tools/call content). Renderers:
| Content type | Renderer | Spec |
|---|---|---|
text |
Markdown incremental + citation inline | streaming-text.kmd, citations.kmd |
code |
Syntax highlight + actions | code-block.kmd |
image |
KoderImage (tap-fullscreen) | media/image.kmd |
mcp_tool_call |
Tool card collapsible | mcp-tool-invocation.kmd |
citation_list |
Sidebar/footer footnotes | citations.kmd |
kvg |
Koder Vector Graphics (declarative) | kvg/ spec (existente) |
Render order = array order from gateway response. Streaming: content items appear progressivamente.
R6 — Surface bindings
| Surface | API |
|---|---|
| Flutter | KoderAIMessageBubble({required role, required content, onAction}) em koder_kit/lib/src/ai/ai_message_bubble.dart |
| Web | <koder-ai-message-bubble role="..." content="..."> em koder_web_kit |
| Compose Android | KoderAIMessageBubble em koder-design-compose (futuro) |
| SwiftUI iOS | KoderAIMessageBubble em koder-design-swift (futuro) |
| CLI / TUI | Texto plain: prefix > user, < assistant; disclaimer linha separada |
API consistent: role: "user" | "assistant", content: List<ContentItem>, state: streaming|complete|error, onAction: callback(action_id).
R7 — Acessibilidade
- Bubble é
<article role="region" aria-label="Message from {role}">. - Avatar tem
aria-hidden="true"(role já anunciado). - Disclaimer chip lido por screen reader como part of bubble: aria-describedby.
- Actions: keyboard
accessible via Tab; arialabel per action. - Streaming state: aria-live="polite" announces "Generating…" → silent during stream → "Done" at end.
- Edited state: aria-label includes "edited".
- Reduced-motion: no entrance animation.
- Touch targets ≥48dp.
R8 — i18n
| Key | en-US | pt-BR |
|---|---|---|
ai.bubble.role.user |
"You" | "Você" |
ai.bubble.role.assistant |
"Assistant" | "Assistente" |
ai.bubble.action.copy |
"Copy" | "Copiar" |
ai.bubble.action.edit |
"Edit" | "Editar" |
ai.bubble.action.regenerate |
"Regenerate" | "Gerar de novo" |
ai.bubble.action.branch |
"Branch" | "Ramificar" |
ai.bubble.action.retry |
"Retry" | "Tentar novamente" |
ai.bubble.state.streaming |
"Generating…" | "Gerando…" |
ai.bubble.state.edited |
"Edited" | "Editado" |
ai.bubble.action.copy.confirm |
"Copied to clipboard" | "Copiado" |
(Disclaimer copy lives em ai-disclaimer.kmd R7.)
R9 — Multi-tenant + persistência
Message stored per policies/multi-tenant-by-default.kmd:
- Key:
(koder_user_id, workspace_id, conversation_id, message_id). - Edit history mantém versions (linear); rollback via long-press menu.
- Cross
tenant lookup 404 per multitenancy contract. - Delete: cascata per
policies/identity-data-retention.kmdR5 (24h grace + cascade).
R10 — Per-preset variation
| Preset | Bubble style |
|---|---|
material3 / material_expressive |
Defaults; shape |
material2 |
shape |
terminal_classic |
No bubbles — > prompt / < response prefix; monospace |
brutalist |
Sharp corners; 2px border; no shadow |
cyberpunk_neon |
Glow outline; gradient backdrop |
glassmorphism |
Backdrop blur 20%; transparent tint |
minimalist_mono |
Mono font; horizontal divider entre roles em vez de bubbles |
T-suite
- *1*Mount user: render user bubble com text content; alignment right (LTR).
- *2*Mount assistant: render assistant bubble com text + disclaimer chip visible.
- *3*Multi
modal: render assistant com text + codeblock + tool-call + citation; ordem preserved. - *4*Action Copy: tap copy → clipboard content includes citations as markdown footnotes.
- *5*Action Regenerate: tap → bubble enters streaming state; new content replaces.
- *6*Action Branch: tap → new conversation created with prefix up to this message.
- *7*Action Edit (user): tap → composer pre-filled; submit invalidates downstream cascade.
- *8*Error state: simulate gateway error → red border + Retry button visible.
- *9*RTL: render em locale rtl-* → alignment flips.
- *10*A11y: screen reader announces role + content + disclaimer + actions list.
- *11*Disclaimer always present: render assistant bubble without disclaimer prop → spec validation FAILS (compile-time or runtime error).
- *1*Cross-tenant access: try to load message from another workspace → 404.
Cross-link
- Companion:
streaming-text.kmd,ai-disclaimer.kmd,mcp-tool-invocation.kmd,citations.kmd,code-block.kmd - Policies:
multi-tenant-by-default.kmd,identity-data-retention.kmd - Tokens:
themes/color-roles.kmd,themes/typography.kmd,themes/shape.kmd - i18n:
specs/i18n/contract.kmd - Backend:
services/ai/chat-adapter/(wire protocol)