Show every option a question offers, on the call that asked
Reported by Iris through the dev-updater session: a two-question AskUserQuestion arrived with only one option visible per question, so the answer she sent was the only one she had been offered. The cause was a `Row`. It hands out intrinsic widths in order and clips whatever runs past the edge, so the first option or two drew and the rest went off the side of the screen -- which does not read as a bug, it reads as those having been the only choices. The same Row was in the permission ask beside it; both wrap now. That pairing is the reason to look: a rule stated on one member of a set is usually missing from the others. The rest of what she asked for, and what each was: - It drew twice, as the tool call and again as loose question cards, because the backend marked these questions as belonging to no call. They belong to the call that asked, and now say so. - So it renders like any other tool: one card, its own heading, opened because a decision cannot be made from a closed row. - Each option shows its description and its `preview` block, which is the part a reader is deciding on and none of which was reaching them. - "Other" is a field on every question. The harness always offers it, so leaving it out narrowed a question that was never that narrow. - A multi-select sends the labels it collected as one string, which is the tool's own schema rather than a guess -- its answers map is string-valued. - No spinner while it waits. A spinner says the machine is working; here the machine is idle and the turn is stopped on the person, so the card says "your turn" in the colour this app already uses for that. Verified against a real session as well as the echo fixture: haiku asked two questions with three described options each, both were answered from the phone, and the model carried on with the answers. Echo grew `/ask` so the shape can be looked at without paying a model to produce one, and its option cards are outlined rather than tinted -- as one surface step up they were three paragraphs where three things to press should be.
This commit is contained in:
1 parent
cae04c2559
commit
fea8e7e92b
5 files changed
+518
-61
No files matched your search
@@ -97,14 +97,17 @@ sealed class TranscriptItem {
|
||||
val output: String,
|
||||
val done: Boolean,
|
||||
/**
|
||||
* The permission ask for this call, when there is one.
|
||||
* The questions this call is waiting on, in the order they were asked.
|
||||
*
|
||||
* On the call's own row rather than beside it: the ask used to arrive as a second card
|
||||
* On the call's own row rather than beside it: an ask used to arrive as a second card
|
||||
* repeating the input verbatim, so the reader saw the same command twice and had to work
|
||||
* out that it was one event. The backend says which call a permission is about, so this is
|
||||
* a fact rather than a match on the input.
|
||||
* out that it was one event. The backend says which call a question is about, so this is a
|
||||
* fact rather than a match on the input.
|
||||
*
|
||||
* A list because AskUserQuestion asks up to four at once, and they are one decision to make
|
||||
* -- a permission is the case of exactly one, not a different shape.
|
||||
*/
|
||||
val ask: QuestionCard? = null,
|
||||
val asks: List<QuestionCard> = emptyList(),
|
||||
/**
|
||||
* Images this call's result carried, drawn under it.
|
||||
*
|
||||
@@ -214,7 +217,7 @@ fun foldEvent(items: List<TranscriptItem>, entry: SeqEvent): List<TranscriptItem
|
||||
event.about != null &&
|
||||
items.any { it is TranscriptItem.ToolRun && it.id == event.about }
|
||||
) {
|
||||
updateTool(items, event.about) { it.copy(ask = card) }
|
||||
updateTool(items, event.about) { it.copy(asks = it.asks + card) }
|
||||
} else {
|
||||
items + card
|
||||
}
|
||||
@@ -227,8 +230,13 @@ fun foldEvent(items: List<TranscriptItem>, entry: SeqEvent): List<TranscriptItem
|
||||
when {
|
||||
it is TranscriptItem.QuestionCard && it.id == event.id ->
|
||||
it.copy(answer = event.answer)
|
||||
it is TranscriptItem.ToolRun && it.ask?.id == event.id ->
|
||||
it.copy(ask = it.ask.copy(answer = event.answer))
|
||||
it is TranscriptItem.ToolRun && it.asks.any { ask -> ask.id == event.id } ->
|
||||
it.copy(
|
||||
asks =
|
||||
it.asks.map { ask ->
|
||||
if (ask.id == event.id) ask.copy(answer = event.answer) else ask
|
||||
}
|
||||
)
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
@@ -798,10 +806,8 @@ fun SessionScreen(
|
||||
if (id in expandedTools) expandedTools - id
|
||||
else expandedTools + id
|
||||
},
|
||||
onAnswer = { call, answer ->
|
||||
call.ask?.let { ask ->
|
||||
act { answerQuestion(settings, summary.id, ask.id, answer) }
|
||||
}
|
||||
onAnswer = { questionId, answer ->
|
||||
act { answerQuestion(settings, summary.id, questionId, answer) }
|
||||
},
|
||||
image = { ref -> SessionImage(settings, summary.id, ref) },
|
||||
)
|
||||
@@ -819,16 +825,14 @@ fun SessionScreen(
|
||||
expandedTools - item.id
|
||||
else expandedTools + item.id
|
||||
},
|
||||
onAnswer = { answer ->
|
||||
item.ask?.let { ask ->
|
||||
act {
|
||||
answerQuestion(
|
||||
settings,
|
||||
summary.id,
|
||||
ask.id,
|
||||
answer,
|
||||
)
|
||||
}
|
||||
onAnswer = { questionId, answer ->
|
||||
act {
|
||||
answerQuestion(
|
||||
settings,
|
||||
summary.id,
|
||||
questionId,
|
||||
answer,
|
||||
)
|
||||
}
|
||||
},
|
||||
image = { ref -> SessionImage(settings, summary.id, ref) },
|
||||
@@ -1033,11 +1037,11 @@ private fun QuestionRow(question: TranscriptItem.QuestionCard, onAnswer: (String
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
} else {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
question.options.forEach { option ->
|
||||
OutlinedButton(onClick = { onAnswer(option) }) { Text(option) }
|
||||
}
|
||||
}
|
||||
// Wrapped, not in a Row. A Row hands out intrinsic widths in order and clips
|
||||
// whatever runs past the edge, so a question with four options showed the first
|
||||
// one or two and silently dropped the rest off the side of the screen -- which
|
||||
// does not look like a bug, it looks like those were the only choices.
|
||||
AnswerOptions(question.options, onAnswer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user