Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ public struct GhosttyConfig {
/// terminal view/engine code.
public typealias ColorSchemePreference = TerminalColorSchemePreference

/// Catppuccin's light palette used for fresh installs before the user has
/// chosen terminal colors. This keeps the default terminal in sync with
/// Codex's default TUI theme.
public static let cmuxDefaultLightThemeName = "Catppuccin Latte"
/// Catppuccin's dark palette used for fresh installs before the user has
/// chosen terminal colors. This keeps the default terminal in sync with
/// Codex's default TUI theme.
public static let cmuxDefaultDarkThemeName = "Catppuccin Mocha"
/// Native fallback light theme name used for fresh installs before the user
/// has chosen terminal colors.
public static let cmuxDefaultLightThemeName = "Apple System Colors Light"
/// Native fallback dark theme name used for fresh installs before the user
/// has chosen terminal colors.
public static let cmuxDefaultDarkThemeName = "Apple System Colors"

private static let loadCacheLock = NSLock()
// Every read/write of this cache is serialized by `loadCacheLock`; the
Expand Down Expand Up @@ -61,12 +59,13 @@ public struct GhosttyConfig {
public var command: String?
/// The scrollback limit. Ghostty measures this in bytes, not lines.
public var scrollbackLimit: Int = 50_000_000
/// The opacity (0...1) applied to unfocused split panes; the cmux default keeps terminal content at full contrast.
public var unfocusedSplitOpacity: Double = 1.0
private var hasUnfocusedSplitOpacityDirective = false
/// The unfocused-split overlay fill, or `nil` to use the background color.
/// The opacity (0...1) applied to unfocused split panes.
public var unfocusedSplitOpacity: Double = 0.7
/// The fill color for the unfocused-split overlay, or `nil` to use the
/// background color.
public var unfocusedSplitFill: NSColor?
/// The split-divider color, or `nil` to derive one from the background.
/// The explicit split-divider color, or `nil` to derive one from the
/// background.
public var splitDividerColor: NSColor?

// Colors (from theme or config)
Expand Down Expand Up @@ -187,13 +186,15 @@ public struct GhosttyConfig {
/// config file, theme, or optional cmux managed appearance is parsed.
public init() {}

/// The overlay opacity (0...1) over unfocused splits, derived from ``unfocusedSplitOpacity``.
/// The opacity (0...1) of the overlay drawn over unfocused splits, derived
/// from ``unfocusedSplitOpacity``.
public var unfocusedSplitOverlayOpacity: Double {
let clamped = min(1.0, max(0.15, unfocusedSplitOpacity))
return min(1.0, max(0.0, 1.0 - clamped))
}

/// The overlay fill: ``unfocusedSplitFill`` when set, otherwise the background color.
/// The fill color of the unfocused-split overlay: the explicit
/// ``unfocusedSplitFill`` when set, otherwise the background color.
public var unfocusedSplitOverlayFill: NSColor {
unfocusedSplitFill ?? backgroundColor
}
Expand Down Expand Up @@ -488,59 +489,59 @@ public struct GhosttyConfig {
return nil
}

static func cmuxDefaultFallbackConfigContents(
private static func cmuxDefaultFallbackConfigContents(
preferredColorScheme: ColorSchemePreference
) -> String {
switch preferredColorScheme {
case .light:
return """
palette = 0=#5c5f77
palette = 1=#d20f39
palette = 2=#40a02b
palette = 3=#df8e1d
palette = 4=#1e66f5
palette = 5=#ea76cb
palette = 6=#179299
palette = 7=#acb0be
palette = 8=#6c6f85
palette = 9=#de293e
palette = 10=#49af3d
palette = 11=#eea02d
palette = 12=#456eff
palette = 13=#fe85d8
palette = 14=#2d9fa8
palette = 15=#bcc0cc
background = #eff1f5
foreground = #4c4f69
cursor-color = #dc8a78
cursor-text = #eff1f5
selection-background = #acb0be
selection-foreground = #4c4f69
palette = 0=#1a1a1a
palette = 1=#cc372e
palette = 2=#26a439
palette = 3=#cdac08
palette = 4=#0869cb
palette = 5=#9647bf
palette = 6=#479ec2
palette = 7=#98989d
palette = 8=#464646
palette = 9=#ff453a
palette = 10=#32d74b
palette = 11=#e5bc00
palette = 12=#0a84ff
palette = 13=#bf5af2
palette = 14=#69c9f2
palette = 15=#ffffff
background = #feffff
foreground = #000000
cursor-color = #98989d
cursor-text = #ffffff
selection-background = #abd8ff
selection-foreground = #000000
"""
case .dark:
return """
palette = 0=#45475a
palette = 1=#f38ba8
palette = 2=#a6e3a1
palette = 3=#f9e2af
palette = 4=#89b4fa
palette = 5=#f5c2e7
palette = 6=#94e2d5
palette = 7=#a6adc8
palette = 8=#585b70
palette = 9=#f37799
palette = 10=#89d88b
palette = 11=#ebd391
palette = 12=#74a8fc
palette = 13=#f2aede
palette = 14=#6bd7ca
palette = 15=#bac2de
background = #1e1e2e
foreground = #cdd6f4
cursor-color = #f5e0dc
cursor-text = #1e1e2e
selection-background = #585b70
selection-foreground = #cdd6f4
palette = 0=#1a1a1a
palette = 1=#cc372e
palette = 2=#26a439
palette = 3=#cdac08
palette = 4=#0869cb
palette = 5=#9647bf
palette = 6=#479ec2
palette = 7=#98989d
palette = 8=#464646
palette = 9=#ff453a
palette = 10=#32d74b
palette = 11=#ffd60a
palette = 12=#0a84ff
palette = 13=#bf5af2
palette = 14=#76d6ff
palette = 15=#ffffff
background = #1e1e1e
foreground = #ffffff
cursor-color = #98989d
cursor-text = #ffffff
selection-background = #3f638b
selection-foreground = #ffffff
"""
}
}
Expand Down Expand Up @@ -715,13 +716,10 @@ public struct GhosttyConfig {
case "unfocused-split-opacity":
if let opacity = Double(value) {
unfocusedSplitOpacity = opacity
hasUnfocusedSplitOpacityDirective = true
}
case "unfocused-split-fill":
if let color = NSColor(hex: value) {
unfocusedSplitFill = color
// A fill-only config opts into Ghostty's default dimming.
if !hasUnfocusedSplitOpacityDirective { unfocusedSplitOpacity = 0.7 }
}
case "split-divider-color":
if let color = NSColor(hex: value) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,10 @@ import Testing
/// Regression coverage for https://github.com/manaflow-ai/cmux/issues/7161
/// and https://github.com/manaflow-ai/cmux/issues/10199.
///
/// cmux's managed default terminal theme (Catppuccin Latte/Mocha) applies only
/// cmux's managed default terminal theme ("Apple System Colors") applies only
/// when enabled and the user has no effective Ghostty settings. Any configured
/// directive preserves Ghostty's own resolved base and user overrides.
@Suite(.serialized) struct GhosttyConfigManagedDefaultAppearanceTests {
@Test("managed defaults match the Codex dark and light palettes")
func managedDefaultsMatchCodexThemePair() {
#expect(GhosttyConfig.cmuxDefaultLightThemeName == "Catppuccin Latte")
#expect(GhosttyConfig.cmuxDefaultDarkThemeName == "Catppuccin Mocha")
}

@Test(arguments: [GhosttyConfig.ColorSchemePreference.light, .dark])
func managedFallbackPreservesCatppuccinColors(
colorScheme: GhosttyConfig.ColorSchemePreference
) {
// Exercise the missing-resource fallback directly so installed theme
// files cannot hide a stale built-in palette.
var config = GhosttyConfig()
config.parse(GhosttyConfig.cmuxDefaultFallbackConfigContents(
preferredColorScheme: colorScheme
))
let expected: [String]
switch colorScheme {
case .light:
expected = [
"#5C5F77", "#D20F39", "#40A02B", "#DF8E1D",
"#1E66F5", "#EA76CB", "#179299", "#ACB0BE",
"#6C6F85", "#DE293E", "#49AF3D", "#EEA02D",
"#456EFF", "#FE85D8", "#2D9FA8", "#BCC0CC",
"#EFF1F5", "#4C4F69", "#DC8A78", "#EFF1F5",
"#ACB0BE", "#4C4F69"
]
case .dark:
expected = [
"#45475A", "#F38BA8", "#A6E3A1", "#F9E2AF",
"#89B4FA", "#F5C2E7", "#94E2D5", "#A6ADC8",
"#585B70", "#F37799", "#89D88B", "#EBD391",
"#74A8FC", "#F2AEDE", "#6BD7CA", "#BAC2DE",
"#1E1E2E", "#CDD6F4", "#F5E0DC", "#1E1E2E",
"#585B70", "#CDD6F4"
]
}
let actual = (0..<16).map { config.palette[$0]?.hexString() } + [
config.backgroundColor.hexString(), config.foregroundColor.hexString(),
config.cursorColor.hexString(), config.cursorTextColor.hexString(),
config.selectionBackground.hexString(), config.selectionForeground.hexString()
]
#expect(actual == expected.map(Optional.some))
}

private func withTempConfigDir(
body: (_ dir: URL) throws -> Void
) throws {
Expand Down Expand Up @@ -216,7 +171,7 @@ import Testing

// MARK: Managed base + user override precedence

/// Writes sentinel managed theme files into a themes root the
/// Writes sentinel "Apple System Colors" theme files into a themes root the
/// managed-default resolution finds via `GHOSTTY_RESOURCES_DIR`, keeping the
/// resolved managed colors deterministic on machines with Ghostty installed.
private func makeManagedThemesRoot(in dir: URL) throws -> URL {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ import Testing
]

private static let managedLightPalette = [
"#5C5F77", "#D20F39", "#40A02B", "#DF8E1D",
"#1E66F5", "#EA76CB", "#179299", "#ACB0BE",
"#6C6F85", "#DE293E", "#49AF3D", "#EEA02D",
"#456EFF", "#FE85D8", "#2D9FA8", "#BCC0CC",
"#1A1A1A", "#CC372E", "#26A439", "#CDAC08",
"#0869CB", "#9647BF", "#479EC2", "#98989D",
"#464646", "#FF453A", "#32D74B", "#E5BC00",
"#0A84FF", "#BF5AF2", "#69C9F2", "#FFFFFF",
]

private static let managedDarkPalette = [
"#45475A", "#F38BA8", "#A6E3A1", "#F9E2AF",
"#89B4FA", "#F5C2E7", "#94E2D5", "#A6ADC8",
"#585B70", "#F37799", "#89D88B", "#EBD391",
"#74A8FC", "#F2AEDE", "#6BD7CA", "#BAC2DE",
"#1A1A1A", "#CC372E", "#26A439", "#CDAC08",
"#0869CB", "#9647BF", "#479EC2", "#98989D",
"#464646", "#FF453A", "#32D74B", "#FFD60A",
"#0A84FF", "#BF5AF2", "#76D6FF", "#FFFFFF",
]

private static let fullExplicitPalette = [
Expand Down Expand Up @@ -119,21 +119,21 @@ import Testing
return ScenarioFixture(
configContents: "# no Ghostty settings\n",
light: snapshot(
foreground: "#4C4F69",
background: "#EFF1F5",
cursor: "#DC8A78",
cursorText: "#EFF1F5",
selectionBackground: "#ACB0BE",
selectionForeground: "#4C4F69",
foreground: "#000000",
background: "#FEFFFF",
cursor: "#98989D",
cursorText: "#FFFFFF",
selectionBackground: "#ABD8FF",
selectionForeground: "#000000",
palette: Self.managedLightPalette
),
dark: snapshot(
foreground: "#CDD6F4",
background: "#1E1E2E",
cursor: "#F5E0DC",
cursorText: "#1E1E2E",
selectionBackground: "#585B70",
selectionForeground: "#CDD6F4",
foreground: "#FFFFFF",
background: "#1E1E1E",
cursor: "#98989D",
cursorText: "#FFFFFF",
selectionBackground: "#3F638B",
selectionForeground: "#FFFFFF",
palette: Self.managedDarkPalette
),
changesWithAppearance: true
Expand Down
5 changes: 0 additions & 5 deletions Sources/GhosttyScrollView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ final class GhosttyScrollView: NSScrollView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)

// NSScrollView is a layer-backed viewport in the terminal hierarchy.
// Keep its view boundary clipped even when AppKit retile/reparent work
// changes the backing layer during a live window resize.
clipsToBounds = true

// Bonsplit lays out the tab strip outside this viewport, so AppKit must not
// infer another terminal-content inset from the window title bar.
automaticallyAdjustsContentInsets = false
Expand Down
Loading
Loading