Detail bar shows real percent + tone once progress > 0

With milestone-backed progress the number is truthful, so replace the
indeterminate 'working' bar with a determinate one labeled 'Summarizing
<tone>… N%' (indeterminate spinner only while waiting for the first token).
This commit is contained in:
avi 2026-09-18 15:00:45 -05:00
commit f258470a6a

View file

@ -777,11 +777,15 @@ fun DetailScreen(state: DesktopState) {
} }
val sJob = detail.jobs.firstOrNull { it.jobType == "summarize" } val sJob = detail.jobs.firstOrNull { it.jobType == "summarize" }
sJob?.takeIf { it.status == "running" || it.status == "queued" }?.let { 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 { val label = when {
// Streaming summarize shows elapsed time, not the backend it.status == "running" && it.progress != null && it.progress > 0 ->
// number: it pins at 99% the moment tokens start. "Summarizing" + (toneWord?.let { t -> " $t" } ?: "") +
it.status == "running" && it.progress != null -> "… ${it.progress}%" +
"Summarizing… working" +
DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis()) DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis())
it.status == "running" -> "Summarizing… 0% waiting for model" + it.status == "running" -> "Summarizing… 0% waiting for model" +
DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis()) DesktopState.waitSuffix(it.startedAt, System.currentTimeMillis())
@ -792,11 +796,18 @@ fun DetailScreen(state: DesktopState) {
color = MaterialTheme.colorScheme.primary) color = MaterialTheme.colorScheme.primary)
if (it.status == "running") { if (it.status == "running") {
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
if (it.progress != null && it.progress > 0) {
LinearProgressIndicator(
progress = { it.progress / 100f },
modifier = Modifier.fillMaxWidth().height(4.dp),
)
} else {
LinearProgressIndicator( LinearProgressIndicator(
modifier = Modifier.fillMaxWidth().height(4.dp), modifier = Modifier.fillMaxWidth().height(4.dp),
) )
} }
} }
}
// Rescue-swap note: the engine succeeded but had to switch // Rescue-swap note: the engine succeeded but had to switch
// summarizers mid-job. Tell the user in plain words, not the raw // summarizers mid-job. Tell the user in plain words, not the raw
// provider ids. // provider ids.
@ -989,23 +1000,33 @@ fun DetailScreen(state: DesktopState) {
val sStatus = sJob?.status val sStatus = sJob?.status
val sProg = sJob?.progress val sProg = sJob?.progress
val sAttempt = sJob?.attempt ?: 0 val sAttempt = sJob?.attempt ?: 0
// Bar stays indeterminate while running: backend summarize // Determinate bar once real units complete: the engine
// progress pins at 99 as soon as tokens start, so a // counts finished JSON contract units, so the percent
// determinate bar would freeze near-full for minutes. // 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()) LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
}
Text(buildString { Text(buildString {
append(when { append(when {
sStatus == "queued" -> "Queued…" + sStatus == "queued" -> "Queued…" +
DesktopState.waitSuffix(sJob?.createdAt, DesktopState.waitSuffix(sJob?.createdAt,
System.currentTimeMillis()) 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, DesktopState.waitSuffix(sJob?.startedAt,
System.currentTimeMillis()) System.currentTimeMillis())
else -> "Summarizing… 0% waiting for model" + else -> "Summarizing… 0% waiting for model" +
DesktopState.waitSuffix(sJob?.startedAt, DesktopState.waitSuffix(sJob?.startedAt,
System.currentTimeMillis()) System.currentTimeMillis())
}) })
sumTone?.let { append(" · $it") }
if (sAttempt > 0) if (sAttempt > 0)
append(" (attempt $sAttempt/${sJob?.maxAttempts})") append(" (attempt $sAttempt/${sJob?.maxAttempts})")
}, style = MaterialTheme.typography.bodySmall, }, style = MaterialTheme.typography.bodySmall,