AI chat message bubble

mandatory

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 (crosslink [`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

  1. *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).
  2. *isclaimer is non-negotiable*— todo assistant bubble carrega disclaimer chip per #119. Sem exceção em produto distribuído.
  3. *omposable content*— bubble é container; renderers de textcodeimagecitationtool são plug-ins.
  4. *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 (per themes/color-roles.kmd).
  • Shape: shape-corner-large exceto 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 (per themes/color-roles.kmd).
  • Shape: shape-corner-large exceto leading-bottom corner.
  • Avatar: assistant icon (configurável: model logo OR product logo OR generic ✨).
  • Disclaimer chip: SEMPRE presente (crosslink `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 ✎ Reopens composer prépreenchido com prompt original; submit substitui esta mensagem + invalida cascata downstream (assistant responses subsequentes).

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 (crosslink `conversationhistory.kmd` #115).

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 longpress/rightclick

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: keyboardaccessible 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.
  • Crosstenant lookup 404 per multitenancy contract.
  • Delete: cascata per policies/identity-data-retention.kmd R5 (24h grace + cascade).

R10 — Per-preset variation

Preset Bubble style
material3 / material_expressive Defaults; shapecornerlarge; spring entrance
material2 shapecornermedium
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*Multimodal: 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.

Source: ../home/koder/dev/koder/meta/docs/stack/specs/ai-ui/chat-message-bubble.kmd