Read a timeout in units, share one usage answer, and let a tilde mean home

The composer's settings row is outlined bubbles opening round menus, and
the message box is a TextFieldValue so anything put into it without being
typed -- a draft, a share, a slash command -- leaves the cursor at the end.

A tap that puts a text selection away no longer also collapses the card the
text was drawn in: every open and close on the session screen goes through
one guard that spends such a press on the selection.

The usage bar and the usage dialog were two polls of one measurement and
disagreed for up to a minute at a time; they are one feed now, and the
countdown rounds up to the minute in the one place both read.

A working directory typed as ~/repos/ai-app was four literal characters on
the local transport and as an argument on both, so the existence check
refused every home-relative path. It is checked by entering the directory
now, expanded for a local spawn the way the remote shell expands it, and
stored short so the phone draws what somebody would write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-03 19:56:36 -04:00
1 parent 2970a6cf9a
commit 32b2a4871a
16 files changed
+414 -99

No files matched your search

@@ -1,10 +1,13 @@
package com.example.aiapp
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.contentDescription
@@ -190,6 +193,24 @@ fun GlyphButton(
}
}
/**
* The square a glyph button occupies, with a spinner in it instead of a mark.
*
* For a button whose work is under way. It takes the button's whole box rather than the mark's, so
* swapping one for the other leaves everything in the row exactly where it was -- a control that
* changed the width of its header while it worked would move its neighbours at the moment somebody
* was pressing them.
*/
@Composable
fun GlyphSpinner(label: String, modifier: Modifier = Modifier) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier.size(GLYPH_BUTTON_SIZE).semantics { contentDescription = label },
) {
CircularProgressIndicator(Modifier.size(GLYPH_EXTENT), strokeWidth = 2.dp)
}
}
/**
* One icon, drawn as text.
*