docs: update CLAUDE.md with all session changes
Added: gradient scrims, mode pills multi-select, EmptyHero centering, settings layout fix, theme switching wiring, i18n cleanup, ToolEventsBlock state machine, attachment picker menu, background prefetch, migration watermark fix documentation.
This commit is contained in:
parent
09489318bf
commit
2e323f3609
1 changed files with 35 additions and 8 deletions
43
CLAUDE.md
43
CLAUDE.md
|
|
@ -78,6 +78,10 @@ Full spec: `DESIGN.md`.
|
|||
|
||||
**Migration status:** All UI components migrated to the design system. Sidebar, ChatInput, ChatScreen, LoginScreen, ModelSheet, SettingsCard, SettingsScreen use `GlassSurface` + `KaizenShapes` + `kaizenShadow`. Only Markdown code-block shapes remain as `RoundedCornerShape` (intentionally rectangular for code).
|
||||
|
||||
**Theme switching fully wired:** All 7 files that previously imported hardcoded `Amber`/`AmberDeep`/`OnAmber`/`BlobX` now read from `LocalKaizenAccent.current`. Changing theme in Settings immediately updates: message bubbles, send button, orb, mesh background blobs, sidebar highlights, model sheet, stream blocks, login screen, settings toggles. No hardcoded amber imports remain outside `Color.kt` and theme definitions.
|
||||
|
||||
**Floating bar scrims:** Top bar (hamburger + model pill) and bottom dock (mode pills + input) have vertical gradient scrims (`MaterialTheme.colorScheme.background` → transparent) to prevent message content from bleeding through. Model pill opacity increased to 0.92.
|
||||
|
||||
**KaizenOrb** (`chat/Background.kt`) uses `rememberTiltState()` from `ui/sensor/` instead of inline sensor code. Breath animation uses `Durations.ORB_BREATH` / `ORB_BREATH_STREAMING` from `ui/motion/`.
|
||||
|
||||
**Typography** — Inter Variable (`res/font/inter_variable.ttf`, 859 KB, SIL OFL). Full type scale from DESIGN.md §8: `displayLarge` (32sp/W600) through `labelSmall` (11sp/W500). All 9 weight axes registered (W100–W900). Negative tracking at display sizes, positive at label sizes. `@OptIn(ExperimentalTextApi::class)` for `FontVariation.Settings`.
|
||||
|
|
@ -112,7 +116,7 @@ The `/api/v1/chat` stream embeds control sections via sentinel code points (from
|
|||
| `\x00` USAGE | trailing | Usage/cost JSON | Stripped (display TODO) |
|
||||
| `\x01` REASONING | toggle | Model's chain-of-thought | `ReasoningBlock` — collapsible "Gedankengang" |
|
||||
| `\x02` SOURCES | trailing | Web search sources JSON | `SourcesBlock` — compact cards with domain |
|
||||
| `\x03` TOOL | leading | Tool-call events (JSON+newline) | `ToolEventsBlock` — status icons per step |
|
||||
| `\x03` TOOL | leading | Tool-call events (JSON+newline) | `ToolEventsBlock` — state machine (thinking/analyzing/start/end/error) |
|
||||
| `\x04` QUERY | trailing | Search query string | Shown above sources |
|
||||
|
||||
**StreamConsumer.Incremental** (`net/StreamConsumer.kt`) returns `StreamState` (not `String`):
|
||||
|
|
@ -126,6 +130,14 @@ The `/api/v1/chat` stream embeds control sections via sentinel code points (from
|
|||
|
||||
**UI files:** `chat/StreamBlocks.kt` — `ReasoningBlock`, `ToolEventsBlock`, `SourcesBlock`, `SourceChip`.
|
||||
|
||||
**ToolEventsBlock state machine** (matches web frontend `LiveToolSteps`):
|
||||
- `type: "thinking"` → "✨ Verarbeitet …" (only shown when no other tool activity)
|
||||
- `type: "analyzing"` → "📄 Analysiert {name} …" (file processing)
|
||||
- `type: "start"` → wrench icon + tool name (in progress)
|
||||
- `type: "end"` → ✅ checkmark + tool name + elapsed ms (completed)
|
||||
- `type: "error"` → ❌ error icon + tool name (failed)
|
||||
- Empty/thinking-only events are hidden (no generic "Tool" fallback).
|
||||
|
||||
### App icon
|
||||
|
||||
Adaptive icon: amber orb with multi-layer radial gradients (body, depth shadow, specular highlight, warm bounce light, crisp rim). Light mode: warm off-white background. Dark mode: Obsidian gradient. Monochrome layer for Android 13+ themed icons. Splash screen uses orb foreground (API 31+). Vector-only — no raster WebPs.
|
||||
|
|
@ -146,19 +158,22 @@ Architecture decision document: `OFFLINE_CACHE_ADD.md`.
|
|||
```
|
||||
App start → Room (instant) → UI shows cached data
|
||||
→ Background: server fetch → Room update → Flow emits → UI updates
|
||||
→ Background: prefetch messages for all uncached conversations
|
||||
Chat open → Room (instant) → Background: server fetch → Room update
|
||||
Send message → in-memory during streaming → Room flush after stream ends
|
||||
```
|
||||
|
||||
**Background prefetch:** After conversation list sync, `MessageRepository.prefetchMissing()` iterates all unlocked conversations and fetches messages for any not yet cached. This ensures all chats are readable offline, not just previously opened ones. Already-cached conversations are skipped.
|
||||
|
||||
**Streaming hybrid model:** During active streaming, messages are mutated in-memory (60x/s). Room is NOT written during streaming — that would be absurd I/O. After stream completion, user + assistant messages are upserted into Room. This avoids SQLite lock contention while keeping the cache fresh.
|
||||
|
||||
**Files:**
|
||||
- `db/Entities.kt` — `ConversationEntity`, `MessageEntity`
|
||||
- `db/ConversationDao.kt` — `observeAll()` (Flow), `replaceAll()` (transaction: deleteAll + upsertAll, avoids SQLite 999-variable limit), `lastCachedAt()`
|
||||
- `db/MessageDao.kt` — `observeByConversation()`, `getByConversation()`, `upsertAll()`, `deleteByConversation()`
|
||||
- `db/ConversationDao.kt` — `observeAll()` (Flow), `replaceAll()` (transaction: deleteAll + upsertAll, avoids SQLite 999-variable limit), `lastCachedAt()`, `allUnlockedIds()`
|
||||
- `db/MessageDao.kt` — `observeByConversation()`, `getByConversation()`, `upsertAll()`, `deleteByConversation()`, `cachedConversationIds()`
|
||||
- `db/KaizenDatabase.kt` — singleton via `companion object` (double-checked locking), thread-safe
|
||||
- `db/ConversationRepository.kt` — `observeAll()` (Flow of ConversationSummary), `refresh()` (server → Room)
|
||||
- `db/MessageRepository.kt` — `observeMessages()`, `getCached()`, `fetchAndCache()`, `cacheMessages()`
|
||||
- `db/ConversationRepository.kt` — `observeAll()` (Flow of ConversationSummary), `refresh()` (server → Room), `allUnlockedIds()`
|
||||
- `db/MessageRepository.kt` — `observeMessages()`, `getCached()`, `fetchAndCache()`, `cacheMessages()`, `prefetchMissing()` (background prefetch for offline)
|
||||
- `db/Converters.kt` — `List<Attachment>` ↔ JSON string via kotlinx.serialization
|
||||
- `db/Mappers.kt` — `ConversationSummary` ↔ `ConversationEntity`, `StoredMessage` ↔ `MessageEntity`
|
||||
|
||||
|
|
@ -197,6 +212,16 @@ All committed on `main`. Compile-verified on Mac (`./gradlew :app:compileDebugKo
|
|||
| `4616ef2` | **Markdown rendering with syntax-highlighted code blocks** — native Compose renderer: headings, bold/italic, inline code, fenced code blocks with keyword highlighting (15+ languages), ordered/unordered lists, streaming cursor integration |
|
||||
| `d8dcb25` | **Attachment support — display, upload, and send files** — parse + render message attachments from loaded conversations (images inline via OkHttp + in-memory cache, other kinds as icon chips); + button wired to Android file picker with upload to `/api/v1/upload`, progress chips, send attachments with chat requests + persist in saved messages. Also: Markdown horizontal rules, blockquotes, links, code block copy button |
|
||||
| `9759d47` | **Offline-first cache layer (Room)** — sidebar + messages load instantly from SQLite cache, background refresh syncs with server, streaming hybrid model (in-memory during stream, Room flush after), ChatViewModel, repositories, mappers, TypeConverters, unit tests |
|
||||
| `5804b33` | **Gradient scrims on floating top/bottom bars** — content no longer bleeds through the model pill or mode pills |
|
||||
| `78f03ad` | **Mode pills cleanup** — removed muddy amber tint, solid surface fills, neutral borders |
|
||||
| `3560665` | **Mode pills multi-select** — modes are now independent toggles, not mutually exclusive |
|
||||
| `e4c6e7f` | **EmptyHero centered** — orb + greeting vertically centered between top/bottom bars |
|
||||
| `fe5e93c` | **Settings layout fix** — option rows stack title above controls instead of cramming side-by-side |
|
||||
| `9186594` | **Background prefetch** — all conversation messages prefetched for offline access on app start |
|
||||
| `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) |
|
||||
| `0948931` | **Attachment picker menu** — camera capture, gallery (image/*), and file picker (*/*) via dropdown |
|
||||
|
||||
**Earlier UI/feel work (Phase 0 prototype → feel-first):**
|
||||
- Liquid glass styling, floating panels, glassmorphic sidebar
|
||||
|
|
@ -207,9 +232,11 @@ All committed on `main`. Compile-verified on Mac (`./gradlew :app:compileDebugKo
|
|||
- Dynamic keyboard handling (suggestion pills hide, spacer shrinks)
|
||||
- Settings screen with SettingsViewModel
|
||||
|
||||
### Bug fixed this session (2026-06-19/20)
|
||||
### Bugs fixed (2026-06-19/20/21)
|
||||
|
||||
**"Verbindung zum Server fehlgeschlagen" on S25** — root cause was **server running an old build** without the v1/conversations route (404) and without Bearer auth on models (401). Fix was `git pull && docker compose -f docker-compose.prod.yml up -d --build` on the server. Additionally, the error diagnostics were improved (see `423abc0`) so future issues show the exact cause in the UI banner instead of a generic message.
|
||||
- **"Verbindung zum Server fehlgeschlagen" on S25** (06-19) — server running old build without v1 routes. Fix: `git pull && docker compose up -d --build`.
|
||||
- **Server 500 on conversation load** (06-21) — Drizzle migration 0025 (`model_id` column) skipped due to rogue watermark entry (`created_at = 1781600000000`) in `drizzle.__drizzle_migrations` table, caused by hand-typed fake timestamps. Fix: lowered rogue entry's timestamp, re-ran `node scripts/migrate.mjs`.
|
||||
- **Deploy gotcha (migrations):** fake timestamps in journal entries 0017–0025 (60-second increments) can cause watermark conflicts. Future migrations via `pnpm db:generate` use real timestamps and won't hit this.
|
||||
|
||||
## What's NOT done yet (app, priority order)
|
||||
|
||||
|
|
@ -238,7 +265,7 @@ These are what separate "prototype" from "daily-driver":
|
|||
|
||||
### 4. Features for parity with the web
|
||||
|
||||
- [x] **Attachments** — image/file upload, attachment preview in messages, display in loaded conversations (camera capture still TODO)
|
||||
- [x] **Attachments** — image/file upload, 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` + `SourceChip`, parses `\x02`/`\x04` sentinels
|
||||
- [x] **Tool call display** — `ToolEventsBlock`, parses `\x03` sentinel, status icons per step
|
||||
|
|
|
|||
Loading…
Reference in a new issue