diff --git a/app/src/main/kotlin/com/shonar/desktop/Screens.kt b/app/src/main/kotlin/com/shonar/desktop/Screens.kt index 280579e..2caee7d 100644 --- a/app/src/main/kotlin/com/shonar/desktop/Screens.kt +++ b/app/src/main/kotlin/com/shonar/desktop/Screens.kt @@ -777,11 +777,15 @@ fun DetailScreen(state: DesktopState) { } val sJob = detail.jobs.firstOrNull { it.jobType == "summarize" } sJob?.takeIf { it.status == "running" || it.status == "queued" }?.let { + // Honest milestone progress: the engine counts completed + // contract units (JSON keys/items finished), so the number + // means real work done — a determinate bar is truthful now. + val toneWord = sJob.tone?.lowercase()?.replace(",", "") + ?.takeIf { it.isNotBlank() && it != "neutral" } val label = when { - // Streaming summarize shows elapsed time, not the backend - // number: it pins at 99% the moment tokens start. - it.status == "running" && it.progress != null -> - "Summarizing… working" + + it.status == "running" && it.progress != null && it.progress > 0 -> + "Summarizing" + (toneWord?.let { t -> " $t" } ?: "") + + "… ${it.progress}%" + DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis()) it.status == "running" -> "Summarizing… 0% waiting for model" + DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis()) @@ -792,9 +796,16 @@ fun DetailScreen(state: DesktopState) { color = MaterialTheme.colorScheme.primary) if (it.status == "running") { Spacer(Modifier.height(4.dp)) - LinearProgressIndicator( - modifier = Modifier.fillMaxWidth().height(4.dp), - ) + if (it.progress != null && it.progress > 0) { + LinearProgressIndicator( + progress = { it.progress / 100f }, + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } else { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth().height(4.dp), + ) + } } } // Rescue-swap note: the engine succeeded but had to switch @@ -989,23 +1000,33 @@ fun DetailScreen(state: DesktopState) { val sStatus = sJob?.status val sProg = sJob?.progress val sAttempt = sJob?.attempt ?: 0 - // Bar stays indeterminate while running: backend summarize - // progress pins at 99 as soon as tokens start, so a - // determinate bar would freeze near-full for minutes. - LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + // Determinate bar once real units complete: the engine + // counts finished JSON contract units, so the percent + // means work done, not vibes. Indeterminate only while + // queued or pre-first-token. + if (sProg != null && sProg > 0) { + LinearProgressIndicator(progress = { sProg / 100f }, + modifier = Modifier.fillMaxWidth()) + } else { + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + } Text(buildString { append(when { sStatus == "queued" -> "Queued…" + DesktopState.waitSuffix(sJob?.createdAt, System.currentTimeMillis()) - sProg != null && sProg > 0 -> "Summarizing… working" + + sProg != null && sProg > 0 -> + "Summarizing" + (sJob?.tone?.lowercase() + ?.replace(",", "") + ?.takeIf { it.isNotBlank() && it != "neutral" } + ?.let { " $it" } ?: "") + + "… $sProg%" + DesktopState.waitSuffix(sJob?.startedAt, System.currentTimeMillis()) else -> "Summarizing… 0% waiting for model" + DesktopState.waitSuffix(sJob?.startedAt, System.currentTimeMillis()) }) - sumTone?.let { append(" · $it") } if (sAttempt > 0) append(" (attempt $sAttempt/${sJob?.maxAttempts})") }, style = MaterialTheme.typography.bodySmall,