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 d656440..b8718c6 100644 --- a/app/androidApp/src/main/kotlin/com/example/aiapp/SessionScreen.kt +++ b/app/androidApp/src/main/kotlin/com/example/aiapp/SessionScreen.kt @@ -262,9 +262,14 @@ fun SessionScreen( // A new item at the newest end shifts every index by one, so the view // has to step back to 0 to stay put. One item, instantly -- not a // journey through the transcript. + // Anything that changes how much room the list has, as well as a new + // item arriving. Typing is the case that gets missed: the field grows + // from one line to four and the keyboard opens under it, and neither + // is a new message, so watching the item count alone leaves the newest + // text drifting out of sight while somebody writes a reply to it. LaunchedEffect(listState) { - snapshotFlow { items.size } - .collect { count -> if (followTail && count > 0) listState.scrollToItem(0) } + snapshotFlow { Pair(items.size, listState.layoutInfo.viewportSize.height) } + .collect { (count, _) -> if (followTail && count > 0) listState.scrollToItem(0) } } // Reaching the far end of what is loaded -- the oldest item, which in // this layout is the last index -- fetches the page before it. @@ -393,7 +398,6 @@ fun SessionScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) } - StatusText(status) // Beside the provider it reports on, which is the line directly to its left. // // Its real home is this provider's settings, which do not exist yet; until they do, @@ -434,6 +438,38 @@ fun SessionScreen( contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp), ) { + // Where the next thing will appear: at the end of what has + // happened, which in this layout is the top of the list. + // In the corner it was a label about the session; here it + // is a place, and the eye is already there because that is + // where the newest message is. + if (running) { + item { + Row(verticalAlignment = Alignment.CenterVertically) { + CircularProgressIndicator( + modifier = Modifier.width(14.dp).height(14.dp), + strokeWidth = 2.dp, + ) + Spacer(Modifier.width(8.dp)) + Text( + if (status == "compacting") "compacting" else "working", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } else if (status == "exited") { + // Still said somewhere: a session whose process is gone + // cannot be typed at, and with the corner label removed + // nothing else on this screen would mention it. + item { + Text( + "exited", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } // Reversed to match the layout, so index 0 is the newest and // the reader still sees them in the order they happened. items(items.asReversed()) { item ->