From f73f12056f96a699ef78250f07f9137ffd6d7168 Mon Sep 17 00:00:00 2001 From: avi Date: Mon, 14 Sep 2026 09:09:28 -0500 Subject: [PATCH] Desktop: live progress bar + % on the detail screen; openDetail polls running jobs --- .../kotlin/com/shonar/desktop/DesktopState.kt | 19 ++++++++++++++++++- .../main/kotlin/com/shonar/desktop/Screens.kt | 11 ++++++++++- 2 files changed, 28 insertions(+), 2 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 98e2ea5..0e60972 100644 --- a/desktop/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt +++ b/desktop/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt @@ -711,12 +711,29 @@ class DesktopState(private val appDir: File = defaultAppDir()) { // file looks empty and the summary appears unreachable. val saved = runCatching { reportFile(file).readText() }.getOrNull() val mapping = loadMapping(file) - _detail.value = DetailUi( + val d = DetailUi( file = file, reportText = saved, remoteId = mapping?.recordingId, overrideModel = mapping?.model, ) + _detail.value = d go(Screen.DETAIL) + // If the file is mid-pipeline (pump running, or reprocess started + // elsewhere), show live stage/progress instead of a stale report. + val remoteId = d.remoteId ?: return + pollJob = scope.launch { + while (true) { + val jobs = runCatching { parseJobs(provider.fetchJobs(remoteId)) } + .getOrNull().orEmpty() + if (_detail.value?.file != file) break + d.jobs = jobs + jobLabel(jobs)?.let { d.busy = it } ?: run { d.busy = null } + _detail.value = d.copy() + val active = jobs.any { it.status == "running" || it.status == "queued" } + if (!active) break + delay(2000) + } + } } fun setOverride(model: String?) { 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 d061cf8..3f42eb5 100644 --- a/desktop/app/src/main/kotlin/com/shonar/desktop/Screens.kt +++ b/desktop/app/src/main/kotlin/com/shonar/desktop/Screens.kt @@ -305,17 +305,26 @@ fun DetailScreen(state: DesktopState) { } val tJob = detail.jobs.firstOrNull { it.jobType == "transcribe" } tJob?.let { + val running = it.status == "running" val label = when { it.status == "succeeded" -> "Transcribed" it.status == "failed" -> "Failed" it.stage == "loading-model" -> "Loading model…" + running && it.progress != null -> "Transcribing… ${it.progress}%" it.stage == "transcribing" -> "Transcribing…" - it.status == "running" -> "Working… (attempt ${it.attempt}/${it.maxAttempts})" + running -> "Working… (attempt ${it.attempt}/${it.maxAttempts})" else -> "Queued" } Text(label, style = MaterialTheme.typography.bodyMedium, color = if (it.status == "failed") MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary) + if (running && it.progress != null) { + Spacer(Modifier.height(4.dp)) + LinearProgressIndicator( + progress = { it.progress / 100f }, + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } } detail.error?.let { Text(it, color = MaterialTheme.colorScheme.error)