diff --git a/app/androidApp/src/main/kotlin/com/example/aiapp/SessionScreen.kt b/app/androidApp/src/main/kotlin/com/example/aiapp/SessionScreen.kt index 80fdfd1..4fdef21 100644 --- a/app/androidApp/src/main/kotlin/com/example/aiapp/SessionScreen.kt +++ b/app/androidApp/src/main/kotlin/com/example/aiapp/SessionScreen.kt @@ -323,6 +323,11 @@ fun SessionScreen( // reads as a rename that did not take. var title by remember(summary.id) { mutableStateOf(summary.title) } var model by remember { mutableStateOf(summary.model) } + // The thinking level, held here for the same reason [title] is: the settings dialog can change + // it, and the row this screen was opened from is a snapshot taken before that. Read straight + // from the summary, a level set here was drawn as the old one again the next time the dialog + // was opened, and only came back right after a return to the list refetched the row. + var effort by remember(summary.id) { mutableStateOf(summary.effort) } var permissionMode by remember { mutableStateOf(summary.permissionMode ?: "auto") } // The models this provider actually offers, asked of the server rather than listed here: a // hardcoded list is a claim about a machine. @@ -2064,8 +2069,9 @@ fun SessionScreen( settings = settings, sessionId = summary.id, title = title, - effort = summary.effort.takeIf { summary.takesEffort }, + effort = effort.takeIf { summary.takesEffort }, takesEffort = summary.takesEffort, + onEffortChanged = { effort = it }, cachedBytes = cachedBytes, // The purge finishes before the epoch moves, because the relaunched opening effect // reads the same directory and would otherwise draw what is about to be deleted. The diff --git a/app/androidApp/src/main/kotlin/com/example/aiapp/SessionSettingsDialog.kt b/app/androidApp/src/main/kotlin/com/example/aiapp/SessionSettingsDialog.kt index 0a68e41..ab50ca6 100644 --- a/app/androidApp/src/main/kotlin/com/example/aiapp/SessionSettingsDialog.kt +++ b/app/androidApp/src/main/kotlin/com/example/aiapp/SessionSettingsDialog.kt @@ -61,13 +61,17 @@ fun SessionSettingsDialog( title: String, onRenamed: (String) -> Unit, /** - * How hard the model thinks, as the session reports it, or null for the CLI's own default. + * How hard the model thinks, or null for the CLI's own default. * - * Taken from the row this dialog was opened over rather than fetched, because unlike the - * notification switch there is nothing else that changes it: the level is this app's to set and - * the server does not resolve it into something else. + * Owned by the screen behind this rather than held here, like [title]: this dialog is what + * changes it, and a level kept only for as long as the dialog is open is the old one again the + * next time it is opened. + * + * Not fetched, because unlike the notification switch there is nothing else that changes it: + * the level is this app's to set and the server does not resolve it into something else. */ effort: String?, + onEffortChanged: (String?) -> Unit, /** Whether a level does anything here; the row is left out entirely where it does not. */ takesEffort: Boolean, /** @@ -85,7 +89,6 @@ fun SessionSettingsDialog( ) { val scope = rememberCoroutineScope() var name by remember(sessionId) { mutableStateOf(title) } - var level by remember(sessionId) { mutableStateOf(effort) } var effortError by remember { mutableStateOf(null) } var saving by remember { mutableStateOf(false) } var error by remember { mutableStateOf(null) } @@ -165,14 +168,14 @@ fun SessionSettingsDialog( * control that stays where it was put after a refusal is stating something untrue. */ fun setEffort(chosen: String?) { - val was = level - level = chosen + val was = effort + onEffortChanged(chosen) effortError = null scope.launch { try { withContext(Dispatchers.IO) { setSessionEffort(settings, sessionId, chosen) } } catch (e: ApiException) { - level = was + onEffortChanged(was) effortError = e.message } } @@ -421,7 +424,7 @@ fun SessionSettingsDialog( ) { Text("Thinking", modifier = Modifier.weight(1f)) PickerButton( - current = level ?: DEFAULT_EFFORT, + current = effort ?: DEFAULT_EFFORT, // The level the CLI picks for itself is in the list as well as in the // button, so leaving a level is not a one-way trip -- the same // correction the model picker carries.