Edit creates a sibling branch (same parentId) instead of deleting messages.
Regenerate creates a new assistant branch. BranchNavigator UI shows chevron
arrows with "1/3" counter on messages with siblings. Tree state (activeRootId,
activeChild) synced to server via PATCH. New ChatTree data structure mirrors
the web frontend's tree.ts. StoredMessage.parentId parsed from API response.
Replace deprecated ClickableText with LinkAnnotation.Url + Text (Compose
native URI handling). Add GET /api/v1/meta call after login and on each
app resume to detect API version skew — shows dismissible amber banner.
Keep [x] items as context for future sessions. Added new done items
(text selection, ChatInput capsule, visual refresh). Fixed section
numbering 1-8. Updated media cache to reflect custom disk cache.
- Mark auto-logout on 401 as done
- Remove WebSocket text-streaming TODO (not needed)
- Add Voice & Call Mode section with v1 (pipeline) and v2 (Gemini Live API via Vertex/GCP)
- Add Image & media generation section
- Document stop button, locale fix, login shadow migration in shipped features
Icon system is now the 7th pillar alongside P3 color, squircle shapes,
GlassSurface, multi-layer shadows, 4-theme system, and sensor motion.
Documents the Lucide base, custom override architecture, semantic
grouping, and how to add hand-drawn SVG overrides. Also documents the
unified top bar redesign (merged sidebar + model pill).
Security section in Settings with three cards:
- Biometric toggle (persisted in SecureStore, respected by unlock flow)
- Signed-in devices list with remote revoke (GET/DELETE /api/v1/auth/tokens)
- Password change form (POST /api/v1/auth/change-password)
requestBiometricOrPassword() now checks the biometric toggle — when
disabled, always falls back to password dialog.
Three paths in openConversation() called unlockAndOpen() without any
authentication when biometrics were unavailable. Sidebar ToggleLock
also sent PATCH { locked: false } without auth.
- Unified requestBiometricOrPassword() gates all unlock paths
- PasswordUnlockDialog as fallback when biometrics unavailable
- KaizenApi.requestUnlock() sends password to server for verification
- All unlock flows (open chat + sidebar toggle) now require auth
Settings now persist across app restarts via SecureStore:
- Theme (KaizenThemeId), appearance (Light/Dark/System), username, locale
- All loaded on SettingsViewModel init, saved on every change
Server sync: fetchMe() returns name + email from /api/v1/me,
populates SettingsViewModel on login and each foreground resume.
Language selector: System / Deutsch / English pill toggle in Settings.
Locale preference stored; runtime switching deferred to next iteration.
MeResponse expanded to parse name + email (was defaultModel only).
SettingsViewModel tests updated for store-less default state.
Inter Variable (v4.1, 859 KB): full type scale with 9 weight axes
(W100–W900), negative tracking at display sizes, positive at labels.
Theme picker: SettingsViewModel now uses KaizenThemeId from the design
system (AMBER/BLAU/MONO/AURORA). MainActivity wires selectedTheme +
appearanceMode to KaizenTheme composable — switching themes in Settings
instantly propagates to the entire app.
No server sync needed — web stores theme in localStorage, app stores
locally. Backend /api/v1/me has no uiTheme field in the DB.
Settings strings migrated to string resources (de + en).
SettingsViewModelTest updated for new enum values + System default.