From de9c9a684c02d8d26d4d48dd6f88f3adec941025 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Thu, 22 Oct 2026 20:49:37 -0700 Subject: [PATCH] Show running tool glow at full strength when a transcript opens --- mac/Sources/Byre/Chat.swift | 24 ++++++++++++++++++------ mac/Sources/Byre/TranscriptBuilder.swift | 11 +++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/mac/Sources/Byre/Chat.swift b/mac/Sources/Byre/Chat.swift index 635e279..b2b48c3 100644 --- a/mac/Sources/Byre/Chat.swift +++ b/mac/Sources/Byre/Chat.swift @@ -66,6 +66,8 @@ final class ChatModel { @ObservationIgnored private var activeObserver: NotificationCenter.ObservationToken? @ObservationIgnored private var keyObserver: NotificationCenter.ObservationToken? @ObservationIgnored private var started = false + @ObservationIgnored private var opened = 0 + @ObservationIgnored private var settled = false @ObservationIgnored private var cursors: [String: Int] = [:] init(_ kind: ChatKind, _ store: Store) { @@ -185,6 +187,10 @@ final class ChatModel { row.pending && (running || subagents && row.inActive || row.inReview) || row.holdsActive && (running || subagents) } + func fades(_ row: Row) -> Bool { + row.firstId > opened + } + var hasOlder: Bool { kind == .lead && store.leadHasOlder() } @@ -246,6 +252,10 @@ final class ChatModel { private func appended(_ all: [ByreEvent]) { _ = builder.append(all.filter(shows)) + if !settled { + opened = Row.newest + settled = feed?.ready == true + } jump() } @@ -1117,6 +1127,7 @@ struct DetailsBox: View { var boxed = true var nested = false var working = false + var fade = false var status: (text: String, color: Color)? = nil @ViewBuilder let summary: Summary @ViewBuilder let content: Content @@ -1153,8 +1164,9 @@ struct DetailsBox: View { .background(boxed ? (nested ? Palette.raised : Palette.surface) : .clear, in: RoundedRectangle(cornerRadius: 8)) .overlay { if boxed { - RoundedRectangle(cornerRadius: 8).stroke(working && shown ? accent : Palette.border) - .animation(.easeInOut(duration: 0.6), value: working && shown) + let lit = working && (shown || !fade) + RoundedRectangle(cornerRadius: 8).stroke(lit ? accent : Palette.border) + .animation(.easeInOut(duration: 0.6), value: lit) } } .onAppear { @@ -1276,7 +1288,7 @@ struct RowView: View { } 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) Text(styled([(name, 600, nil), (" ", 400, nil), (brief, 400, Palette.muted)])) } content: { @@ -1288,7 +1300,7 @@ struct RowView: View { } 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) Text(styled([(title, 600, nil), (" ", 400, nil), (description, 400, Palette.muted)])) } content: { @@ -1303,7 +1315,7 @@ struct RowView: View { } 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)])) .lineLimit(1) } content: { @@ -1317,7 +1329,7 @@ struct RowView: View { } case .group: - DetailsBox(nested: nested, working: model.working(row)) { + DetailsBox(nested: nested, working: model.working(row), fade: model.fades(row)) { SubagentTag(show: row.tag) RunSummary(calls: row.calls) } content: { diff --git a/mac/Sources/Byre/TranscriptBuilder.swift b/mac/Sources/Byre/TranscriptBuilder.swift index 73f4e67..4284c7f 100644 --- a/mac/Sources/Byre/TranscriptBuilder.swift +++ b/mac/Sources/Byre/TranscriptBuilder.swift @@ -145,6 +145,17 @@ final class Row: Identifiable { 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) { rowIds += 1 id = rowIds