Commit graph

183 commits

Author SHA1 Message Date
Bruno Deanoz
b2df7e9330 feat: modern message action icons — larger, distinctive
Icons swapped to more distinctive Lucide variants:
- Copy → ClipboardCopy (clipboard with arrow, instantly recognizable)
- Edit → SquarePen (filled square with pen, modern)
- Regenerate → RotateCcw (single-arrow rotate, cleaner than RefreshCw)
- Delete/Volume stay (already good)

Size: 36dp touch target, 20dp icons (was 32/18). Tint alpha 0.50→0.55
for better visibility. Added ClipboardCopy, SquarePen, RotateCcw, PenLine
to KaizenIcons registry.
2026-06-23 01:33:59 +02:00
Bruno Deanoz
8f72afd714 feat: edit and resend user messages
Pencil icon on user messages (next to copy, before delete). Tap removes
the edited message and all messages after it from the UI + server, then
puts the text back into the input field for editing and resending.

Only shown on user messages with text content (not voice-only), and only
when not currently streaming.
2026-06-23 01:30:52 +02:00
Bruno Deanoz
44ff82a66d fix: voice messages appear in wrong order after reopening chat
Voice auto-send was saving messages to the server but not caching them
in Room with correct sortOrder. When navigating away and back, the app
loaded from Room cache (empty for voice messages) then from server,
where the tree-based ordering could differ from insertion order.

Now caches both user and assistant messages in Room with proper sortOrder
after the server save succeeds, matching the normal send() flow.
2026-06-23 01:28:53 +02:00
Bruno Deanoz
1899f2cff1 fix: voice messages show as compact pill instead of file attachment chip
Audio-only user messages (no text, only audio attachments) now render as
a clean "🎤 Sprachnachricht" pill instead of the ugly filename chip
(voice_1782170416740.m4a). Detects via attachments.all { kind == "audio" }
&& content.isBlank(). Regular file attachments are unaffected.
2026-06-23 01:24:56 +02:00
Bruno Deanoz
fb6bc981d1 feat: web search provider picker for Vertex models (Google vs Enterprise)
When search mode is active and a vertex: model is selected, a second pill
appears next to "Suche" with a dropdown to choose between Google Search
(default) and Enterprise Web Search grounding. The selection is sent as
webSearchProvider in the chat request body. Matches the web frontend's
OptionPill for webSearchProvider.

Only visible for Vertex models — OpenRouter uses agentive web_search
server-tool which has no provider toggle.
2026-06-23 01:23:03 +02:00
Bruno Deanoz
4150936b18 fix: sampling panel too large + pills barely visible
Sampling panel: width 260→220dp, padding reduced, spacing 14→6dp between
parameters, checkbox 20→16dp, slider height capped at 28dp, removed extra
spacer between label row and slider. Panel is now ~40% shorter.

Pills visibility: inactive background alpha raised (dark 0.06→0.10, light
0.04→0.07), border alpha doubled (dark 0.08→0.14, light was white-on-light
invisible, now uses black 0.10), icon tint 0.60→0.75, text 0.70→0.80.
Active state also strengthened. Pills are now clearly visible in both modes.
2026-06-23 01:17:56 +02:00
Bruno Deanoz
d289466276 feat: live voice waveform replaces static recording indicator
32-bar waveform visualization that reacts to microphone amplitude in
real-time. MediaRecorder.getMaxAmplitude() polled every 60ms, normalized
to 0-1 range, fed into a scrolling bar chart (new bars enter right, old
shift left). Bars use accent color — louder = taller + more opaque,
quiet = short + muted. Replaces the static "Aufnahme läuft…" text.
2026-06-23 01:15:51 +02:00
Bruno Deanoz
20a71adb99 feat: voice recording auto-send + recording indicator UI
Voice messages now upload and send automatically when recording stops —
no manual send button needed. The input area transforms during recording:
text field replaced by a centered pulsing red dot + "Aufnahme läuft…" label,
with a single centered red stop button below.

