- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
- 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.
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).
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).
- 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)
- 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)
- 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