Make jump-to-latest the same chevron, pointing down

It was a labelled button beside a tool group that collapses with a
drawn chevron -- two controls doing the same kind of thing in two
visual languages. Now one `Chevron` composable serves both directions,
parameterised rather than copied, since a pair that differs by a minus
sign drifts and the drift is a bug in exactly one direction.

The comment it replaces argued against an arrow here, on the grounds
that the list is laid out upside down. That reasoning was about the
code: nobody reading the screen knows the list is reversed, and on
screen the newest message is at the bottom, which is where this goes.

It draws no text, so the name lives in its content description -- the
whole of what a screen reader has, and the answer to "what was that
arrow for" later.

Verified on the emulator: scrolled up, the chevron appears bottom
centre matching the group's; tapped, it returns to the newest message
and takes itself away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-08-29 13:46:13 -04:00
1 parent 011ed0d0e1
commit 73923dbbc3
3 files changed
+78 -26

No files matched your search

@@ -28,6 +28,10 @@ import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Surface
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@@ -660,16 +664,29 @@ fun SessionScreen(
// Only while the newest message is off-screen. Reading back
// through a conversation is a place to be, not a state to be
// rescued from, so this waits to be wanted -- and says where it
// goes rather than drawing an arrow, since an arrow in a list
// that is laid out upside down is the one thing nobody should
// have to reason about.
// rescued from, so this waits to be wanted.
//
// Down, and the same chevron a tool group collapses with: the
// list is built upside down internally, but nobody reading it
// knows that -- on screen the newest message is at the bottom,
// which is where this goes. The name is carried in the
// description, since an arrow alone says nothing to a screen
// reader and nothing to whoever finds this in six months.
if (!followTail) {
FilledTonalButton(
Surface(
onClick = { scope.launch { listState.animateScrollToItem(0) } },
modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 12.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.surfaceContainerHigh,
modifier =
Modifier.align(Alignment.BottomCenter)
.padding(bottom = 12.dp)
.semantics { contentDescription = "Jump to latest" },
) {
Text("Jump to latest", style = MaterialTheme.typography.bodySmall)
Chevron(
pointingUp = false,
colour = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
)
}
}
}