The old flow (record → attachment chip → manual send) treated voice like
a file attachment. New flow: tap mic → record → tap stop → auto-upload →
auto-send → stream response. Matches the expected voice message UX.
2026-06-23 01:12:48 +02:00
Bruno Deanoz
497f0a82f7 feat: redesign mode pills and dropdowns
Pills: extracted shared ModePill composable — thinner border (0.8dp gradient
instead of 1.2dp solid), subtler inactive background, smaller text (12.5sp),
compact padding (12x7 instead of 13x8). Active state uses accent gradient
border instead of solid color.

Reasoning dropdown: styled container (dark glass bg, 16dp shadow, squircle
shape), check icon on selected preset, cleaner row layout.

Sampling popover: same styled container, custom checkbox (squircle with accent
tint instead of Material Checkbox), disabled state dims labels and track,
wider panel (260dp), more spacing between parameters.
2026-06-23 01:07:57 +02:00
Bruno Deanoz
fcac983201 fix: flat message action icons — remove background box
Clean flat icons without bordered container. 32dp touch target (circle clip
for ripple), 18dp icon size, no background fill. Modern minimal look.
2026-06-23 01:04:30 +02:00
Bruno Deanoz
dea379e237 fix: increase message spacing from 16dp to 24dp 2026-06-23 01:02:47 +02:00
Bruno Deanoz
0ade063b34 feat: improve message action buttons — larger, visible, subtle background
Buttons: 30dp→34dp with squircle shape (KaizenShapes.sm), subtle tinted
background (dark: white 4%, light: black 2.5%), icons 16→17dp. Tint alpha
raised from 0.35→0.50 (copy/regenerate/volume) and 0.6→0.70 (delete) so
buttons are actually visible in both light and dark mode. Spacing 2→4dp
between buttons, top padding 4→6dp.
2026-06-23 01:02:07 +02:00
Bruno Deanoz
10ece6d72f feat: rounder chat items with card-style borders
Items use KaizenShapes.md (16dp radius) instead of sm (12dp), more spacing
(4dp gap), horizontal padding, and subtle glass borders on inactive items.
Active items get accent gradient border + shadow. Matches web's rounded-xl
card treatment.
2026-06-23 00:59:58 +02:00
Bruno Deanoz
558b1be675 feat: sidebar redesign — glasier look, functional search, generate title, duplicate
Design: lower tintAlpha (0.80/0.86) for more blob bleed-through, accent-tinted
gradient borders + level1 shadow on active items, specular borders on search bar
and user card, accent gradient avatar, separator line in context menu.

Features: live search filter, "Titel generieren" (Sparkles, manual POST with
{ manual: true }), "Duplizieren" (copies all messages with new IDs + parentId
chain), titleGenerated field on ConversationSummary/Entity (Room v4).
2026-06-23 00:56:58 +02:00
Bruno Deanoz
2f9ada646c docs: comprehensive CLAUDE.md update for 2026-06-23 session
- Document voice recording, TTS read-aloud, TTS cache, server-synced voice
- Add /api/v1/audio-gen and PATCH /api/v1/me to API contract
- Mark 5 Voice & Call Mode items as done (recording, TTS, cache, sync, backend)
- Add bugs-fixed section for 06-23 (padding, locale, shadow, migration)
- Update backend section with audio-gen, tts_voice, me PATCH
2026-06-23 00:47:01 +02:00
Bruno Deanoz
1645be8a97 feat: sync TTS voice from server — app reads ttsVoice from /api/v1/me
Voice preference set in web settings now automatically applies in the app.
Falls back to "Kore" if no server preference set.
2026-06-23 00:39:34 +02:00
Bruno Deanoz
c4884a3748 feat: TTS audio cache in Room — skip regeneration on replay
- MessageEntity gets ttsUrl + ttsVoice fields (Room version 2→3)
- MessageDao.getTtsUrl() checks cache before API call
- MessageDao.updateTts() saves URL + voice after generation
- readAloud() checks Room first, only calls /api/v1/audio-gen on miss
- Voice change invalidates cache automatically (query includes voice)
- Default voice "Kore" (Gemini-TTS)
2026-06-23 00:33:09 +02:00
Bruno Deanoz
9e1f30de1f feat: voice recording + TTS read-aloud for assistant messages
Audio recording:
- Mic button wired to MediaRecorder (AAC/M4A, 44.1kHz/128kbps)
- Tap to start, tap again to stop — auto-uploads as audio attachment
- RECORD_AUDIO permission with runtime request
- Recording state: red stop-square on mic button

