Commit graph

77 commits

Author SHA1 Message Date
avi
25d53cb251 Detail: always fetch live summary for open file — voice tag was invisible for library-opened files (the report's own '## Summary' section defeated the fetch heuristic) 2026-09-16 17:12:10 -05:00
avi
3e1ec8eff8 Queue wait is visible: 'Queued… · Ns' + created_at on job payloads 2026-09-16 16:15:32 -05:00
avi
1dde1019e9 Detail: unmapable Summarize/transcript-save say why instead of silently doing nothing 2026-09-16 11:57:36 -05:00
avi
5ed4f31973 Always state the summary's voice: tag on the detail screen, Voice line in the saved report 2026-09-16 11:36:50 -05:00
avi
d66c27463c Settings: pick the summarizer (LAN H200 / laptop Ollama / auto); waiting label shows elapsed wait 2026-09-16 11:26:15 -05:00
avi
8e465bdb93 Detail: edit the transcript in place (user version wins over re-transcribe) 2026-09-16 11:26:15 -05:00
avi
ba54cb853b Adopt engine jobs at startup and on 'already running'; live work outranks a saved report 2026-09-15 19:45:02 -05:00
avi
cd1b08fda0 Summarize voices: exactly the four personas, drop custom-voice chip 2026-09-15 15:40:19 -05:00
avi
7d3e0e886d Detail: inline summarize progress + more voices + custom voice
- Progress bar + 'Summarizing… · <voice> (attempt n/3)' right under the
  Re-summarize button: the top spinner hides behind a long report, which
  made a running job look like nothing happened.
- Voices: + Enthusiastic, + Storyteller, + 'Other…' free-text (any
  phrase, 60 chars; dialog shows how it reads in the prompt).
- Verified end-to-end on BOTS: sarcastic summarize succeeded, summary
  v2 stored with tone=sarcastic ('…a heroic inability to monetize
  anything'). BUILD SUCCESSFUL + 39 tests.
2026-09-15 15:10:39 -05:00
avi
994dbc3690 Detail: personality picker beside Re-summarize
Chips: Neutral / Sarcastic / Funny / Dry wit. The chosen voice rides
the reprocess call and the button echoes it ('Re-summarize · Sarcastic');
an existing summary shows '· in a sarcastic voice' next to its heading.
compileKotlin + 39 unit tests green.
2026-09-15 14:24:36 -05:00
avi
0d8f3be614 Settings screen: make root column scrollable
Model cards + folder row overflowed in short windows (split-screen) with
no scroll path — the only screen where content was truly unreachable.
Library/Detail already scroll via LazyColumns; verified compileKotlin +
39 unit tests green.
2026-09-15 14:00:25 -05:00
avi
0b778e179d Desktop: drop per-row Delete; selection bar's Delete is the one path
Every row had a Delete button that duplicated the bulk bar (check a
box -> Delete N). Removed it plus its now-dead single-file confirm
dialog; deletion is checkbox + bar from now on. Detail screen keeps
its own delete.
2026-09-15 12:11:33 -05:00
avi
71b55715b4 Remove all Android/phone code: the app is standalone desktop
shared/ carried the vendored phone app's tree behind a 30-entry
exclude list. Deleted the 46 phone-only files (recording service,
widgets, Room DB, Nextcloud/local-only/folder-sync providers, TOFU
trust, Android settings stores, phone UI screens) and kept only the
seven the desktop compiles: CustomShonarProvider, ProviderTypes,
ShonarProvider, ShonarAuthStore, ShonarHandshake, ProviderRegistry,
AiContent. ProviderRegistry reduces to its id constant; handshake
drops the TOFU param; provider defaults self-identify as desktop.
Gradle exclude list gone — srcDir('../shared') is pure desktop now.
2026-09-15 11:42:20 -05:00
avi
751428cf3e Desktop: batch card shows overall % plus current file's stage and % 2026-09-15 10:53:58 -05:00
avi
36a1d2374b Desktop: queued files can be deleted/renamed; only running jobs lock
inFlight conflated queued with actually-running, so a file waiting in
the pump refused deletes ('is transcribing' — false) and hid its
Rename/Delete buttons during a batch. Track the truly-running files in
a dedicated set: the pump registers a file at poll-time atomically with
the queue pop, and deleteFile/renameFile either find it running (refuse)
or cancel its queue entry under the same lock (proceed). Batch progress
shrinks when a queued file is cancelled (batchRemove) so the bar still
ends; row status now distinguishes QUEUED from RUNNING on rescan.
2026-09-15 10:15:28 -05:00
avi
757595aaed Desktop: move folder path + Choose-folder into Settings
Library header keeps only wordmark/Settings plus the auto-transcribe
and Transcribe-all controls. Settings gains a 'Recordings folder'
section (full path + Change folder…) at the top; the empty library now
shows a card that jumps straight to Settings.
2026-09-15 10:15:28 -05:00
avi
957b4d7c77 Desktop: Transcribe-all button with batch progress bar
Toolbar gets 'Transcribe all (N)' when N files still lack a transcript
(includes previously-failed ones — asking again means retry). Queues
them through the existing pump; hidden while a batch runs or when the
library is fully transcribed.

While a batch runs, a card shows 'Transcribing batch: k of N — <current
file>' with a bar that blends the settled-file count with the current
file's own backend-reported fraction. Disconnected mid-batch clears the
bar instead of stranding it.

Pure logic in LibraryQueue (transcribeAllPlan, batchAdvance) + 3 new
tests; app suite 37/37. Verified button renders on live build 07:34.
2026-09-15 07:37:14 -05:00
avi
087e8386f5 Transcription: dwindle model choice to Whisper Base + Whisper Large v3
Registry now lists exactly two models instead of five:
- 'Whisper Base' — names the actual bundled model (was 'Base (default)').
- 'Whisper Large v3' — kept; honest copy on size/speed/RAM.
tiny/small/medium removed from the registry (validate_model_name now
rejects them; existing rows keep their saved model — all current data
is 'base', still valid).
Detail screen shows the model that WILL run by real display name
('Whisper Base (default)') instead of 'Use default (base)'; the picker
lists the two models with the default marked inline.
Backend 78 passed/1 skipped; app 34/34; engine hot-reloaded clean.
2026-09-15 07:24:57 -05:00
avi
5859a082d2 Desktop: make Ctrl+-/= zoom actually work
Two compounding bugs:
1. Nodes skipped in a plain recomposition kept their old captured
   density, so changing the zoom state left the tree unscaled — wrap the
   CompositionLocalProvider in key(zoom) to force full-tree recompose.
2. Compose focus routing missed the shortcut depending on which child
   held focus — add an AWT KeyboardFocusManager dispatcher below Compose
   (also accepts Ctrl+Up/Down, numpad +/-, Ctrl+0 reset).

User-confirmed zooming works on the live app.
2026-09-15 06:52:30 -05:00
avi
b45e9ff216 Desktop: replace tiny Swing folder chooser with in-app themed picker
JFileChooser rendered unusably small on this HiDPI/Wayland compositor and
had no resize grip. Replace it with a Compose dialog matching the app's
navy+teal skin: breadcrumb row (Home + path steps), scrollable list of
subfolders as cards (dot-dirs hidden), '..' row, and a confirm button
naming the selected folder. Scales with the app's Ctrl+-/= zoom since it
lives inside the Compose tree. Pure nav logic in FolderPicker with 3 new
tests (34 total green).
2026-09-15 06:41:05 -05:00
avi
3e0e3b50cb Desktop: adopt the Android app's companion design (navy+teal sonar skin)
Theme.kt now copies the Android ui/theme dark scheme verbatim: deep navy
#0B1220 + navy surfaces + teal ping #4FD1C5 — replaces warm-graphite gold.
Headers carry the identity: SonarLogo (same geometry as the Android
launcher's ic_launcher_foreground: center dot, teal upper-right arcs,
teal-dark lower-left) + S.H.O.N.A.R. wordmark + 'Self-hosted notes and
audio recording' tagline. Action buttons use the phone's icon vocabulary
(Filled PlayArrow/Edit/Settings/Stop/Delete). Row 'Open' button removed —
clicking the card already opens the recording.
Verified on-screen: navy+teal render, wordmark + tagline, icons, Open gone.
Ctrl+=/- zoom was already ported (4944fcd) and confirmed present.
2026-09-15 06:26:16 -05:00
avi
0a713879b9 Desktop: progress-while-open, search-within-recording, click-hit-to-playback
- openDetail attaches to a pump-owned file with no mapping yet: mirrors the
  pump's live stage/progress until it leaves in-flight, then picks up the
  mapping/report — opening a mid-transcribe file now shows movement instead
  of an idle Transcribe button.
- Detail screen gets a Play/Stop row and a 'Search this recording' box.
  Searchable lines come from live segments or by parsing the [HH:MM:SS]
  prefixes out of the saved report (round-trip tested); each hit card shows
  its timestamp and plays the audio from that moment on click.
- Playback via mpv (external process; Java can't decode m4a) — exact flag
  set verified on this box against a real recording. Stop on leave-screen,
  on file delete, and at app shutdown; natural end clears the playing
  state. 31/31 tests green.
2026-09-15 05:45:30 -05:00
avi
fb1d0e4880 Desktop: multi-select library rows for bulk delete
Checkbox on every row; selecting any reveals a toolbar (N selected,
Select all/Clear all, Clear, red Delete N). One confirmation dialog names
every file (first 8 + count) before anything goes. deleteFiles() re-checks
in-flight state at confirm time and skips anything the pump grabbed since
selection (named in the notice) instead of yanking a live transcribe.
Selection keys on absolute paths and prunes vanished files on rescan so
the count never lies. Selection logic pure + unit-tested (28/28).
2026-09-15 05:37:56 -05:00
avi
a5aedb2b0a Desktop: delete a recording from the app (library row + detail, with confirm)
Library rows and the detail header get a red Delete action that removes the
audio, its .transcript.md report, and its .shonar.json mapping, then purges
the engine's copy in the background (DELETE ?purge=true). Refuses while a
file is queued/transcribing so the pump can't resurrect it mid-flight; a
failed background purge surfaces as an inline warning instead of blocking
the local delete (stale mappings that 404 are silently fine). Confirmation
dialog on both entry points states exactly what goes. deleteSet() is pure
and unit-tested (25/25 green).
2026-09-15 05:14:20 -05:00
avi
c25d3ab150 Bundle the whisper base model + one-shot standalone provisioning
- models/hf-cache/ vendors Systran/faster-whisper-base (142MB, gitignored);
  engine spawn pins HF_HUB_CACHE there, so transcription works with a cold
  ~/.cache and fully offline
- verified: HOME=/tmp/fakehome HF_HUB_OFFLINE=1 model loads and transcribes
  via the bundled cache alone
- setup-standalone.sh: idempotent fresh-clone provisioning (local venv,
  model copy-or-download, engine boot smoke on :8123) — runs green
- backend/README rewritten for the desktop bundled-lite reality (was
  pointing at a nonexistent root README/docs and a Postgres quickstart)

Kotlin suite still green (gradlew test, 0 failures)
2026-09-14 21:05:41 -05:00
avi
adc40201a1 Engine venv is now local to this repo (uv venv + editable install of this backend)
- backend/.venv was a symlink into ~/Projects/Shonar; replaced with a real
  local venv built fully offline from the uv cache (deps pinned to the
  versions the app was tested with: fastapi, uvicorn, sqlalchemy,
  aiosqlite, alembic, faster-whisper 1.2.1, dev extras)
- editable install resolves 'shonar' to THIS backend/ (verified via
  shonar.__file__); PYTHONPATH pin kept as a belt
- verified standalone: with ~/Projects/Shonar renamed away, engine boots,
  self-migrates (alembic head m8models000001), /healthz + /readyz ok, and
  full register -> upload -> finalize -> list -> audio-download roundtrip
  passes (sha256 match)
2026-09-14 20:46:50 -05:00
avi
76c867fca4 Standalone Shonar Desktop: vendor portable sources + local engine; decouple from ~/Projects/Shonar
- shared/ = portable Android-origin sources vendored from deferred/desktop-server
  (app/build.gradle.kts srcDir repointed; PlaybackController.kt excluded as Android-only)
- backend/ = bundled-lite engine (SQLite + inline queue); .venv symlinked from the
  old checkout, PYTHONPATH pins THIS backend's code over any editable install
- repoRoot() resolves this project dir (env SHONAR_REPO still wins); desktop-dev.sh
  watches shared/ + backend/
- Verified: :app:compileKotlin + :app:test green (23 tests); engine boots on :8010,
  self-migrates, /healthz ok
2026-09-14 17:14:54 -05:00