Fix stale terminal foreground after theme switch - #3852
Conversation
The appearance-change path currently reaches each terminal surface after the app Ghostty config reload, but the per-surface sequence still only refreshes the host background and redraws. This regression pins the required ordering before the production fix: soft surface config update, host background refresh, then visual refresh. Constraint: Issue 3851 requires a two-commit regression-test-then-fix structure Confidence: high Scope-risk: narrow Directive: Keep app-level Ghostty config reload and per-surface redraw as one ordered sequence Tested: git diff --check Not-tested: Local XCTest/build per user instruction; test is intentionally red until the follow-up fix commit
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR introduces a GhosttySurfaceConfigurationRefresh helper to orchestrate per-surface Ghostty configuration reload, background refresh, and visual refresh after app-level config changes. It integrates this into AppDelegate.refreshTerminalSurfacesAfterGhosttyConfigReload so terminal surfaces receive updated palette colors during appearance switches. ChangesSurface Configuration Refresh Orchestration
Sequence Diagram(s)sequenceDiagram
participant AppDelegate
participant GhosttySurfaceConfigurationRefresh
participant GhosttyApp
participant HostedView
participant TerminalSurface
AppDelegate->>GhosttySurfaceConfigurationRefresh: applyAfterAppConfigReload(liveSurface?, source, reloadCB, refreshHostBG, forceRefreshCB)
alt surface exists
GhosttySurfaceConfigurationRefresh->>GhosttyApp: reloadSurfaceConfiguration(surface, true, source)
end
GhosttySurfaceConfigurationRefresh->>HostedView: refreshHostBackground()
GhosttySurfaceConfigurationRefresh->>TerminalSurface: forceRefresh(forceRefreshReason)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 14 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (14 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryFixes stale terminal foreground colors after theme switches by soft-reloading each live terminal surface's configuration before the existing hosted-background refresh and force-redraw on app-level Ghostty config reload.
Confidence Score: 5/5Safe to merge; the change is a focused, well-tested fix with no regressions introduced. The fix is narrow and correct: the new liveSurfaceForGhosttyAccess guard prevents calling into a freed Ghostty surface pointer, the three-step sequence is enforced in the right order, and both the happy path and the nil-surface path are covered by regression tests. forceRefresh already guards internally against a nil surface. No new timing dependencies, flags, or singletons are introduced. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "Keep terminal palettes fresh after appea..." | Re-trigger Greptile |
| @@ -0,0 +1,18 @@ | |||
| @MainActor | |||
| enum GhosttySurfaceConfigurationRefresh { | |||
| static let forceRefreshReason = "appDelegate.refreshAfterGhosttyConfigReload" | |||
There was a problem hiding this comment.
forceRefreshReason is a pure immutable string constant, but because the enclosing enum is @MainActor, this static let is implicitly main-actor-isolated. Any call site outside a @MainActor context (e.g., a nonisolated logging helper or a background diagnostic) would need to await MainActor.run { ... } just to read a plain string. The actor isolation rule flags value-only utilities that should be nonisolated to avoid unnecessary main-actor coupling.
| static let forceRefreshReason = "appDelegate.refreshAfterGhosttyConfigReload" | |
| nonisolated static let forceRefreshReason = "appDelegate.refreshAfterGhosttyConfigReload" |
Rule Used: Flag new or materially worsened Swift 6 actor isol... (source)
There was a problem hiding this comment.
Fixed by marking forceRefreshReason nonisolated so reading the plain string does not require MainActor isolation.
— Claude Code
The app-level Ghostty config reload now pushes the already-loaded app config into each live terminal surface with a soft surface reload before repainting. That matches the direct GHOSTTY_ACTION_RELOAD_CONFIG ordering and prevents dark-theme foreground palettes from being reused after a dark-to-light appearance switch. Constraint: Use soft reload so surfaces reuse the app config already loaded by the appearance path Rejected: Force redraw only | redraw uses stale per-surface palette state Confidence: high Scope-risk: narrow Directive: Do not separate per-surface config update from the subsequent redraw on app-level config reloads Tested: git diff --check Not-tested: Local XCTest/build per user instruction; CI will run the regression
11d694f to
ecaf2e1
Compare
Summary
Testing
Note
Medium Risk
Touches terminal surface refresh behavior during Ghostty config reload; incorrect ordering or nil-surface handling could cause missed updates or visual regressions across all open terminals.
Overview
Fixes stale terminal colors after theme/config reload by pushing the reloaded Ghostty config into each live terminal surface before repainting.
Introduces
GhosttySurfaceConfigurationRefresh.applyAfterAppConfigReloadto enforce the sequence soft per-surface config reload → hosted background refresh → forced surface redraw, and updatesAppDelegate.refreshTerminalSurfacesAfterGhosttyConfigReloadto use a live-surface guard.Adds unit tests verifying the call order and the behavior when the surface is unavailable.
Reviewed by Cursor Bugbot for commit ecaf2e1. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Fixes #3851 by soft-reloading each live terminal surface with the app’s reloaded Ghostty config before repaint to prevent stale foreground colors after theme switches. The surface update, host background refresh, and redraw now run in one ordered pass.
liveSurfaceForGhosttyAccess.Written for commit ecaf2e1. Summary will update on new commits.
Summary by CodeRabbit
Bug Fixes
Tests