TTS read-aloud:
- Volume button on assistant messages → POST /api/v1/audio-gen
- Gemini-TTS generates speech server-side, MediaPlayer streams it
- Tap again to stop playback, accent-colored icon when playing
- Graceful error handling (Toast on failure)

Supporting changes:
- KaizenApi.generateSpeech() for /api/v1/audio-gen endpoint
- KaizenIcons.Volume2 added (Lucide)
- DE/EN strings for recording, permission, TTS states
2026-06-23 00:27:02 +02:00
Bruno Deanoz
edd8ccaa05 fix messages overlapping mode pills — increase bottom padding to 220dp
The two-row ChatInput redesign made the bottom dock ~210dp tall but
contentPadding was still 152dp, causing the last message to scroll
behind the mode pills.
2026-06-23 00:13:21 +02:00
Bruno Deanoz
3c6dab33a0 docs: add Call Mode roadmap (Weg A pipeline + Weg B Live API), image/media gen section
- 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
2026-06-23 00:10:15 +02:00
Bruno Deanoz
141d4de227 fix locale, add stop button, auto-logout on 401, migrate login shadows
- EmptyHero date respects system locale instead of hardcoded German
- Stop button (red square) replaces send/call during streaming, cancels stream cleanly
- Auto-logout on HTTP 401 (expired token) routes to login instead of error banner
- LoginScreen migrated from Modifier.shadow to kaizenShadow (design system pillar 4)
2026-06-23 00:02:30 +02:00
Bruno Deanoz
3032dc869f docs: update CLAUDE.md for 2026-06-22 session — input redesign, perf, markdown
Mark completed: table rendering, clickable links, strikethrough, task
lists, streaming performance (pre-compiled regex). Document: ChatInput
two-row layout, soft dock scrim, sensor/blob perf optimizations,
CallButton with AudioLines, expanded code highlighting languages.
2026-06-22 23:12:42 +02:00
Bruno Deanoz
bf9441422b style CallButton to match SendButton — accent gradient, press animation
Same 38dp circle as SendButton. Subtle accent gradient fill (16-28%
alpha), AudioLines icon in accent.primary color, press-scale animation.
Visually pairs with the send button as its idle counterpart.
2026-06-22 19:21:16 +02:00
Bruno Deanoz
fd0de5ccef upgrade Markdown renderer — tables, strikethrough, clickable links, task lists
New block types: Table (header + separator + body rows, horizontal scroll,
styled borders), task lists (- [ ] / - [x] with check icons).
New inline: ~~strikethrough~~, clickable links (tap opens ACTION_VIEW).
Code highlighting: block comments (/* */), HTML comments (<!-- -->),
template strings, SQL case-insensitive keywords, added C/C++/Ruby/PHP/
Dockerfile/TOML language support. All regex patterns pre-compiled as
top-level constants for streaming performance.
2026-06-22 19:20:17 +02:00
Bruno Deanoz
70aa5c56da fix ChatInput — restore mic/call toggle, softer send button
Mic (voice recording) stays left next to +. Right side toggles
between AudioLines icon (call, when empty) and send button
(when content). Send button: removed border, translucent gradient,
38dp instead of 44dp.
2026-06-22 19:16:50 +02:00
Bruno Deanoz
313c21d47d perf: reduce sensor rate and pause blob animation during chat
Sensor: SENSOR_DELAY_GAME → SENSOR_DELAY_UI (~15Hz instead of ~60Hz),
parallax effect unchanged. Blob drift animation only runs on empty
state, frozen at midpoint during chat (blobs still visible, no GPU
animation cost).
2026-06-22 19:01:25 +02:00
Bruno Deanoz
f703196087 soften bottom dock scrim — transparent gradient instead of opaque block
Keyboard closed: 0% → 25% → 55% alpha gradient.
Keyboard open: 50% → 85% instead of solid fill.
Pills and input now float over the mesh background.
2026-06-22 18:58:04 +02:00
Bruno Deanoz
34a527cdb6 redesign ChatInput — two-row layout, rounder, elevated
Text field on top with full width, action icons below.
More padding (16dp margins, 14dp inner), pill shape with
32dp corners, level3 shadow for float effect. Plus and Mic
buttons get subtle circular backgrounds.
2026-06-22 18:54:32 +02:00
Bruno Deanoz
4351428c66 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.
2026-06-22 18:33:44 +02:00
Bruno Deanoz
ad11c0ee46 fix: multi-select for gallery and file picker
Gallery uses PickMultipleVisualMedia (image + video), file picker uses
OpenMultipleDocuments — both now allow selecting multiple items at once.
Camera stays single-shot (one photo per shutter, standard behavior).
Each selected item uploads in parallel as before.
2026-06-22 16:29:35 +02:00
Bruno Deanoz
875a319ddd docs: document KaizenIcons as 7th design system pillar
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).
2026-06-22 14:13:10 +02:00
Bruno Deanoz
68721d8ddf feat: KaizenIcons system — Lucide base, full Material Icons migration
Introduces a centralized icon registry (ui/icon/KaizenIcons.kt) that
decouples every UI component from the underlying icon library. All 10
source files migrated from Material Icons Rounded to KaizenIcons backed
by Lucide (1.5px stroke, same library as the web frontend).

