Voice chips follow the saved report's voice in saved-report view
Opening a recording whose live summary row isn't loaded fell through to sumTone (null = Neutral), so the pill showed Neutral while the report text on screen read "*Voice: funny*". chipTone now reads the report's own *Voice: X* metadata line as the fallback, so the chips can never contradict the artifact being displayed. Running job and live summary rows still take precedence. 3 new tests (reportVoice).
This commit is contained in:
parent
1a6acd92d6
commit
70483b4cf8
3 changed files with 38 additions and 0 deletions
|
|
@ -1957,6 +1957,21 @@ class DesktopState(private val appDir: File = defaultAppDir()) {
|
|||
listOf(uploadStage, tStage, sStage, fStage), elapsed, eta)
|
||||
}
|
||||
|
||||
/** Voice recorded inside a saved .transcript.md report — the
|
||||
* `*Voice: X*` metadata line written by Reports.renderReport.
|
||||
* Used when the live summary row isn't loaded (saved-report
|
||||
* view) so the voice chips can't contradict the report on
|
||||
* screen. "neutral" (or absent) maps to null. Pure — tested. */
|
||||
fun reportVoice(reportText: String?): String? {
|
||||
reportText ?: return null
|
||||
val line = reportText.lineSequence().firstOrNull {
|
||||
it.trim().startsWith("*Voice:")
|
||||
} ?: return null
|
||||
val v = line.trim().removePrefix("*Voice:").removeSuffix("*")
|
||||
.trim()
|
||||
return v.takeIf { it.isNotBlank() && !it.equals("neutral", true) }
|
||||
}
|
||||
|
||||
const val KEY_URL = "server.url"
|
||||
const val KEY_FOLDER = "library.folder"
|
||||
const val KEY_PASSWORD = "local.password"
|
||||
|
|
|
|||
|
|
@ -683,6 +683,11 @@ fun DetailScreen(state: DesktopState) {
|
|||
val chipTone: String? = when {
|
||||
sJobNow != null && sJobNow.status in setOf("queued", "running") -> sJobNow.tone
|
||||
sSummary != null -> sSummary.tone
|
||||
// Live summary row unavailable (saved-report view): read the
|
||||
// voice out of the report itself so the chips can't contradict
|
||||
// the "*Voice: funny*" text on screen (user: report said funny,
|
||||
// Neutral pill was highlighted).
|
||||
detail.reportText != null -> DesktopState.reportVoice(detail.reportText)
|
||||
else -> sumTone
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -189,4 +189,22 @@ class JobProgressTest {
|
|||
assertNull(DesktopState.etaFrom(300, 99)) // no promising the last token
|
||||
assertEquals(25L, DesktopState.etaFrom(100, 80))
|
||||
}
|
||||
|
||||
// ---- reportVoice: chips follow the saved report's own voice ----
|
||||
|
||||
@Test fun `reportVoice reads the metadata line`() {
|
||||
val md = "# X\n## Summary\n*Voice: funny*\n\ntext"
|
||||
assertEquals("funny", DesktopState.reportVoice(md))
|
||||
}
|
||||
|
||||
@Test fun `reportVoice neutral and absent map to null`() {
|
||||
assertNull(DesktopState.reportVoice("# X\n*Voice: neutral*\n"))
|
||||
assertNull(DesktopState.reportVoice("# X\nno voice line\n"))
|
||||
assertNull(DesktopState.reportVoice(null))
|
||||
}
|
||||
|
||||
@Test fun `reportVoice keeps multi-word tones intact`() {
|
||||
assertEquals("dry, witty",
|
||||
DesktopState.reportVoice("*Voice: dry, witty*"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue