Skip to content

Auto-hide terminal scroll bar fixes 2674#2678

Merged
austinywang merged 1 commit intomainfrom
issue-2674-scrollbar-occlusion
Apr 8, 2026
Merged

Auto-hide terminal scroll bar fixes 2674#2678
austinywang merged 1 commit intomainfrom
issue-2674-scrollbar-occlusion

Conversation

@austinywang
Copy link
Copy Markdown
Contributor

@austinywang austinywang commented Apr 7, 2026

Closes #2674.

What changed

  • read Ghostty's embedded scrollbar config in the cmux host wrapper so scrollbar = never is respected instead of always showing a vertical scroller
  • restore Ghostty's macOS overlay-scrollbar behavior when AppKit reports preferred scroller style changes, so the terminal grid width returns to the full overlay width instead of staying under a persistent legacy gutter
  • flash the overlay scroller on hover when the system preference is legacy, matching Ghostty's usability fallback for mouse users
  • refresh scrollbar appearance before terminal geometry is reconciled after Ghostty config reloads
  • update the existing scrollbar-width regression to assert that preferred style changes restore overlay width rather than pinning the terminal under a visible gutter

Why

cmux embeds Ghostty but its forked GhosttySurfaceScrollView had drifted from Ghostty's macOS scrollbar behavior. That let a persistent right-side scrollbar gutter overlap the terminal's last column. This brings cmux back to Ghostty-style overlay behavior so the scrollbar only surfaces as an overlay instead of permanently occluding content.

Notes

  • no local tests were run per repo policy
  • verified the change builds successfully with the required tagged reload flow

Note

Medium Risk
Touches terminal scroll view sizing and event handling, which can regress layout (grid width) or scrolling behavior across macOS scroller-style modes. Changes are localized to UI but affect a core interaction path.

Overview
Restores Ghostty-like macOS scrollbar behavior in the embedded terminal: reads Ghostty’s scrollbar config (honoring scrollbar = never), forces overlay scrollers (no reserved gutter), and re-syncs scroller appearance when system preferred scroller style changes.

Adds hover flashing for overlay scrollers when the system preference is legacy via a scroller tracking area, and refreshes scrollbar appearance before geometry reconciliation on config reload. Updates the existing regression test to assert overlay-width restoration after preferred scroller style changes.

Reviewed by Cursor Bugbot for commit c4a08fb. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Fixes terminal scrollbar occlusion by restoring macOS overlay scroller behavior in the embedded Ghostty view and honoring the scrollbar config (e.g., hides when scrollbar = never). This removes the permanent right gutter and keeps the last column visible.

  • Bug Fixes
    • Read scrollbar from Ghostty config and hide the vertical scroller when set to never.
    • Always use overlay scrollers and refresh on AppKit preferred-style changes to avoid reserving a gutter; retile only the scroll view.
    • Flash the overlay scroller on mouse hover in legacy mode; add a tracking area to the vertical scroller.
    • Sync scroller appearance during init and before geometry reconciliation on config reload.
    • Update tests to assert overlay width is restored and rename the scroller-style test accordingly.

Written for commit c4a08fb. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Introduced scrollbar visibility configuration option enabling system-default or always-hidden modes
    • Enhanced scrollbar behavior with improved hover detection and responsive visibility feedback during interaction

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Apr 7, 2026 9:37am

@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai Bot commented Apr 7, 2026

This review could not be run because your cubic account has exceeded the monthly review limit. If you need help restoring access, please contact contact@cubic.dev.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0adb5b34-d779-4202-8639-6d6c8882b4c2

📥 Commits

Reviewing files that changed from the base of the PR and between e88ff43 and c4a08fb.

📒 Files selected for processing (3)
  • Sources/AppDelegate.swift
  • Sources/GhosttyTerminalView.swift
  • cmuxTests/TerminalAndGhosttyTests.swift

📝 Walkthrough

Walkthrough

The changes implement overlay scrollbar functionality with dynamic visibility control. AppDelegate reorders geometry reconciliation timing, GhosttyTerminalView adds scrollbar configuration reading and mouse-tracking behavior, and tests update assertions to reflect the new scroller style handling.

Changes

Cohort / File(s) Summary
AppDelegate Geometry Reconciliation
Sources/AppDelegate.swift
Reordered reconcileGeometryNow() call to execute after refreshHostBackgroundAfterGhosttyConfigReload() instead of before, affecting the sequence of geometry updates during config reload.
Scrollbar Visibility and Tracking
Sources/GhosttyTerminalView.swift
Added ScrollbarVisibility enum with system and never cases; implemented scrollbarVisibility() config reader; added scrollbarTrackingArea property and synchronizeScrollbarAppearance() method to manage scrollbar state; overrode updateTrackingAreas() and mouseMoved(with:) to enable mouse-based scrollbar tracking and flash behavior on hover.
Scroller Style Test Updates
cmuxTests/TerminalAndGhosttyTests.swift
Refactored GhosttySurfaceOverlayTests assertions to verify overlay scroller style application and content-width restoration after preferredScrollerStyleDidChangeNotification rather than asserting intermediate state changes.

