nvrsion: Add: Move the chevron circle vertically above the composer bar and fix the jump distance so the chevron button is hidden when tapped.

Nucleic-Promote: 1
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-06 19:27:30 -07:00
co-authored by Nucleic
parent 33aedb4b97
commit 5021783f7a
@@ -306,9 +306,11 @@ struct SessionDetailView: View {
.overlay(alignment: .top) { .overlay(alignment: .top) {
if !isScrolledToBottom { if !isScrolledToBottom {
JumpToBottomButton { scrollToBottomRequest += 1 } JumpToBottomButton { scrollToBottomRequest += 1 }
// Hang the button *above* the bar: report its bottom edge (plus a gap) as its // Hang the button fully *above* the bar: top-aligned, then shifted up by its
// top guide, so the overlay's top alignment places it outside the bar's bounds. // own 30pt height plus an 8pt gap. A render-time offset (not an alignment
.alignmentGuide(.top) { $0[.bottom] + 8 } // guide, which misplaced it half-overlapping the bar's top edge) so the
// placement is exact and — critically — contributes nothing to the inset.
.offset(y: -38)
.transition(.move(edge: .bottom).combined(with: .opacity)) .transition(.move(edge: .bottom).combined(with: .opacity))
} }
} }
@@ -705,11 +707,18 @@ struct TranscriptList: View {
scrollToEnd(proxy, animated: !transcriptSettling) scrollToEnd(proxy, animated: !transcriptSettling)
} }
} }
// An explicit jump — the chevron or sending a message — always wins. The chevron // An explicit jump — the chevron or sending a message — always wins, and it *is* the
// itself lives in the parent's chat bar (above the composer), not as an overlay here, // user saying "follow again": re-engage the gate directly rather than waiting for the
// so it sits over the composer instead of behind the floating bar; a tap bumps this // geometry to confirm. The animated scroll can come to rest a few points short of the
// same request, and following resumes once the geometry reader sees the bottom. // follow threshold (content padding, inset rounding), which sat inside the hysteresis
.onChange(of: scrollToBottomRequest) { scrollToEnd(proxy) } // band — following never re-engaged and the chevron lingered until the user manually
// scrolled the last few points. Setting the gate here hides the chevron immediately
// and hands pinning back to the bottom anchor; a later real scroll-up still
// disengages it as usual.
.onChange(of: scrollToBottomRequest) {
isScrolledToBottom = true
scrollToEnd(proxy)
}
// Warm the Markdown parse caches off the main thread whenever the row set grows — // Warm the Markdown parse caches off the main thread whenever the row set grows —
// `initial: true` fires it for the batch that lands on open (the expensive case), and // `initial: true` fires it for the batch that lands on open (the expensive case), and
// each later new row tops it up. Keyed on the row *count*, so streaming deltas into an // each later new row tops it up. Keyed on the row *count*, so streaming deltas into an