Make merge dialog Enter follow the task's offered merge action

This commit is contained in:
Ian Gulliver
2026-10-21 11:59:52 -07:00
parent abe4d7c9d5
commit c7d0a1722a
7 changed files with 240 additions and 198 deletions
+6 -7
View File
@@ -295,7 +295,7 @@ final class ChatModel {
if let r = row.review, r.snapshotN == latest, findingOpen(r, i) {
open.append(row.id)
}
case .ready(_, _, let d, _) where row.id == builder.lastReady && active:
case .ready(_, _, let d) where row.id == builder.lastReady && active:
ready.append(row.id)
discard = d
default:
@@ -644,7 +644,7 @@ private struct MergeButton: View {
: "The agent merges \(target) in, then byre squashes into one commit on \(target) and pushes it to origin; held while another task in the project is merging (⌘S)"
Button(intoParent ? "Merging into parent" : held ? "Merge held" : task.mergeAfterReview ? "Merge after review" : keepOpen == false ? "Merge & complete" : "Merge") {
Task {
await confirmMerge(task.id, store, overlays)
await confirmMerge(task.id, store, overlays, keepOpen: keepOpen)
}
}
.buttonStyle(WebButtonStyle(primary: primary))
@@ -1264,8 +1264,8 @@ struct RowView: View {
case .findingResponse(let snapshot, let index, let response, let reason):
FindingResponseView(row: row, model: model, snapshot: snapshot, index: index, response: response, reason: reason)
case .ready(let when, let blocks, let discard, let keepOpen):
ReadyBubble(when: when, blocks: blocks, discard: discard, keepOpen: keepOpen, taskId: row.taskId, latest: row.id == model.builder.lastReady, unmerged: model.hasUnmerged)
case .ready(let when, let blocks, let discard):
ReadyBubble(when: when, blocks: blocks, discard: discard, taskId: row.taskId, latest: row.id == model.builder.lastReady, unmerged: model.hasUnmerged)
case .handoff:
DetailsBox(boxed: false) {
@@ -1385,7 +1385,6 @@ private struct ReadyBubble: View {
let when: Date
let blocks: [MDBlock]
let discard: Bool
let keepOpen: Bool
let taskId: String?
let latest: Bool
let unmerged: Bool
@@ -1412,8 +1411,8 @@ private struct ReadyBubble: View {
DiscardButton(task: task, busy: busy)
} trailing: {
if !discard {
MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: true, primary: keepOpen)
MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: false, primary: !keepOpen)
MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: true, primary: task.readyMerge == ._continue)
MergeButton(task: task, busy: busy, unmerged: unmerged, keepOpen: false, primary: task.readyMerge != ._continue)
}
}
.padding(.top, 4)
+2 -1
View File
@@ -50,10 +50,11 @@ final class MergeModel {
}
@MainActor
func confirmMerge(_ taskId: String, _ store: Store, _ overlays: Overlays, keepOpen: Bool = false) async {
func confirmMerge(_ taskId: String, _ store: Store, _ overlays: Overlays, keepOpen: Bool? = nil) async {
guard let task = store.findTask(taskId), task.status == .active else {
return
}
let keepOpen = keepOpen ?? (task.readyMerge == ._continue)
if store.taskBusy(taskId) {
overlays.showAlert("Agent is mid-turn; merge when it's idle")
return
+3 -3
View File
@@ -54,7 +54,7 @@ enum RowKind {
case review(note: Note, md: [MDBlock])
case finding(index: Int)
case findingResponse(snapshot: Int, index: Int, response: String, reason: String)
case ready(when: Date, md: [MDBlock], discard: Bool, keepOpen: Bool)
case ready(when: Date, md: [MDBlock], discard: Bool)
case handoff
case snapshot(Snapshot)
case question
@@ -972,10 +972,10 @@ final class TranscriptBuilder {
return note([.text("\(ev.actor) set the task summary")])
case "task.ready":
return Row(.ready(when: when, md: parseMarkdown(jsString(p["overview"])), discard: false, keepOpen: str(p, "merge") == "continue"), taskId: rowTask)
return Row(.ready(when: when, md: parseMarkdown(jsString(p["overview"])), discard: false), taskId: rowTask)
case "task.discard_suggested":
return Row(.ready(when: when, md: parseMarkdown(jsString(p["reason"])), discard: true, keepOpen: false), taskId: rowTask)
return Row(.ready(when: when, md: parseMarkdown(jsString(p["reason"])), discard: true), taskId: rowTask)
case "task.discard_started":
return note([.text("\(ev.actor) asked to discard the task; the agent writes the task summary first")])