Fix explorer back and session usage selection

This commit is contained in:
iris committed 2026-09-09 13:01:27 -04:00
1 parent 8c88a7e991
commit 14dd520719
10 files changed
+222 -57

No files matched your search

@@ -30,7 +30,7 @@ import java.time.OffsetDateTime
* own, so the only thing its Back could ever have meant was "put this away".
*/
@Composable
fun UsageDialog(feed: UsageFeed, onDismiss: () -> Unit) {
fun UsageDialog(feed: UsageFeed, session: SessionSummary, onDismiss: () -> Unit) {
// A plain Dialog rather than an AlertDialog, for the spacing alone. AlertDialog fixes the gaps
// between its title, content and buttons at sizes meant for a sentence of prose and a decision;
// this is a dense read-out, and those gaps left a band of empty dialog above Close that was
@@ -45,10 +45,6 @@ fun UsageDialog(feed: UsageFeed, onDismiss: () -> Unit) {
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
) {
// Deliberately not subtitled with the provider this was opened from. These
// numbers belong to an account on a particular machine -- naming the session's
// provider here made an echo session's screen read "echo" above a line reading
// "claude". Each machine names itself and the service it came from.
Text(
"Usage",
style = MaterialTheme.typography.headlineSmall,
@@ -65,10 +61,24 @@ fun UsageDialog(feed: UsageFeed, onDismiss: () -> Unit) {
}
Spacer(Modifier.height(8.dp))
// Scrolls rather than being trimmed: a machine can report any number of windows and
// there can be any number of machines, and a dialog is the one place where running
// out of room is silent. `fill = false` so a short read-out keeps a short dialog.
// a provider can report several billing pools, and a dialog is the one place where
// running out of room is silent. `fill = false` so a short read-out keeps a short
// dialog.
Column(Modifier.weight(1f, fill = false).verticalScroll(rememberScrollState())) {
UsageBody(feed.snapshots)
val state =
when (val snapshots = feed.snapshots) {
is LoadState.Loading -> LoadState.Loading
is LoadState.Error -> snapshots
is LoadState.Loaded ->
LoadState.Loaded(
usageSnapshotsFor(
snapshots.value,
session.setup,
session.usageProvider,
)
)
}
UsageBody(state)
}
TextButton(onClick = onDismiss, modifier = Modifier.align(Alignment.End)) {
Text("Close")
@@ -87,18 +97,18 @@ private fun UsageBody(state: LoadState<List<UsageSnapshot>>) {
is LoadState.Error -> Text(current.message, color = MaterialTheme.colorScheme.error)
is LoadState.Loaded ->
if (current.value.isEmpty()) {
// Not an error and not a blank screen: no machine offers a paid service, so
// Not an error and not a blank screen: this provider has no paid quota, so
// there is genuinely nothing to report and saying so is the answer.
Text(
"No machine here runs anything with usage limits.",
"This session's provider has no usage limits.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
} else {
// No card around each machine. A card is a step up the surface ladder, and
// inside a dialog -- itself a raised surface -- the step barely renders while
// costing 16dp on every side. What separates one machine from the next is the
// line naming it.
// No card around each pool. A card is a step up the surface ladder, and inside
// a dialog -- itself a raised surface -- the step barely renders while costing
// 16dp on every side. What separates one pool from the next is the line naming
// it.
current.value.forEachIndexed { index, snapshot ->
if (index > 0) {
Spacer(Modifier.height(20.dp))