Skip to content

[Feature]: Reusable auto-hiding scrollbar widget with macOS Aqua styling #209

@forketyfork

Description

@forketyfork

Description

Add a reusable scrollbar widget with classic macOS Tiger/Aqua aesthetics — rounded capsule thumb, translucent track groove, glossy feel — that auto-hides after idle. Use it in both the diff overlay and terminal views, replacing the current ad-hoc diff scrollbar and the binary yellow indicator bar for terminals. The scrollbar should be draggable.

User Flow

Trigger: User scrolls content (mouse wheel, trackpad, or keyboard in diff view)

  1. Scrollbar fades in on the right edge of the scrollable area, showing a proportionally-sized capsule thumb at the current position
  2. User can grab the thumb and drag to scrub through content; the viewport follows in real time
  3. After ~1.5s of no scroll activity, the scrollbar fades out smoothly
  4. Hovering over the scrollbar track area prevents fade-out and allows click-to-jump

Result: Uniform, polished scroll indication and interaction across all scrollable surfaces

Scope

In scope:

  • Reusable Scrollbar widget in src/ui/components/scrollbar.zig
  • Aqua-style visuals: rounded capsule thumb, subtle track groove, semi-transparent, DPI-aware
  • Auto-hide with fade-in/fade-out animation
  • Drag-to-scroll interaction on the thumb
  • Click-on-track to jump to position
  • Integration with diff overlay (replacing current renderScrollbar)
  • Integration with terminal views (using ghostty-vt's pages.scrollbar() API for position and pages.scroll(.{ .row = N }) for drag)
  • Remove the yellow 4px scrollback indicator bar from terminal grid view

Out of scope:

  • Horizontal scrollbars
  • Scroll-to-top/scroll-to-bottom buttons
  • Custom scrollbar theming via config.toml (uses accent color from theme)
  • Keyboard-driven scrollbar interaction (existing keyboard scrolling in diff view stays as-is)

Implementation Plan

Affected Modules

  • src/ui/components/scrollbar.zig (new): Reusable scrollbar widget
  • src/ui/components/diff_overlay.zig: Replace inline renderScrollbar with the new widget
  • src/ui/components/session_interaction.zig: Feed scrollbar state from ghostty-vt, handle drag actions
  • src/render/renderer.zig: Remove yellow scrollback indicator bar, render terminal scrollbars
  • src/ui/session_view_state.zig: Add scrollbar visibility/fade state per session

Tasks

  1. Create Scrollbar widget struct with state (scroll ratio, thumb rect, fade alpha, drag state) and rendering (Aqua capsule thumb, track groove, rounded rects) — src/ui/components/scrollbar.zig
  2. Implement auto-hide logic: fade-in on scroll activity, fade-out after ~1.5s idle, using easing functions from src/anim/easing.zigsrc/ui/components/scrollbar.zig
  3. Implement drag interaction: hit-test on thumb, track mouse delta during drag, emit a scroll-to-position callback/value — src/ui/components/scrollbar.zig
  4. Implement click-on-track: hit-test on track area, compute target scroll ratio from click position — src/ui/components/scrollbar.zig
  5. Integrate into diff overlay: replace renderScrollbar() with the new widget, feed it scroll_offset / max_scroll, handle drag output by updating scroll_offsetsrc/ui/components/diff_overlay.zig
  6. Integrate into terminal views: query pages.scrollbar() each frame for { total, offset, len }, feed to widget, handle drag via pages.scroll(.{ .row = N })src/ui/components/session_interaction.zig, src/render/renderer.zig
  7. Remove the yellow 4px scrollback indicator bar — src/render/renderer.zig
  8. Add scrollbar fade state to SessionViewState (or co-locate with the widget instance per session) — src/ui/session_view_state.zig
  9. Ensure FirstFrameGuard / wantsFrame() fires during fade animations so frames render even under idle throttling — src/ui/components/scrollbar.zig
  10. Update docs/ARCHITECTURE.md with the new scrollbar component
  11. Update README.md if scrollbar behavior is user-visible documentation-worthy

New Dependencies

None

Acceptance Criteria

  • Scrollbar renders with Aqua-style capsule thumb and translucent track in both diff overlay and terminal views
  • Scrollbar auto-hides after ~1.5s idle, fades in/out smoothly
  • Thumb is draggable; viewport follows drag position in real time
  • Click on track jumps viewport to that position
  • Scrollbar size is proportional to content-to-viewport ratio
  • DPI-aware rendering (respects host.scale)
  • No yellow indicator bar remains in terminal grid view
  • zig build, zig build test, and just lint pass
  • Docs updated

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions