feat(avatars): monogram avatars in roster, self card, and chat — W4 Phase 1

Foundation for user avatars (W4). Every participant now shows a circular
monogram avatar: their initial(s) on a colour deterministically derived from a
stable key (node id, or name where no id is available). This is also the
universal fallback for the later preset/upload phases.

- New pure `avatar` module: `initials`, `color_for_key` (FNV-1a → 12-colour
  palette), `use_dark_text_on` (contrast). +5 unit tests. Dependency-free.
- `avatar_badge` view helper: a radius-capped coloured container + centred
  initials (plain iced widgets, no canvas/image needed).
- Wired into the self card, each peer row (left of the name — pairs with the
  A10 fixed-width row), and each chat line.
- Threaded the chat sender's node id end to end (RoomEvent → UiEvent::ChatMessage
  gains `from`, ChatEntry gains `from`) so chat avatars are id-keyed and ready
  for the Phase 2/3 per-peer avatar lookup.

Build + clippy clean, 219 lib tests green. Visual confirm: join/create a room.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 16:28:05 -04:00
co-authored by Claude Opus 4.8
parent 2621576ed9
commit 19194f0cee
5 changed files with 157 additions and 7 deletions
+3 -2
View File
@@ -69,8 +69,9 @@ pub enum UiEvent {
/// Call recording stopped; carries the finished WAV path.
RecordingStopped { path: String },
/// A room text-chat message arrived from a peer (never our own — local
/// messages are echoed by the UI on send).
ChatMessage { name: String, text: String },
/// messages are echoed by the UI on send). `from` is the sender's node id
/// string, used to key their avatar (W4).
ChatMessage { from: String, name: String, text: String },
/// Our own screen share started; the UI flips the Share button to "Stop".
ScreenShareStarted,
/// Our own screen share stopped (or failed to start).