Show running tool glow at full strength when a transcript opens

This commit is contained in:
Ian Gulliver
2026-10-22 20:49:37 -07:00
parent 101dace80e
commit de9c9a684c
2 changed files with 29 additions and 6 deletions
+18 -6
View File
@@ -66,6 +66,8 @@ final class ChatModel {
@ObservationIgnored private var activeObserver: NotificationCenter.ObservationToken? @ObservationIgnored private var activeObserver: NotificationCenter.ObservationToken?
@ObservationIgnored private var keyObserver: NotificationCenter.ObservationToken? @ObservationIgnored private var keyObserver: NotificationCenter.ObservationToken?
@ObservationIgnored private var started = false @ObservationIgnored private var started = false
@ObservationIgnored private var opened = 0
@ObservationIgnored private var settled = false
@ObservationIgnored private var cursors: [String: Int] = [:] @ObservationIgnored private var cursors: [String: Int] = [:]
init(_ kind: ChatKind, _ store: Store) { init(_ kind: ChatKind, _ store: Store) {
@@ -185,6 +187,10 @@ final class ChatModel {
row.pending && (running || subagents && row.inActive || row.inReview) || row.holdsActive && (running || subagents) row.pending && (running || subagents && row.inActive || row.inReview) || row.holdsActive && (running || subagents)
} }
func fades(_ row: Row) -> Bool {
row.firstId > opened
}
var hasOlder: Bool { var hasOlder: Bool {
kind == .lead && store.leadHasOlder() kind == .lead && store.leadHasOlder()
} }
@@ -246,6 +252,10 @@ final class ChatModel {
private func appended(_ all: [ByreEvent]) { private func appended(_ all: [ByreEvent]) {
_ = builder.append(all.filter(shows)) _ = builder.append(all.filter(shows))
if !settled {
opened = Row.newest
settled = feed?.ready == true
}
jump() jump()
} }
@@ -1117,6 +1127,7 @@ struct DetailsBox<Summary: View, Content: View>: View {
var boxed = true var boxed = true
var nested = false var nested = false
var working = false var working = false
var fade = false
var status: (text: String, color: Color)? = nil var status: (text: String, color: Color)? = nil
@ViewBuilder let summary: Summary @ViewBuilder let summary: Summary
@ViewBuilder let content: Content @ViewBuilder let content: Content
@@ -1153,8 +1164,9 @@ struct DetailsBox<Summary: View, Content: View>: View {
.background(boxed ? (nested ? Palette.raised : Palette.surface) : .clear, in: RoundedRectangle(cornerRadius: 8)) .background(boxed ? (nested ? Palette.raised : Palette.surface) : .clear, in: RoundedRectangle(cornerRadius: 8))
.overlay { .overlay {
if boxed { if boxed {
RoundedRectangle(cornerRadius: 8).stroke(working && shown ? accent : Palette.border) let lit = working && (shown || !fade)
.animation(.easeInOut(duration: 0.6), value: working && shown) RoundedRectangle(cornerRadius: 8).stroke(lit ? accent : Palette.border)
.animation(.easeInOut(duration: 0.6), value: lit)
} }
} }
.onAppear { .onAppear {
@@ -1276,7 +1288,7 @@ struct RowView: View {
} }
case .tool(let name, let brief, let input): case .tool(let name, let brief, let input):
DetailsBox(nested: nested, working: model.working(row)) { DetailsBox(nested: nested, working: model.working(row), fade: model.fades(row)) {
SubagentTag(show: row.tag) SubagentTag(show: row.tag)
Text(styled([(name, 600, nil), (" ", 400, nil), (brief, 400, Palette.muted)])) Text(styled([(name, 600, nil), (" ", 400, nil), (brief, 400, Palette.muted)]))
} content: { } content: {
@@ -1288,7 +1300,7 @@ struct RowView: View {
} }
case .subagent(let title, let description, let prompt): case .subagent(let title, let description, let prompt):
DetailsBox(nested: nested, working: model.working(row), status: (row.status, row.statusState == .done ? Palette.muted : row.statusState == .error ? Palette.danger : Palette.accent)) { DetailsBox(nested: nested, working: model.working(row), fade: model.fades(row), status: (row.status, row.statusState == .done ? Palette.muted : row.statusState == .error ? Palette.danger : Palette.accent)) {
SubagentTag(show: row.tag) SubagentTag(show: row.tag)
Text(styled([(title, 600, nil), (" ", 400, nil), (description, 400, Palette.muted)])) Text(styled([(title, 600, nil), (" ", 400, nil), (description, 400, Palette.muted)]))
} content: { } content: {
@@ -1303,7 +1315,7 @@ struct RowView: View {
} }
case .reviewer(let agentModel): case .reviewer(let agentModel):
DetailsBox(nested: nested, working: row.active, status: (row.status, row.statusState == .done ? Palette.muted : row.statusState == .error ? Palette.danger : Palette.accent)) { DetailsBox(nested: nested, working: row.active, fade: model.fades(row), status: (row.status, row.statusState == .done ? Palette.muted : row.statusState == .error ? Palette.danger : Palette.accent)) {
Text(styled([(row.review?.stage ?? "review", 600, nil), (agentModel.isEmpty ? "" : " · \(agentModel)", 400, Palette.muted), (row.text.isEmpty ? "" : " \(row.text)", 400, Palette.muted)])) Text(styled([(row.review?.stage ?? "review", 600, nil), (agentModel.isEmpty ? "" : " · \(agentModel)", 400, Palette.muted), (row.text.isEmpty ? "" : " \(row.text)", 400, Palette.muted)]))
.lineLimit(1) .lineLimit(1)
} content: { } content: {
@@ -1317,7 +1329,7 @@ struct RowView: View {
} }
case .group: case .group:
DetailsBox(nested: nested, working: model.working(row)) { DetailsBox(nested: nested, working: model.working(row), fade: model.fades(row)) {
SubagentTag(show: row.tag) SubagentTag(show: row.tag)
RunSummary(calls: row.calls) RunSummary(calls: row.calls)
} content: { } content: {
+11
View File
@@ -145,6 +145,17 @@ final class Row: Identifiable {
return false return false
} }
static var newest: Int {
rowIds
}
var firstId: Int {
if case .group = kind {
return body!.rows.first?.id ?? id
}
return id
}
init(_ kind: RowKind, taskId: String?, body: Bool = false) { init(_ kind: RowKind, taskId: String?, body: Bool = false) {
rowIds += 1 rowIds += 1
id = rowIds id = rowIds