Profiles had only a coloured-initial disc. Now each person can set an
avatar — a real photo OR one of our own seed illustrations (so
pseudonymity stays the default; no photo required).
- ui/avatar.dart: the one-string value scheme carried in the kind:0
'picture' — a 'data:' photo thumbnail, a 'tane:seed:<glyph>' token, or
empty. The Nostr ProfileTransport already published 'picture'.
- Photos ride inline as a tiny thumbnail (reuses offerThumbnailDataUri,
24 KB cap) — no media server, like offer photos.
- avatar_edit.dart: pick/take a photo, choose a seed illustration, or
remove; ProfileStore stores it; ProfileScreen shows a big editable
avatar and publishes it.
- PeerAvatar renders photo / illustration / initial fallback.
- ProfileCache gains picture/setPicture; the inbox caches peers' avatars
alongside their names.
- i18n avatar block (en/es/pt/ast). Tests: value scheme + PeerAvatar
render modes, store + cache round-trips.
Follow-up: render peers' avatars in chat/market/your-people (thread the
cached picture into PeerAvatar at each call site).
- Anchor the message list to the bottom (`reverse: true`) so new messages
stay in view instead of landing below the fold.
- Move chat history from the OS keystore (O(n²) JSON blob, silent 200-msg
cap) to a separate encrypted Drift/SQLCipher DB (`ChatDatabase`):
indexed append, uncapped history, dedup as a unique-key invariant. It's
an ephemeral per-device cache, isolated from the inventory schema, its
migrations, and its sync. No data migration (pre-release).
- Add day separators (Today/Yesterday/locale date) and a per-bubble time,
all via ICU (12/24h per locale; Localizations locale maps Asturian →
Spanish for intl date symbols).
- Add peer avatars (deterministic colour from the pubkey + name initial),
surface send failures that were previously silent, and make bubble text
selectable (addresses, links).
- New i18n keys in en/es/pt/ast; tests for grouping, formatting, avatars,
scroll anchoring, storage and send errors.
Docs: docs/design/chat-storage.md + open-decisions.md.