diff --git a/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt b/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt index d966881..f236365 100644 --- a/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt +++ b/app/src/main/kotlin/com/shonar/desktop/DesktopState.kt @@ -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" diff --git a/app/src/main/kotlin/com/shonar/desktop/Screens.kt b/app/src/main/kotlin/com/shonar/desktop/Screens.kt index e35d0ed..cc8cbbd 100644 --- a/app/src/main/kotlin/com/shonar/desktop/Screens.kt +++ b/app/src/main/kotlin/com/shonar/desktop/Screens.kt @@ -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 } diff --git a/app/src/test/kotlin/com/shonar/desktop/JobProgressTest.kt b/app/src/test/kotlin/com/shonar/desktop/JobProgressTest.kt index c054e54..a8f6bf4 100644 --- a/app/src/test/kotlin/com/shonar/desktop/JobProgressTest.kt +++ b/app/src/test/kotlin/com/shonar/desktop/JobProgressTest.kt @@ -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*")) + } }