Carry the source transcript into forks and cross-link fork points

This commit is contained in:
Ian Gulliver
2026-10-20 09:14:44 -07:00
parent 29c993cde0
commit 09d6c6e023
10 changed files with 208 additions and 123 deletions
+39 -26
View File
@@ -268,7 +268,7 @@ final class ChatModel {
store.jump = nil
scrollTo(row.id)
case .review(let n):
guard let row = reviewRow(n) else {
guard let row = reviewRow(n, taskId) else {
return
}
store.jump = nil
@@ -276,8 +276,8 @@ final class ChatModel {
}
}
private func reviewRow(_ n: Int) -> Row? {
let rows = builder.root.rows
private func reviewRow(_ n: Int, _ task: String?) -> Row? {
let rows = builder.root.rows.filter { $0.taskId == task }
return rows.first { $0.reviewOf == n } ?? rows.first { r in
if case .snapshot(let s) = r.kind {
return s.n == n
@@ -286,8 +286,8 @@ final class ChatModel {
}
}
func showReview(_ n: Int) {
if let row = reviewRow(n) {
func showReview(_ n: Int, _ task: String?) {
if let row = reviewRow(n, task) {
scrollTo(row.id)
}
}
@@ -317,7 +317,7 @@ final class ChatModel {
var discard = false
for row in builder.root.rows where !row.hidden {
switch row.kind {
case .question where row.question?.state == .open:
case .question where row.question?.state == .open && row.taskId == taskId:
questions.append(row.id)
case .finding(let i):
if let r = row.review, r.id == latest?.id, findingOpen(r, i) {
@@ -1300,13 +1300,13 @@ struct RowView: View {
}
case .snapshot(let snap):
SnapshotRow(snap: snap, review: row.review, taskId: row.taskId) {
model.showReview(snap.n)
SnapshotRow(snap: snap, review: row.review, taskId: row.taskId, inherited: row.taskId != model.taskId, forks: row.forks) {
model.showReview(snap.n, row.taskId)
}
case .question:
if let item = row.question {
QuestionBubble(item: item)
QuestionBubble(item: item, inherited: row.taskId != model.taskId)
}
}
}
@@ -1314,9 +1314,10 @@ struct RowView: View {
private struct QuestionBubble: View {
let item: InboxItem
let inherited: Bool
var body: some View {
let open = item.state == .open
let open = item.state == .open && !inherited
Group {
if open {
QuestionCard(item: item)
@@ -1357,7 +1358,7 @@ private struct QuestionBubble: View {
let answer = item.responses.last
switch item.state {
case .open:
return "waiting for your answer"
return inherited ? "unanswered when this task was forked" : "waiting for your answer"
case .dismissed:
return item.resolvedBy == nil ? "withdrawn by the agent" : "dismissed by \(item.resolvedBy!)"
default:
@@ -1594,16 +1595,7 @@ struct NoteView: View {
case .text(let s):
out += AttributedString(s)
case .task(let id):
guard let task = store.findTask(id) else {
out += AttributedString(id)
continue
}
var a = AttributedString(task.name)
if task.status == .active {
a.link = AppLink.task(task.id)
a.foregroundColor = taskPalette[task.color]
}
out += a
out += taskLink(store, id)
case .url(let port):
let url = previewURL(store.api.origin, Int(port) ?? 0)
var a = AttributedString(url.absoluteString)
@@ -1616,6 +1608,19 @@ struct NoteView: View {
}
}
@MainActor
func taskLink(_ store: Store, _ id: String) -> AttributedString {
guard let task = store.findTask(id) else {
return AttributedString(id)
}
var a = AttributedString(task.name)
if task.status == .active {
a.link = AppLink.task(task.id)
a.foregroundColor = taskPalette[task.color]
}
return a
}
func previewURL(_ origin: URL, _ port: Int) -> URL {
var c = URLComponents()
c.scheme = "https"
@@ -1629,6 +1634,8 @@ private struct SnapshotRow: View {
let snap: Snapshot
let review: Review?
let taskId: String?
let inherited: Bool
let forks: [String]
let showReview: () -> Void
@Environment(Store.self) private var store
@Environment(Overlays.self) private var overlays
@@ -1640,12 +1647,14 @@ private struct SnapshotRow: View {
HStack(spacing: 8) {
Rectangle().fill(Palette.border).frame(height: 1)
Text("snapshot #\(snap.n)")
Button("changes") {
store.pinSnapshot(snap.n)
store.focusTab(.changes)
if !inherited {
Button("changes") {
store.pinSnapshot(snap.n)
store.focusTab(.changes)
}
.buttonStyle(SmallLinkButtonStyle())
.help("Show snapshot \(snap.n) in the Changes tab")
}
.buttonStyle(SmallLinkButtonStyle())
.help("Show snapshot \(snap.n) in the Changes tab")
Button("fork") {
guard let taskId else {
return
@@ -1667,6 +1676,10 @@ private struct SnapshotRow: View {
if ![.answer, .fork, .askHuman].contains(snap.trigger) {
reviewButton
}
ForEach(forks, id: \.self) { id in
Text(AttributedString("forked as ") + taskLink(store, id))
.fixedSize()
}
Rectangle().fill(Palette.border).frame(height: 1)
}
.font(Fonts.font(11))