Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
bb191eec21
17 files changed
+627
-310
No files matched your search
@@ -27,8 +27,8 @@ import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
@@ -316,7 +316,6 @@ fun SessionScreen(
|
||||
settings: ServerSettings,
|
||||
summary: SessionSummary,
|
||||
onBack: () -> Unit,
|
||||
onUsage: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -737,12 +736,18 @@ fun SessionScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// One poll for this machine's limits, read by the two things that show them: the bar under
|
||||
// the header, and the colour of the button that opens the dialog.
|
||||
val usage = rememberSessionUsage(settings, summary.setup)
|
||||
var usageOpen by remember { mutableStateOf(false) }
|
||||
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
) {
|
||||
TextButton(onClick = onBack) { Text("Back") }
|
||||
GlyphButton(BACK_GLYPH, "Back", onBack)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(title, style = MaterialTheme.typography.titleMedium)
|
||||
Text(
|
||||
@@ -766,22 +771,29 @@ fun SessionScreen(
|
||||
// the paid service's own numbers, so a session on a provider with no such service
|
||||
// gets an honest "unavailable" rather than a hidden button -- a control that comes
|
||||
// and goes makes its absence the signal, and absence cannot say why.
|
||||
TextButton(onClick = onUsage) { Text("Usage") }
|
||||
// A step down from this session, so it sits at the end of the session's own row.
|
||||
// The name is the whole of what it holds today, which is why it is a gear and not a
|
||||
// word: there will be more, and a bar of words has nowhere to put it.
|
||||
IconButton(
|
||||
onClick = onSettings,
|
||||
modifier = Modifier.semantics { contentDescription = "Session settings" },
|
||||
) {
|
||||
Gear()
|
||||
// Coloured by the worst window behind it, so the row says whether the limits are
|
||||
// worth opening before anybody opens them. Blue at every ordinary level and only
|
||||
// yellow or red near a limit -- and the theme's plain control colour whenever there
|
||||
// is no measurement, since blue is the low end of the scale here and would read as
|
||||
// "checked, and fine" about a machine nobody could reach.
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(GLYPH_BUTTON_GAP)) {
|
||||
GlyphButton(
|
||||
USAGE_GLYPH,
|
||||
"Usage",
|
||||
{ usageOpen = true },
|
||||
colour = usageGlyphColour(usage),
|
||||
)
|
||||
// A step down from this session, so it sits at the end of the session's own row.
|
||||
// The name is the whole of what it holds today, which is why it is a cog and not
|
||||
// a word: there will be more, and a bar of words has nowhere to put it.
|
||||
GlyphButton(SETTINGS_GLYPH, "Session settings", onSettings)
|
||||
}
|
||||
}
|
||||
|
||||
// Under the header, above everything the session itself says: it is a fact about the
|
||||
// machine rather than a turn in the conversation, and it is the number that decides
|
||||
// whether to keep going -- which was a screen away from where that gets decided.
|
||||
SessionUsageBar(settings = settings, setup = summary.setup)
|
||||
SessionUsageBar(usage)
|
||||
|
||||
(streamError ?: actionError)?.let { message ->
|
||||
Text(
|
||||
@@ -1052,21 +1064,46 @@ fun SessionScreen(
|
||||
}
|
||||
if (running) {
|
||||
OutlinedButton(onClick = { act { interruptSession(settings, summary.id) } }) {
|
||||
Text("Stop")
|
||||
// A filled square, which is what stop has looked like since tape decks.
|
||||
// Outlined beside the filled Send, so the pair still reads as one primary
|
||||
// action and one secondary -- the glyphs changed, the weighting did not.
|
||||
Glyph(
|
||||
STOP_GLYPH,
|
||||
colour = LocalContentColor.current,
|
||||
modifier = Modifier.semantics { contentDescription = "Stop" },
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(8.dp))
|
||||
}
|
||||
// "Queue" while a turn is in flight, because that is what
|
||||
// sending then does: the message is injected at the next
|
||||
// tool boundary rather than starting a turn of its own.
|
||||
// Naming it Send there would promise something immediate
|
||||
// and describe something that waits.
|
||||
Button(onClick = { send() }) { Text(if (running) "Queue" else "Send") }
|
||||
// The paper plane alone when it means send. While a turn is in flight it keeps
|
||||
// the word "Queue" beside it, because that is what sending then does -- the
|
||||
// message is injected at the next tool boundary rather than starting a turn of
|
||||
// its own -- and an icon that does two things while looking identical would
|
||||
// promise something immediate and do something that waits. The word is also the
|
||||
// button's accessible name, which is all a screen reader gets either way.
|
||||
Button(onClick = { send() }) {
|
||||
Glyph(
|
||||
SEND_GLYPH,
|
||||
colour = LocalContentColor.current,
|
||||
modifier = Modifier.semantics { contentDescription = sendLabel(running) },
|
||||
)
|
||||
if (running) {
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(sendLabel(running))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usageOpen) {
|
||||
UsageDialog(settings = settings, onDismiss = { usageOpen = false })
|
||||
}
|
||||
}
|
||||
|
||||
/** What pressing Send does right now, said the same way to the eye and to a screen reader. */
|
||||
private fun sendLabel(running: Boolean) = if (running) "Queue" else "Send"
|
||||
|
||||
/**
|
||||
* An inline transcript image, fetched (authenticated, pinned) from the session's files route. The
|
||||
* bitmap is remembered per ref, so scrolling doesn't refetch.
|
||||
|
||||
Reference in new issue
Block a user