Questions tab, question bubbles, answer snapshots; errors to transcript

This commit is contained in:
Ian Gulliver
2026-10-18 15:06:06 -07:00
parent 1ef3594016
commit 10a80ccb22
31 changed files with 744 additions and 665 deletions
+67 -2
View File
@@ -1019,10 +1019,73 @@ struct RowView: View {
case .snapshot(let snap):
SnapshotRow(snap: snap, review: row.review, taskId: row.taskId)
case .question:
if let item = row.question {
QuestionBubble(item: item)
}
}
}
}
private struct QuestionBubble: View {
let item: InboxItem
@Environment(Store.self) private var store
@ViewState private var hover = false
var body: some View {
let open = item.state == .open
let tone = open ? Palette.warn : Palette.muted
VStack(alignment: .leading, spacing: 4) {
HStack(spacing: 4) {
Text("asked")
WhenText(when: item.createdAt)
}
.font(Fonts.font(11))
.foregroundStyle(Palette.muted)
MarkdownView(blocks: parseMarkdown(jsString(item.payload.value["question"] ?? "")), ctx: MDContext(taskId: item.taskId))
Text(status)
.font(Fonts.font(11, weight: open ? 700 : 400))
.foregroundStyle(tone)
.fixedSize(horizontal: false, vertical: true)
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
.background(open ? Palette.warnTint : Palette.surface, in: RoundedRectangle(cornerRadius: 10))
.overlay(RoundedRectangle(cornerRadius: 10).stroke(hover ? Palette.accent : open ? Palette.warn.opacity(0.55) : Palette.border))
.contentShape(Rectangle())
.onTapGesture {
store.showQuestion(item.taskId, item.id)
}
.onHover { hover = $0 }
.help("Show this question in the Questions tab")
}
private var status: String {
let answer = item.responses.last
switch item.state {
case .open:
return "waiting for your answer"
case .dismissed:
return item.resolvedBy == nil ? "withdrawn by the agent" : "dismissed by \(item.resolvedBy!)"
default:
guard let answer else {
return item.state.rawValue
}
if let options = answer.options, !options.isEmpty {
return "\(answer.user) chose: \(options.joined(separator: "; "))"
}
return "\(answer.user) answered: \(firstLine(answer.text ?? ""))"
}
}
private func firstLine(_ s: String) -> String {
let line = trimmed(s).components(separatedBy: "\n").first ?? ""
return line.count > 120 ? "\(line.prefix(120))…" : line
}
}
private struct ToolResult: View {
let result: String?
let unfinished: Bool
@@ -1109,7 +1172,7 @@ struct NoteView: View {
@ViewState private var cancelling = false
var body: some View {
let color = note.style == .error ? Palette.danger : note.style == .ask ? Palette.accent : Palette.muted
let color = note.style == .error ? Palette.danger : Palette.muted
HStack(alignment: .firstTextBaseline, spacing: 12) {
Text(text())
.font(Fonts.font(11))
@@ -1216,7 +1279,9 @@ private struct SnapshotRow: View {
.buttonStyle(SmallLinkButtonStyle())
.disabled(forking)
.help("Start a new task from this snapshot, its agent continuing the conversation as it stood here")
reviewButton
if snap.trigger != .answer {
reviewButton
}
Rectangle().fill(Palette.border).frame(height: 1)
}
.font(Fonts.font(11))