Transcript Incremental Projection
Nucleic-Session: 0FFD007B-0696-4517-9429-129C7B0FD5AC Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -284,15 +284,6 @@ struct SessionDetailView: View {
|
|||||||
/// pending. Content scrolls beneath it; nothing renders when there's nothing to act on.
|
/// pending. Content scrolls beneath it; nothing renders when there's nothing to act on.
|
||||||
private var actionArea: some View {
|
private var actionArea: some View {
|
||||||
VStack(spacing: 8) {
|
VStack(spacing: 8) {
|
||||||
// The jump-to-bottom chevron rides here, immediately above the chat bar — not as a
|
|
||||||
// transcript overlay, which aligned to the scroll view's full-height bounds and so sat
|
|
||||||
// behind this floating bar at the screen's bottom edge. Shown only while scrolled up; a
|
|
||||||
// tap bumps the same scroll request the send button uses, so following resumes once the
|
|
||||||
// transcript reaches the bottom. Mirrors the Mac's `JumpToBottomButton`.
|
|
||||||
if !isScrolledToBottom {
|
|
||||||
JumpToBottomButton { scrollToBottomRequest += 1 }
|
|
||||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
||||||
}
|
|
||||||
// Interacting with a chat while the owning Mac is unreachable surfaces this first, so a
|
// Interacting with a chat while the owning Mac is unreachable surfaces this first, so a
|
||||||
// disabled composer reads as "offline / read-only history" rather than broken.
|
// disabled composer reads as "offline / read-only history" rather than broken.
|
||||||
if !store.connectivity.isLive { disconnectedBanner }
|
if !store.connectivity.isLive { disconnectedBanner }
|
||||||
@@ -300,6 +291,27 @@ struct SessionDetailView: View {
|
|||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
|
// The jump-to-bottom chevron floats just above the chat bar as an *overlay* — deliberately
|
||||||
|
// not a stack member. This whole action area is the transcript's bottom `safeAreaInset`,
|
||||||
|
// so a stack-member chevron changed the scroll view's bottom inset by ~38pt every time it
|
||||||
|
// appeared — and its visibility is *decided by* that same scroll geometry
|
||||||
|
// (`isScrolledToBottom`). That geometry→chevron→geometry cycle could oscillate every
|
||||||
|
// frame near the follow threshold, re-layouting the whole eager transcript each time and
|
||||||
|
// pinning the main thread at 100% with the UI locked. An overlay contributes nothing to
|
||||||
|
// the inset height, so showing or hiding it can't move the scroll geometry at all.
|
||||||
|
// (It's not a transcript overlay either — that aligned to the scroll view's full-height
|
||||||
|
// bounds and sat behind this floating bar at the screen's bottom edge.) A tap bumps the
|
||||||
|
// same scroll request the send button uses, so following resumes once the transcript
|
||||||
|
// reaches the bottom. Mirrors the Mac's `JumpToBottomButton`.
|
||||||
|
.overlay(alignment: .top) {
|
||||||
|
if !isScrolledToBottom {
|
||||||
|
JumpToBottomButton { scrollToBottomRequest += 1 }
|
||||||
|
// Hang the button *above* the bar: report its bottom edge (plus a gap) as its
|
||||||
|
// top guide, so the overlay's top alignment places it outside the bar's bounds.
|
||||||
|
.alignmentGuide(.top) { $0[.bottom] + 8 }
|
||||||
|
.transition(.move(edge: .bottom).combined(with: .opacity))
|
||||||
|
}
|
||||||
|
}
|
||||||
.animation(.easeInOut(duration: 0.15), value: isScrolledToBottom)
|
.animation(.easeInOut(duration: 0.15), value: isScrolledToBottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user