Architecture:
- KaizenIcons object with semantic getters (Send, Lock, Brain, etc.)
- Grouped by function (Navigation, Chat, Modes, Files, Settings, etc.)
- Custom brand section ready for hand-drawn SVG overrides
- One-line swap: change the getter, every usage updates
- Zero Material Icon imports remaining in app source

Dependency: com.composables:icons-lucide-android:1.0.0
2026-06-22 14:11:34 +02:00
Bruno Deanoz
1362daee53 fix: snappy sidebar — optimistic insert, instant title, smart merge
Three fixes for sluggish conversation list:

1. Optimistic sidebar insert: new conversation appears in the sidebar
   immediately after creation (with first-line preview as title),
   before the stream finishes.

2. generateTitle now returns the title from the server response and
   updates Room directly via updateTitle() — no blind refresh that
   races with server-side generation.

3. replaceAll uses upsert + deleteExcept instead of deleteAll + insert,
   eliminating the empty-list flash that caused sidebar flicker.
2026-06-22 13:57:52 +02:00
Bruno Deanoz
1929073ad5 redesign top bar — unified glass pill for sidebar + model
Merge the separate hamburger circle and ModelPill into one cohesive
GlassSurface pill. Menu icon (17dp, muted) sits left, subtle 3dp dot
divider, then model name (13sp Medium, 180dp max with ellipsis).
No more chunky ExpandMore chevron. Matches the TokenBadge's glass
language on the right.
2026-06-22 13:54:08 +02:00
Bruno Deanoz
e62c525386 redesign app icon — glass-lens orb with depth
10-layer foreground: soft halo, warm amber body with 6-stop gradient,
cool glass overlay, bounce light, volume shadow, internal caustic
structure, elliptical specular + micro pinpoint glint, gradient rims.
Replaces the flat 5-layer amber ball that looked like an emoji.

