Skip to content

D-14: Message views — UserMessageView + AssistantMessageView with live caret #264

@kirich1409

Description

@kirich1409

Description

Implement the two message views for Dialogue — user bubble (right side) and assistant bubble (left side, with streaming caret, fade-in, thinking block, tool call markers).

Spec: Epic #250 §6 (UI concept — message level live feel).

Scope

Files

  • UserMessageView.swift — plain right-aligned bubble.
  • AssistantMessageView.swift — left-aligned, contains markdown rendering + tool call markers + thinking block.
  • LiveCaret.swift — reusable blinking cursor component.

UserMessageView

```swift
public struct UserMessageView: View {
public let message: AgentMessage
}
```

  • Renders message.content as plain text (user input is not markdown-formatted in MVP).
  • Right-aligned with HStack { Spacer() }.
  • Card(style: .plain) wrapper from DS.
  • Max width 80% of available — otherwise balloons with 1-char messages.

AssistantMessageView

```swift
public struct AssistantMessageView: View {
public let message: AgentMessage
public let toolCalls: IdentifiedArrayOf // to look up by ID
public let isStreaming: Bool
}
```

  • Renders message.content[] segments in order:
    • .text(String)DialogueMarkdownView(source: text, streaming: isStreaming, cache: shared).
    • .thinking(Thinking)ThinkingIndicatorView (D-17).
    • .toolCall(ToolCallID) → lookup in toolCalls, render via ToolCallCardView (D-15/D-16).
    • .citation → inline footnote marker.
  • Live caret at end of last .text segment while isStreaming == true.
  • Fade-in animation on message appear (.transition(.opacity.combined(with: .move(edge: .bottom))), DS.Motion.fast).
  • Active-streaming glow pulse (1–2% opacity tint overlay) — guarded by accessibilityReduceMotion.

LiveCaret

Small character with .opacity animation (0.3 ↔ 1.0, 0.5s cycle). Disabled under reduceMotion (show static).

Accessibility

  • .accessibilityLabel on each message: "Claude's message" / "Your message".
  • .accessibilityValue includes content (markdown stripped to plain text for VoiceOver).
  • Live region announce when new assistant message completes (message_stop).
  • Thinking block and tool cards — own accessibility; not announced separately here.

Acceptance Criteria

  • UserMessageView + AssistantMessageView implemented with public init.
  • Live caret shown during streaming, hidden when isStreaming = false.
  • Fade-in works on appear; respects reduceMotion (no slide-in under reduceMotion).
  • Multi-segment assistant message (text → tool_call → text → thinking) renders in correct order.
  • .textSelection(.enabled) allows copying either message content.
  • A11y: VoiceOver reads messages in reading order; only new message-complete announces (not per-token).
  • Snapshot tests: user message, short assistant, long assistant with mixed content, all in 4 appearances.
  • No hardcoded values — DS tokens only.

Relationships

Metadata

Metadata

Assignees

No one assigned

    Labels

    a11yAccessibility: VoiceOver, Dynamic Type, Reduce Motion, etc.complexity:MdialogueDialogue feature — structured chat UI for agent sessionsfrontendwave-3

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions