@@ -6052,6 +6052,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
60526052 }
60536053
60546054 override func scrollWheel( with event: NSEvent ) {
6055+ NotificationCenter . default. post ( name: . ghosttyDidReceiveWheelScroll, object: self )
60556056 guard let surface = surface else { return }
60566057 lastScrollEventTime = CACurrentMediaTime ( )
60576058 Self . focusLog ( " scrollWheel: surface= \( terminalSurface? . id. uuidString ?? " nil " ) firstResponder= \( String ( describing: window? . firstResponder) ) " )
@@ -6453,6 +6454,7 @@ enum GhosttyNotificationKey {
64536454extension Notification . Name {
64546455 static let ghosttyDidUpdateScrollbar = Notification . Name ( " ghosttyDidUpdateScrollbar " )
64556456 static let ghosttyDidUpdateCellSize = Notification . Name ( " ghosttyDidUpdateCellSize " )
6457+ static let ghosttyDidReceiveWheelScroll = Notification . Name ( " ghosttyDidReceiveWheelScroll " )
64566458 static let ghosttySearchFocus = Notification . Name ( " ghosttySearchFocus " )
64576459 static let ghosttyConfigDidReload = Notification . Name ( " ghosttyConfigDidReload " )
64586460 static let ghosttyDefaultBackgroundDidChange = Notification . Name ( " ghosttyDefaultBackgroundDidChange " )
@@ -6586,6 +6588,8 @@ final class GhosttySurfaceScrollView: NSView {
65866588 /// When true, auto-scroll should be suspended to prevent the "doomscroll" bug
65876589 /// where the terminal fights the user's scroll position.
65886590 private var userScrolledAwayFromBottom = false
6591+ private var pendingExplicitWheelScroll = false
6592+ private var allowExplicitScrollbarSync = false
65896593 /// Threshold in points from bottom to consider "at bottom" (allows for minor float drift)
65906594 private static let scrollToBottomThreshold : CGFloat = 5.0
65916595 private var isActive = true
@@ -7011,6 +7015,14 @@ final class GhosttySurfaceScrollView: NSView {
70117015 self ? . handleScrollbarUpdate ( notification)
70127016 } )
70137017
7018+ observers. append ( NotificationCenter . default. addObserver (
7019+ forName: . ghosttyDidReceiveWheelScroll,
7020+ object: surfaceView,
7021+ queue: . main
7022+ ) { [ weak self] _ in
7023+ self ? . pendingExplicitWheelScroll = true
7024+ } )
7025+
70147026 observers. append ( NotificationCenter . default. addObserver (
70157027 forName: . ghosttySearchFocus,
70167028 object: nil ,
@@ -9013,26 +9025,21 @@ final class GhosttySurfaceScrollView: NSView {
90139025 userScrolledAwayFromBottom = false
90149026 }
90159027
9016- // Only auto-scroll if user hasn't manually scrolled away from bottom
9017- // or if we're following terminal output (scrollbar shows we're at bottom)
9018- let shouldAutoScroll = !userScrolledAwayFromBottom ||
9019- ( scrollbar . offset + scrollbar . len >= scrollbar . total )
9028+ // Passive bottom packets should not override an explicit scrollback review,
9029+ // but the first scrollbar packet caused by the user's own wheel input should
9030+ // still move the viewport to the requested scrollback position.
9031+ let shouldAutoScroll = !userScrolledAwayFromBottom || allowExplicitScrollbarSync
90209032
90219033 if shouldAutoScroll && !pointApproximatelyEqual( currentOrigin, targetOrigin) {
9022- #if DEBUG
9023- logDragGeometryChange (
9024- event: " scrollOrigin " ,
9025- old: currentOrigin,
9026- new: targetOrigin
9027- )
9028- #endif
90299034 scrollView. contentView. scroll ( to: targetOrigin)
90309035 didChangeGeometry = true
90319036 }
90329037 lastSentRow = Int ( scrollbar. offset)
90339038 }
90349039 }
90359040
9041+ allowExplicitScrollbarSync = false
9042+
90369043 if didChangeGeometry {
90379044 scrollView. reflectScrolledClipView ( scrollView. contentView)
90389045 }
@@ -9068,6 +9075,11 @@ final class GhosttySurfaceScrollView: NSView {
90689075 guard let scrollbar = notification. userInfo ? [ GhosttyNotificationKey . scrollbar] as? GhosttyScrollbar else {
90699076 return
90709077 }
9078+ if pendingExplicitWheelScroll {
9079+ userScrolledAwayFromBottom = scrollbar. offset + scrollbar. len < scrollbar. total
9080+ allowExplicitScrollbarSync = true
9081+ pendingExplicitWheelScroll = false
9082+ }
90719083 surfaceView. scrollbar = scrollbar
90729084 synchronizeScrollView ( )
90739085 }
0 commit comments