Skip to content

Dim inactive split panes#260

Merged
sbertix merged 1 commit intomainfrom
sbertix/250-surface-prominence
Apr 20, 2026
Merged

Dim inactive split panes#260
sbertix merged 1 commit intomainfrom
sbertix/250-surface-prominence

Conversation

@sbertix
Copy link
Copy Markdown
Collaborator

@sbertix sbertix commented Apr 20, 2026

Summary

  • Dim non-focused split panes so it's obvious which pane has keyboard focus without hunting for the cursor.
  • Respect Ghostty's unfocused-split-opacity and unfocused-split-fill config values — Supacode bypasses Ghostty's SurfaceWrapper, so the overlay is applied here using the same semantics as upstream.
  • Consolidate active-pane state behind a single source of truth (focusedSurfaceIdByTab) with one choke point for mutation (recordActiveSurface), so mouse clicks, goto_split, split zoom, and palette actions can't drift apart.

Closes #250
Closes #243

Test plan

  • Open a worktree with a single pane → no dim overlay.
  • Split the pane → non-active pane dims; clicking it swaps the active pane instantly.
  • Trigger goto_split via keybinding → active pane flips and the overlay follows.
  • Resign the window (switch apps) → active pane is remembered; overlay doesn't flicker.
  • Set unfocused-split-opacity = 0.5 in ~/.config/ghostty/config, reload → overlay darkens to match.
  • Set unfocused-split-opacity = 1 → overlay disappears entirely.
  • Set unfocused-split-fill = #ff0000 → overlay tints red on the inactive pane.
  • Run SplitTreeTests.recordActiveSurfaceSymmetryAcrossClickAndGotoSplitPaths (once the DependenciesTestSupport test-target issue is fixed separately).

Surface which pane has focus by dimming inactive splits with Ghostty's
own unfocused-split overlay semantics.

- WorktreeTerminalState exposes activeSurfaceID(for:) backed by the
  single focusedSurfaceIdByTab source of truth. Click-path and explicit
  focus paths (goto_split, split zoom, palette) both funnel through the
  new recordActiveSurface choke point so two panes can't both claim
  active and focus-loss can't wipe the remembered active pane.
- GhosttyRuntime reads unfocused-split-opacity and unfocused-split-fill
  via ghostty_config_get, clamping the overlay opacity to [0, 1] and
  returning nil (with a SupaLogger warning) when both the fill and
  background keys are missing so the UI can skip the overlay rather
  than silently paint it black.
- WorktreeTerminalTabsView threads the overlay config down to LeafView
  and bumps a SwiftUI invalidation counter on
  .ghosttyRuntimeConfigDidChange so live config reloads re-read the
  values. Split divider uses the system separator color.
- SplitTreeTests locks in recordActiveSurface symmetry across the
  click and goto_split paths, dedup via emitFocusChangedIfNeeded,
  idempotence on the explicit path, and focus-loss as a no-op.
@sbertix sbertix enabled auto-merge (squash) April 20, 2026 20:22
@sbertix sbertix merged commit 4d19b06 into main Apr 20, 2026
2 checks passed
@sbertix sbertix deleted the sbertix/250-surface-prominence branch April 20, 2026 20:27
@tuist
Copy link
Copy Markdown

tuist Bot commented Apr 20, 2026

🛠️ Tuist Run Report 🛠️

Builds 🔨

Scheme Status Duration Commit
supacode 1m 37s acbef7bf8

jzillmann referenced this pull request in jzillmann/supacool Apr 23, 2026
Surface which pane has focus by dimming inactive splits with Ghostty's
own unfocused-split overlay semantics.

- WorktreeTerminalState exposes activeSurfaceID(for:) backed by the
  single focusedSurfaceIdByTab source of truth. Click-path and explicit
  focus paths (goto_split, split zoom, palette) both funnel through the
  new recordActiveSurface choke point so two panes can't both claim
  active and focus-loss can't wipe the remembered active pane.
- GhosttyRuntime reads unfocused-split-opacity and unfocused-split-fill
  via ghostty_config_get, clamping the overlay opacity to [0, 1] and
  returning nil (with a SupaLogger warning) when both the fill and
  background keys are missing so the UI can skip the overlay rather
  than silently paint it black.
- WorktreeTerminalTabsView threads the overlay config down to LeafView
  and bumps a SwiftUI invalidation counter on
  .ghosttyRuntimeConfigDidChange so live config reloads re-read the
  values. Split divider uses the system separator color.
- SplitTreeTests locks in recordActiveSurface symmetry across the
  click and goto_split paths, dedup via emitFocusChangedIfNeeded,
  idempotence on the explicit path, and focus-loss as a no-op.
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.

Feature Request: Emphasis active pane (Or deemphasis inactive pane) unfocused-split-opacity and unfocused-split-fill config settings not respected

1 participant