Background: warmer cream (light) with vignette, deeper obsidian (dark)
with warm center glow. Monochrome: stronger contrast + rim for themed
icon legibility.
2026-06-22 13:49:43 +02:00
Bruno Deanoz
ba7ebafe08 feat: security settings — devices, password change, biometric toggle
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.
2026-06-22 13:44:01 +02:00
Bruno Deanoz
15f5781824 fix: close biometric bypass on locked conversations
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
2026-06-22 13:27:55 +02:00
Bruno Deanoz
f8e1747df4 docs: comprehensive CLAUDE.md update for 2026-06-21/22 session
Added 16 new feature commits to the shipped features table.
Updated TODO lists (conversation management, biometric unlock,
sources all marked done). Documented 10 bugs fixed. Added 3 new
constraints/gotchas (keyboard handling, Room schema, BiometricPrompt
context). Updated dependencies (biometric), test descriptions
(reasoning/sources round-trips, SearchSource converter).
2026-06-22 01:35:26 +02:00
Bruno Deanoz
e9e2a66b7d stronger haptics + live language switching
Haptics: all feedback stronger and more distinct —
- click (send): 1.0 intensity CLICK (was 0.85)
- thinkingStart: double-tap TICK 0.5→0.7 (was single 0.22)
- responseStart: three-step crescendo TICK→CLICK→CLICK (firmer)
- responseEnd: firm CLICK + settling TICK (was soft 0.3 TICK)

Language: selectLocale() now applies immediately —
- API 33+: LocaleManager.setApplicationLocales (no restart)
- API <33: updateConfiguration + activity.recreate()
No new dependencies needed.
2026-06-22 01:31:02 +02:00
Bruno Deanoz
163a2cee28 fix 3 bugs: pills with keyboard, token count, chat model
1. Mode pills stay visible when keyboard open (were hidden).
   Spacing reduced (6dp vs 24dp) to keep dock compact.

2. Token counter works for loaded chats — sum inputTokens +
   outputTokens from all StoredMessages when fetching from
   server. StoredMessage now parses model/inputTokens/outputTokens.

3. Model pill shows the chat's actual model (from last assistant
   message) instead of always the global default. chatModel state
   tracks per-conversation model, falls back to session default
   for new chats.
2026-06-22 01:27:29 +02:00
Bruno Deanoz
0db20bfda8 docs: document keyboard handling approach (offset, not imePadding) 2026-06-22 01:23:30 +02:00
Bruno Deanoz
c014d6d90c fix keyboard handling — offset dock instead of padding
adjustResize didn't work with enableEdgeToEdge (input hidden
behind keyboard). imePadding inflated the dock (input too high).

New approach: read WindowInsets.ime bottom value directly and
apply it as negative Y offset on the dock. The dock slides up
by exactly the keyboard height — no inflation, no layout changes.
windowSoftInputMode=adjustNothing (edge-to-edge default).

When keyboard closed: offset = nav bar height (gesture bar).
When keyboard open: offset = keyboard height (includes nav bar).
2026-06-22 01:21:32 +02:00
Bruno Deanoz
420bcd7cb6 fix input field way too high — use adjustResize instead of imePadding
imePadding() on Compose Box/Column with overlapping children
(BottomCenter-aligned dock over fillMaxSize LazyColumn) caused
the input to float to the top of the screen. The Compose inset
system doesn't work well with this overlay layout pattern.

Fix: remove all imePadding() usage, set windowSoftInputMode=
adjustResize in AndroidManifest. Android resizes the window
itself when the keyboard opens — the entire layout naturally
reflows, dock stays at the bottom, chat area shrinks. This is
the standard approach for chat apps with edge-to-edge.
2026-06-22 01:17:59 +02:00
Bruno Deanoz
e697dd7e76 fix error banner sticky, token badge always visible
- loadError auto-clears after 4s for unlock failures
- loadError reset on every conversation switch (was sticky)
- Token badge always shown (was hidden when usedTokens == 0,
  making it invisible for all loaded chats)

