From 7b3436837ae053b9975913db5c6475c1f38ab474 Mon Sep 17 00:00:00 2001 From: avi Date: Mon, 14 Sep 2026 11:20:55 -0500 Subject: [PATCH] =?UTF-8?q?Desktop:=20always-visible=20work=20indication?= =?UTF-8?q?=20=E2=80=94=20indeterminate=20bars=20while=20running=20w/o=20p?= =?UTF-8?q?ct,=20summarize=20job=20row,=20upload=20phase=20on=20library=20?= =?UTF-8?q?rows;=20dev-loop=20watcher=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/shonar/desktop/DesktopState.kt | 4 ++ .../main/kotlin/com/shonar/desktop/Screens.kt | 43 ++++++++++++++----- desktop/desktop-dev.sh | 40 +++++++++++------ 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/desktop/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt b/desktop/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt index ed6e0ed..17db9eb 100644 --- a/desktop/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt +++ b/desktop/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt @@ -840,6 +840,10 @@ class DesktopState(private val appDir: File = defaultAppDir()) { val ref = runCatching { provider.upload(draft) { p -> d.uploadProgress = p + // Row shows the upload too — pressing Transcribe must + // always change something on screen, immediately. + setLive(d.file.name, LiveProgress("uploading…", p)) + rescanStatuses() _detail.value = d.copy() } }.getOrElse { diff --git a/desktop/app/src/main/kotlin/com/shonar/desktop/Screens.kt b/desktop/app/src/main/kotlin/com/shonar/desktop/Screens.kt index bca7d54..de4066b 100644 --- a/desktop/app/src/main/kotlin/com/shonar/desktop/Screens.kt +++ b/desktop/app/src/main/kotlin/com/shonar/desktop/Screens.kt @@ -196,12 +196,19 @@ fun LibraryScreen(state: DesktopState) { else -> MaterialTheme.colorScheme.onSurfaceVariant }, ) - if (e.progress != null) { + if (e.status == FileStatus.RUNNING) { Spacer(Modifier.height(6.dp)) - LinearProgressIndicator( - progress = { e.progress }, - modifier = Modifier.fillMaxWidth().height(4.dp), - ) + val frac = e.progress + if (frac != null) { + LinearProgressIndicator( + progress = { frac }, + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } else { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } } } if (e.status == FileStatus.NEW || e.status == FileStatus.FAILED) { @@ -318,14 +325,30 @@ fun DetailScreen(state: DesktopState) { Text(label, style = MaterialTheme.typography.bodyMedium, color = if (it.status == "failed") MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary) - if (running && it.progress != null) { + if (running) { Spacer(Modifier.height(4.dp)) - LinearProgressIndicator( - progress = { it.progress / 100f }, - modifier = Modifier.fillMaxWidth().height(4.dp), - ) + if (it.progress != null) { + LinearProgressIndicator( + progress = { it.progress / 100f }, + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } else { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } } } + val sJob = detail.jobs.firstOrNull { it.jobType == "summarize" } + sJob?.takeIf { it.status == "running" || it.status == "queued" }?.let { + val label = if (it.status == "running") "Summarizing…" else "Summary queued" + Text(label, style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.primary) + Spacer(Modifier.height(4.dp)) + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } detail.error?.let { Text(it, color = MaterialTheme.colorScheme.error) if (tJob?.status == "failed") { diff --git a/desktop/desktop-dev.sh b/desktop/desktop-dev.sh index 021eeb4..5a8a153 100755 --- a/desktop/desktop-dev.sh +++ b/desktop/desktop-dev.sh @@ -4,28 +4,43 @@ # (SHONAR_DEV=1 -> uvicorn --reload), so backend edits apply live too. # # Stop with Ctrl-C (or pkill -f desktop-dev.sh). -set -euo pipefail +set -uo pipefail cd "$(dirname "$0")" export SHONAR_DEV=1 export DISPLAY="${DISPLAY:-:0}" -WATCH_DIRS=( - "app/src/main/kotlin" - "../../android/app/src/main/java/com/shonar" -) FLAG="$(pwd)/.dev-restart-flag" +# Watch targets resolved to absolute paths, non-existent dirs skipped at +# start but retried each cycle (inotifywait -r tolerates a dir appearing). +watch_targets() { + local t=() + for d in "$PWD/app/src/main/kotlin" \ + "$PWD/../android/app/src/main/java/com/shonar" \ + "$PWD/../backend/shonar"; do + [ -d "$d" ] && t+=("$d") + done + printf '%s\n' "${t[@]}" +} + pkill -f "com.shonar.desktop.MainKt" 2>/dev/null || true rm -f "$FLAG" sleep 1 ( - while inotifywait -qq -e close_write,move \ - --include '\.kt$|\.kts$' "${WATCH_DIRS[@]}" 2>/dev/null; do - echo "[dev] source change -> restarting app…" - touch "$FLAG" - pkill -f "com.shonar.desktop.MainKt" 2>/dev/null || true + while true; do + mapfile -t TARGETS < <(watch_targets) + [ ${#TARGETS[@]} -eq 0 ] && { sleep 5; continue; } + if inotifywait -qq -e close_write,move --include '\.kt$|\.kts$' \ + -r "${TARGETS[@]}"; then + echo "[dev] source change -> restarting app…" + touch "$FLAG" + pkill -f "com.shonar.desktop.MainKt" 2>/dev/null || true + sleep 2 # debounce bursts (editor writes can fire several events) + else + sleep 5 # targets vanished; retry with a fresh list + fi done ) & WATCHER=$! @@ -39,6 +54,7 @@ while true; do fi rm -f "$FLAG" # App closed cleanly or build failed — wait for the next edit. - # (Build failure: gradlew prints the error; the loop idles until you fix it.) - inotifywait -qq -e close_write,move --include '\.kt$|\.kts$' "${WATCH_DIRS[@]}" + mapfile -t TARGETS < <(watch_targets) + [ ${#TARGETS[@]} -eq 0 ] && { sleep 5; continue; } + inotifywait -qq -e close_write,move --include '\.kt$|\.kts$' -r "${TARGETS[@]}" || sleep 5 done