nvrsion: Add Stop Button Implementation, Fix Session View Animation Ordering, Match Stop Button Color to Composer: Update the stop button to light gray and resolve stuttering during iOS session view animations.
Nucleic-Promote: 1 Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -369,7 +369,7 @@ struct SessionDetailView: View {
|
|||||||
Button { store.interrupt(sessionID) } label: {
|
Button { store.interrupt(sessionID) } label: {
|
||||||
Image(systemName: "stop.circle.fill")
|
Image(systemName: "stop.circle.fill")
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.foregroundStyle(.red)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
.disabled(!store.connectivity.isLive)
|
.disabled(!store.connectivity.isLive)
|
||||||
.accessibilityLabel("Stop")
|
.accessibilityLabel("Stop")
|
||||||
@@ -494,8 +494,40 @@ struct TranscriptList: View {
|
|||||||
/// settling — a `.task(id:)` debounces it to detect when the opening layout has come to rest.
|
/// settling — a `.task(id:)` debounces it to detect when the opening layout has come to rest.
|
||||||
@State private var transcriptContentHeight: CGFloat = 0
|
@State private var transcriptContentHeight: CGFloat = 0
|
||||||
|
|
||||||
|
/// Memoized transcript projection. `TranscriptProjection.build` folds the entire raw event
|
||||||
|
/// stream into rows on every read, but `body` re-evaluates far more often than `events`
|
||||||
|
/// actually changes: while the chat is opening, `onScrollGeometryChange` bumps
|
||||||
|
/// `transcriptContentHeight` on every settling-layout frame, and each bump re-runs `body` —
|
||||||
|
/// so the untouched event stream was re-folded dozens of times during the exact window the
|
||||||
|
/// tab bar is sliding away, starving that animation of the main thread and making the
|
||||||
|
/// load-in jitter. Reuse the last fold whenever the stream is unchanged. A class held in
|
||||||
|
/// `@State` so reading/updating it from `body` doesn't itself invalidate the view (SwiftUI
|
||||||
|
/// stores the reference and never diffs its interior).
|
||||||
|
private final class ProjectionCache {
|
||||||
|
/// The event stream is append-only with strictly increasing `seq`, so `(count, lastSeq)`
|
||||||
|
/// uniquely pins it; `firstSeq` guards the open reset (stream cleared to `[]`, then
|
||||||
|
/// refilled). The two display toggles are inputs to the fold, so they key it too.
|
||||||
|
private struct Key: Equatable {
|
||||||
|
var count: Int; var firstSeq: UInt64; var lastSeq: UInt64
|
||||||
|
var showRaw: Bool; var showLockEvents: Bool
|
||||||
|
}
|
||||||
|
private var key: Key?
|
||||||
|
private var value: [TranscriptItem] = []
|
||||||
|
|
||||||
|
func items(for events: [AgentEvent], showRaw: Bool, showLockEvents: Bool) -> [TranscriptItem] {
|
||||||
|
let k = Key(count: events.count, firstSeq: events.first?.seq ?? 0,
|
||||||
|
lastSeq: events.last?.seq ?? 0, showRaw: showRaw, showLockEvents: showLockEvents)
|
||||||
|
if key == k { return value }
|
||||||
|
let built = TranscriptProjection.build(events, showRaw: showRaw, showLockEvents: showLockEvents)
|
||||||
|
key = k
|
||||||
|
value = built
|
||||||
|
return built
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@State private var projectionCache = ProjectionCache()
|
||||||
|
|
||||||
private var items: [TranscriptItem] {
|
private var items: [TranscriptItem] {
|
||||||
TranscriptProjection.build(events, showRaw: showRaw, showLockEvents: showLockEvents)
|
projectionCache.items(for: events, showRaw: showRaw, showLockEvents: showLockEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How much content may still sit below the viewport's bottom edge and still count as "at
|
/// How much content may still sit below the viewport's bottom edge and still count as "at
|
||||||
|
|||||||
Reference in New Issue
Block a user