Note: locked chat "Authentifizierungsfehler" happens because
POST /api/v1/auth/unlock is not deployed yet on the server.
After git pull + docker build on prod, biometric unlock works.
2026-06-22 01:14:55 +02:00
Bruno Deanoz
77976ea974 move imePadding to outer Box — fix input floating into chat area
The root cause: imePadding on the bottom dock Column inflated it
by the full keyboard height, pushing it up into the chat content.

Fix: imePadding on the outer Box that contains everything. Now
the entire layout shrinks when keyboard opens — chat area gets
shorter, dock stays at the (new) bottom edge. No overlap.
2026-06-22 01:13:40 +02:00
Bruno Deanoz
27deaca2e4 fix locked chat not opening — context cast + biometric fallback
Two issues prevented locked chats from opening:

1. context as? FragmentActivity silently returned null on some
   devices where LocalContext.current is a ContextWrapper. Fix:
   walk the ContextWrapper chain to find the FragmentActivity.

2. No fallback when biometrics unavailable — the chat just stayed
   locked with no feedback. Fix: if biometrics not available (no
   fingerprint enrolled, no hardware), skip biometric prompt and
   unlock directly via server token. Biometric is still enforced
   when available.

Extracted unlockAndOpen() to avoid duplicating the server unlock
+ message loading logic across biometric/fallback paths.
2026-06-22 01:10:57 +02:00
Bruno Deanoz
ba01902b08 fix input field overlapping chat when keyboard open
Root cause: gradient scrim (transparent at top) let chat text
bleed through when imePadding pushed the dock up. Fix: use solid
background color when keyboard is visible, gradient only when
keyboard is closed. Pills and spacer also hidden during keyboard.

Input field: pill shape instead of xl (rounder), higher opacity
(0.85/0.90 instead of 0.75/0.74) for cleaner look — less muddy
glass, more solid surface. Bottom spacer reduced to 4dp when
keyboard open (was 16dp) to sit tight above the keyboard.
2026-06-22 01:09:00 +02:00
Bruno Deanoz
fb269731b7 fix scroll buttons + restyle sidebar context menu
Scroll buttons: only one shown at a time based on direction
(up when scrolled down, down when scrolled up), hidden during
streaming. Mutually exclusive instead of both visible.

Sidebar menu: replaced default Material3 DropdownMenu/MenuItem
with custom styled menu — dark surface color (1A2030 dark,
F8F9FB light), KaizenShapes.md rounded corners, 12dp shadow
elevation, custom MenuRow composable with proper spacing.
2026-06-22 01:05:35 +02:00
Bruno Deanoz
f62cc4b7ac fix token badge, hide pills on keyboard, reset tokens on nav
Three fixes:
1. Token badge only shown when usedTokens > 0 (not in old chats
   where we have no streaming data). Pushed to far right with
   Spacer weight. Reset to 0 on new chat and conversation switch.
2. Mode pills hidden when keyboard is open (WindowInsets.isImeVisible)
   — prevents the bottom dock from pushing too far up and overlapping
   chat content. Input field stays compact at the bottom.
3. ModelPill no longer constrained with weight(fill=false) which
   was causing layout issues.
2026-06-22 01:01:53 +02:00
Bruno Deanoz
e87220afb5 redesign SourcesBlock: collapsed by default, vertical cards, clickable
Sources are now collapsed by default with a "Quellen (N)" header
that expands/collapses on tap (matching web's SourcesBlock behavior).

Each source renders as a full-width vertical card (not a small
140dp chip) with title, URL in accent color, and optional snippet.
Tapping a source opens it in the browser via ACTION_VIEW intent.
2026-06-22 00:56:44 +02:00