From 4351428c664a2fa28e2524e885983a3374fca257 Mon Sep 17 00:00:00 2001 From: Bruno Deanoz Date: Mon, 22 Jun 2026 18:33:44 +0200 Subject: [PATCH] docs: comprehensive CLAUDE.md update for 2026-06-22 session 8 commits documented: biometric bypass fix, security settings (devices + password change + biometric toggle), app icon redesign, unified top bar, snappy sidebar (optimistic insert + instant title + smart merge), KaizenIcons system (7th design pillar), multi-select file picker. Updated shipped features table, bugs fixed section, NOT done checklist, dependencies. --- CLAUDE.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 53dcfd8..05e1672 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -228,7 +228,7 @@ All committed on `main`. Compile-verified on Mac (`./gradlew :app:compileDebugKo | `d468ec8` | **Theme switching wired** — all 7 files migrated from hardcoded Amber to `LocalKaizenAccent.current` | | `cad445f` | **i18n cleanup** — replaced all remaining hardcoded German strings with `stringResource()` | | `5c54b9b` | **ToolEventsBlock rewrite** — state machine matching web frontend (thinking/analyzing/start/end/error) | -| `da01b57` | **Attachment picker menu** — three floating GlassSurface pills (camera/gallery/file) with colored round icons, slide animation, dismiss-on-tap-outside. Positioned bottom-left above the + button. Camera uses `TakePicturePreview`, gallery filters `image/*`, file allows `*/*` | +| `da01b57` | **Attachment picker menu** — three floating GlassSurface pills (camera/gallery/file) with colored round icons, slide animation, dismiss-on-tap-outside. Positioned bottom-left above the + button. Camera uses `TakePicturePreview`, gallery uses `PickMultipleVisualMedia(ImageAndVideo)`, file uses `OpenMultipleDocuments` — gallery + files support multi-select | | `f3d8154` | **Sidebar redesign** — GlassSurface opacity 0.92/0.88, flat conversation rows, 3-dot context menu (rename/delete/pin/lock via `PATCH`/`DELETE /api/v1/conversations`), smaller avatar (30dp), logout integrated into user card | | `f3d8154` | **Markdown typography overhaul** — line-height 23→26sp (×1.625), paragraph spacing 6→12dp, heading margins 18dp/8dp, list spacing doubled. Matches Gemini readability | | `532cb8c` | **Reasoning + sources persistence** — saved to server (`SaveMessage.reasoning/sources`), loaded back (`StoredMessage`), cached in Room (`MessageEntity` + `SearchSource` TypeConverter). Reasoning rendered above content, sources below | @@ -244,6 +244,13 @@ All committed on `main`. Compile-verified on Mac (`./gradlew :app:compileDebugKo | `2d57aff` | **Input field redesign** — pill shape, higher opacity (0.85/0.90), Mic/Send toggle (mic when empty, send when text), bottom-aligned buttons | | `e9e2a66` | **Stronger haptics** — send CLICK 1.0, thinkingStart double-tap, responseStart crescendo, responseEnd firm settle | | `e9e2a66` | **Live language switching** — `LocaleManager` (API 33+) or `recreate()` fallback, immediate effect | +| `15f5781` | **Biometric bypass fix** — closed 3 auth bypass paths in `openConversation()` + sidebar `ToggleLock`. Unified `requestBiometricOrPassword()` gate, `PasswordUnlockDialog` fallback, server `POST /api/v1/auth/unlock` now verifies password (Argon2id) when provided | +| `ba7ebaf` | **Security settings** — biometric toggle (persisted in SecureStore), signed-in devices list with remote revoke (`GET/DELETE /api/v1/auth/tokens`), password change form (`POST /api/v1/auth/change-password`) | +| `e62c525` | **App icon redesign** — 10-layer glass-lens orb (soft halo, 6-stop amber body, cool glass overlay, bounce light, volume shadow, caustic structure, elliptical specular + micro pinpoint glint, gradient rims). Improved backgrounds (light/dark) + monochrome | +| `1929073` | **Unified top bar** — merged hamburger circle + ModelPill into single GlassSurface pill (menu icon → dot divider → model name 13sp Medium, 180dp max ellipsis). No chevron | +| `1362dae` | **Snappy sidebar** — optimistic insert (new chat appears instantly with first-line preview), `generateTitle` returns title directly + Room `updateTitle`, `replaceAll` uses upsert+deleteExcept (no empty-list flash) | +| `68721d8` | **KaizenIcons system** — centralized icon registry (`ui/icon/KaizenIcons.kt`), Lucide 1.5px stroke as base (same as web), custom override layer for brand icons. All 10 source files migrated, zero Material Icon imports remaining | +| `ad11c0e` | **Multi-select file picker** — gallery uses `PickMultipleVisualMedia(ImageAndVideo)`, file picker uses `OpenMultipleDocuments`. Multiple items selectable at once | **Earlier UI/feel work (Phase 0 prototype → feel-first):** - Liquid glass styling, floating panels, glassmorphic sidebar @@ -274,6 +281,14 @@ All committed on `main`. Compile-verified on Mac (`./gradlew :app:compileDebugKo - **Token counter always 0** — only updated during streaming, not for loaded chats. Fix: sum `inputTokens + outputTokens` from server messages. - **Model pill wrong** — showed global default instead of per-chat model. Fix: `chatModel` state from last assistant message's `model` field. +### Bugs fixed (2026-06-22 — security + UI session) + +- **Locked chats bypassed biometric auth** — three paths in `openConversation()` called `unlockAndOpen()` without authentication when biometrics unavailable. Sidebar `ToggleLock` sent `PATCH { locked: false }` without auth. Fix: unified `requestBiometricOrPassword()` gates all unlock paths; `PasswordUnlockDialog` fallback; server verifies password via Argon2id. +- **New chats not appearing in sidebar** — conversation only showed after stream + save + title + server refresh (seconds of delay). Fix: optimistic Room insert immediately after `createConversation()` with first-line preview as title. +- **Title generation not working immediately** — `generateTitle` was fire-and-forget, `refreshConversations()` raced with server-side generation. Fix: `generateTitle` now returns the title from the response; `updateTitle()` writes directly to Room. +- **Sidebar flicker on refresh** — `replaceAll()` did `DELETE ALL` + `INSERT`, causing Room Flow to emit empty list briefly. Fix: `upsert` + `deleteExcept` (no flash). +- **Gallery/file picker single-select only** — `GetContent()` allowed only one item. Fix: gallery → `PickMultipleVisualMedia(ImageAndVideo)`, file → `OpenMultipleDocuments(["*/*"])`. + ## What's NOT done yet (app, priority order) ### 1. Remaining visual/UX gaps (daily-drive blockers) @@ -296,12 +311,15 @@ These are what separate "prototype" from "daily-driver": - [ ] **Auto-logout on 401** — if the token is rejected, route to login screen instead of showing error banner - [ ] **Server version check** — call `/api/v1/meta` after login to detect server skew and warn cleanly -- [x] **Biometric lock** (`BiometricPrompt` + KeyStore/CryptoObject) — implemented for conversation unlock; `setUserAuthenticationRequired(true)` + `setInvalidatedByBiometricEnrollment(true)` ensures hardware-enforced biometric, not bypassable even on rooted devices +- [x] **Biometric lock** (`BiometricPrompt` + KeyStore/CryptoObject) — hardware-enforced, password fallback dialog when unavailable, biometric toggle in Settings, all unlock paths gated +- [x] **Security settings** — biometric toggle, signed-in devices with remote revoke, password change +- [x] **Password change** — `POST /api/v1/auth/change-password` (Argon2id, rate-limited) +- [x] **Device management** — `GET/DELETE /api/v1/auth/tokens` (list + revoke, can't revoke self) - [ ] **`FLAG_SECURE`** on sensitive screens — see `LATER.md` §1B ### 4. Features for parity with the web -- [x] **Attachments** — image/file upload, attachment preview in messages, display in loaded conversations, camera capture, attachment picker menu (camera/gallery/file) +- [x] **Attachments** — image/file upload (multi-select for gallery + files), attachment preview in messages, display in loaded conversations, camera capture, attachment picker menu (camera/gallery/file) - [x] **Reasoning/thinking display** — `ReasoningBlock` (collapsible), parses `\x01` sentinel - [x] **Web search sources** — `SourcesBlock` (collapsible, clickable links via ACTION_VIEW), parses `\x02`/`\x04` sentinels. `webSearch: true` in ChatRequest triggers agentive search - [x] **Tool call display** — `ToolEventsBlock`, parses `\x03` sentinel, status icons per step @@ -338,6 +356,7 @@ okhttp = "4.12.0" kotlinxSerialization = "1.7.3" securityCrypto = "1.1.0-alpha06" biometric = "1.4.0-alpha02" +composeIcons = "1.0.0" # com.composables:icons-lucide-android (Lucide for Compose) ``` ## Tests