Voice chips directly under the summary (before the long sections): every position further down fell off-screen and read as 'missing'; saved-report path keeps them under the report; dedupe with the bottom block

This commit is contained in:
avi 2026-09-17 11:09:19 -05:00
commit 1f688e79bc

View file

@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
@ -853,6 +854,43 @@ fun DetailScreen(state: DesktopState) {
Text(it, style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.primary)
}
// Voice controls FIRST under the summary — before the long
// sections. Every time they sat further down (page bottom,
// then below the bullets) they fell off-screen and read as
// "missing". They act on this summary; they live here.
run {
val report = detail.reportText
val hasTranscript = detail.transcript != null ||
(report != null && !report.contains("_No transcript available._"))
val remoteKnown = state.remoteIdFor(detail.file) != null
val sumBusy = detail.jobs.firstOrNull { it.jobType == "summarize" }
?.status in setOf("queued", "running")
val voicesEnabled = remoteKnown && hasTranscript && !sumBusy
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text("Re-summarize in the voice of",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
SUMMARIZE_PERSONAS.forEach { (label, tone) ->
FilterChip(
selected = sumTone == tone,
enabled = voicesEnabled,
onClick = {
sumTone = tone
state.summarize(tone)
},
label = { Text(label) },
)
}
}
if (!voicesEnabled && !remoteKnown) {
Text("Summarize unlocks once this file is uploaded (press Transcribe).",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant)
}
}
}
Spacer(Modifier.height(4.dp))
// Full structured sections (key points, decisions, action
// items, questions, details) right on screen — the saved
// .md report had them but the live view showed only the
@ -878,22 +916,21 @@ fun DetailScreen(state: DesktopState) {
Spacer(Modifier.height(4.dp))
}
}
// Voice controls sit directly under the summary — they act on it,
// and at the page bottom (below a long transcript) they read as
// gone. Hoisted above the transcript section; same conditions.
// Re-transcribe + summarize progress (the chips themselves live
// under the summary for the live path; this block also covers a
// file opened as a saved report with no live summary object).
if (detail.transcript != null || detail.reportText != null) {
// Honest labels: "Re-…" only when that output actually exists.
val report = detail.reportText
val hasTranscript = detail.transcript != null ||
(report != null && !report.contains("_No transcript available._"))
val hasSummary = detail.summary != null ||
(report?.contains("## Summary") == true)
val remoteKnown = state.remoteIdFor(detail.file) != null
// A voice chip IS the action: clicking one re-summarizes in
// that voice immediately — no separate Re-summarize button.
val sumBusy = detail.jobs.firstOrNull { it.jobType == "summarize" }
?.status in setOf("queued", "running")
val voicesEnabled = remoteKnown && hasTranscript && !sumBusy
if (detail.transcript == null) {
// Saved-report path: chips here, same rules.
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text(if (hasSummary) "Re-summarize in the voice of"
else "Summarize in the voice of",
@ -913,6 +950,7 @@ fun DetailScreen(state: DesktopState) {
}
}
}
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
TextButton({ state.transcribe() }) {
Text(if (hasTranscript) "Re-transcribe" else "Transcribe")