Watch: snooze finished threads, name the files a file approval would edit, Settle on finished notifications, an instant launch from the saved feed, plain Loading states, and no more cancelled errors
Gateway / check (push) Successful in 9s
Gateway / image (push) Successful in 22s

Gateway: an approval push for a file change says which files it would edit
This commit is contained in:
gib committed 2026-11-02 14:45:34 -05:00
1 parent 4dc4e57dac
commit edc4b88990
35 files changed
+1162 -144

No files matched your search

+19 -18
View File
@@ -1,9 +1,10 @@
// One thread: the agent's latest message large, older messages one scroll up with Load earlier above
// them, the pending approval or question with its options, the thread's subagents under a header
// that collapses them, the files its latest run changed with their line counts, Reply by
// dictation or Scribble, Settle once it finished or failed, an Ask before acting switch, and Stop
// while a run is active. A subagent's request shows first, labeled with the subagent. Message text
// renders as markdown.
// dictation or Scribble, Settle and Snooze once it finished or failed, an Ask before acting switch,
// and Stop while a run is active. A subagent's request shows first, labeled with the subagent. A file
// approval names the files and shows the first changed lines when the gateway has them. Message
// text renders as markdown. The detail saved when the thread last loaded shows while it loads again.
import SwiftUI
@@ -63,16 +64,8 @@ struct ThreadView: View {
VStack(alignment: .leading, spacing: 8) {
if let detail {
content(detail)
} else if error == nil {
Text("Loading")
.font(.system(size: 15, weight: .semibold))
.foregroundStyle(Theme.secondaryText)
}
if let error {
Text(error)
.font(.system(size: 12))
.foregroundStyle(Theme.secondaryText)
}
LoadStatus(loaded: detail != nil, error: error)
}
.padding(.horizontal, 4)
}
@@ -95,7 +88,10 @@ struct ThreadView: View {
}
}
.navigationTitle(detail?.item.title ?? "Thread")
.onAppear { visible = true }
.onAppear {
if detail == nil { detail = model.cachedThread(hostId: hostId, threadId: threadId) }
visible = true
}
.onDisappear { visible = false }
.task(id: visible && scenePhase == .active) {
guard visible && scenePhase == .active else { return }
@@ -183,10 +179,12 @@ struct ThreadView: View {
.disabled(sending)
if settleable {
Button { settle() } label: {
Label("Settle", systemImage: "checkmark")
HStack(spacing: 6) {
Button("Settle") { settle() }
.buttonStyle(PillButtonStyle(fill: Theme.work))
Button("Snooze") { model.snoozing = item }
.buttonStyle(PillButtonStyle(fill: Theme.control, foreground: Theme.snooze))
}
.buttonStyle(PillButtonStyle(fill: Theme.work))
.disabled(sending)
}
@@ -272,7 +270,9 @@ struct ThreadView: View {
private func approval(_ request: PendingRequest, from via: FeedItem.Via?) -> some View {
Card(edge: Theme.you) {
subagentLabel(via)
if let prompt = request.prompt {
if let edit = request.fileEdit {
FileEditRows(edit: edit, inThread: true)
} else if let prompt = request.prompt {
Text(prompt)
.font(.system(size: 13, design: .monospaced))
.foregroundStyle(.white)
@@ -393,8 +393,9 @@ struct ThreadView: View {
if let detail { history.keep(detail.messages) }
detail = fresh
error = nil
model.saveThread(fresh)
} catch {
self.error = model.message(for: error)
if let message = model.message(for: error) { self.error = message }
}
}