Sequence Diagram

sequenceDiagram
    participant GhosttyApp as GhosttyApp
    participant Config as Ghostty Config
    participant ScrollView as GhosttySurfaceScrollView
    participant Scroller as NSScroller
    participant Mouse as Mouse Events
    
    GhosttyApp->>Config: Read 'scrollbar' setting
    Config-->>GhosttyApp: Return config value
    GhosttyApp->>ScrollView: scrollbarVisibility()
    ScrollView->>ScrollView: synchronizeScrollbarAppearance()
    ScrollView->>Scroller: Apply visibility setting<br/>(hasVerticalScroller)
    ScrollView->>ScrollView: updateTrackingAreas()
    ScrollView->>Scroller: Install tracking area<br/>over scroller bounds
    Mouse->>ScrollView: mouseMoved(with:)
    ScrollView->>Scroller: flashScrollers() if legacy style
    Scroller-->>Mouse: Display scrollbar
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A scrollbar that hides with grace,
Appears when paws draw near the place,
No occlusion blocks our view today,
Content flows freely, hip-hop-hooray! 🎉
With overlay's dance and tracking's care,
The terminal's joy floats light as air.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main changes: fixing auto-hide terminal scrollbar behavior to address issue #2674.
Description check ✅ Passed The PR description covers all required template sections: What changed, Why, Testing notes, Review trigger block, and a comprehensive explanation of the fix. All checklist items are addressed.
Linked Issues check ✅ Passed The PR fully addresses issue #2674 by reading Ghostty's scrollbar config, restoring overlay behavior, and preventing scrollbar occlusion of terminal content.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing scrollbar occlusion: scrollbar config reading, overlay behavior restoration, hover flashing, and test updates for issue #2674.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-2674-scrollbar-occlusion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 7, 2026

Greptile Summary

This PR fixes GhosttySurfaceScrollView to respect Ghostty's scrollbar = never config and restore overlay-scrollbar behavior on system-preference changes, eliminating the persistent right-side gutter that was occluding the terminal's last column. It also adds hover-flashing for users whose system preference is legacy and ensures scrollbar state is refreshed before terminal geometry is reconciled after config reloads.

Confidence Score: 5/5

Safe to merge; all changes are well-scoped scrollbar layout fixes with no data-path side effects.

The implementation correctly forces overlay scrollerStyle before reading contentSize in both the system-pref-change and config-reload paths. The new test verifies the key invariant. No P0 or P1 issues found — remaining observations are all P2 style notes.

No files require special attention.

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift Adds scrollbar config reading, forced overlay-style synchronization, preferredScrollerStyle change observer, and hover-flash tracking area for legacy-pref users
Sources/AppDelegate.swift Calls synchronizeScrollbarAppearance() before geometry reconcile in the config-reload refresh path so scrollbar=never is applied before terminal width is recomputed
cmuxTests/TerminalAndGhosttyTests.swift Adds testPreferredScrollerStyleChangeRestoresOverlayScrollbarWidth: verifies system-pref change notification restores overlay mode and full-width terminal grid

Sequence Diagram

sequenceDiagram
    autonumber
    box System/AppKit
        participant System as macOS System
        participant NSSV as NSScrollView (internal)
    end
    box GhosttySurfaceScrollView
        participant GSSV as GhosttySurfaceScrollView
        participant SA as synchronizeScrollbarAppearance()
        participant CS as synchronizeCoreSurface()
    end
    box Config Reload
        participant AD as AppDelegate
    end
    rect rgb(230,240,255)
        Note over System,CS: System scroller style change
        System->>NSSV: preferredScrollerStyleDidChangeNotification
        NSSV-->>NSSV: may set scrollerStyle=.legacy
        NSSV->>GSSV: notification (queue:nil, main thread)
        GSSV->>GSSV: handlePreferredScrollerStyleChange()
        GSSV->>SA: synchronizeScrollbarAppearance()
        SA-->>NSSV: scrollerStyle=.overlay + updateTrackingAreas()
        GSSV->>NSSV: scrollView.tile()
        GSSV->>CS: synchronizeCoreSurface()
        CS-->>CS: reads full-width contentSize
        CS->>GSSV: pushTargetSurfaceSize(fullWidth, height)
    end
    rect rgb(230,255,235)
        Note over AD,CS: Config reload path
        AD->>GSSV: refreshHostBackgroundAfterGhosttyConfigReload()
        GSSV->>SA: synchronizeScrollbarAppearance()
        SA-->>NSSV: reads new scrollbarVisibility, forces overlay
        AD->>GSSV: reconcileGeometryNow()
        GSSV->>CS: synchronizeCoreSurface()
        CS->>GSSV: pushTargetSurfaceSize(correctWidth, height)
    end
Loading

Reviews (1): Last reviewed commit: "Fix terminal scrollbar occlusion" | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scroll bar occlusion

1 participant