Fix Ghostty theme loading in debug builds (#830)

* Revert "Fix Cmd+Tab activation ordering for cmux windows (#744) (#766)"

This reverts commit a6f6485e3c.

* Fix debug Ghostty theme loading fallback
This commit is contained in:
Austin Wang 2026-03-03 19:14:55 -08:00 committed by GitHub
parent 2f6cb6ff38
commit c7bdd92df9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 115 additions and 0 deletions

View file

@ -551,6 +551,7 @@ private final class GhosttySurfaceCallbackContext {
class GhosttyApp {
static let shared = GhosttyApp()
private static let releaseBundleIdentifier = "com.cmuxterm.app"
private static let backgroundLogTimestampFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
@ -912,6 +913,7 @@ class GhosttyApp {
private func loadDefaultConfigFilesWithLegacyFallback(_ config: ghostty_config_t) {
ghostty_config_load_default_files(config)
loadReleaseAppSupportGhosttyConfigIfNeeded(config)
loadLegacyGhosttyConfigIfNeeded(config)
ghostty_config_load_recursive_files(config)
ghostty_config_finalize(config)
@ -926,6 +928,22 @@ class GhosttyApp {
return true
}
static func shouldLoadReleaseAppSupportGhosttyConfig(
currentBundleIdentifier: String?,
currentConfigFileSize: Int?,
currentLegacyConfigFileSize: Int?,
releaseConfigFileSize: Int?,
releaseLegacyConfigFileSize: Int?
) -> Bool {
guard SocketControlSettings.isDebugLikeBundleIdentifier(currentBundleIdentifier) else { return false }
let hasCurrentAppSupportConfig = (currentConfigFileSize ?? 0) > 0 || (currentLegacyConfigFileSize ?? 0) > 0
guard !hasCurrentAppSupportConfig else { return false }
let hasReleaseAppSupportConfig = (releaseConfigFileSize ?? 0) > 0 || (releaseLegacyConfigFileSize ?? 0) > 0
return hasReleaseAppSupportConfig
}
static func shouldApplyDefaultBackgroundUpdate(
currentScope: GhosttyDefaultBackgroundUpdateScope,
incomingScope: GhosttyDefaultBackgroundUpdateScope
@ -963,6 +981,57 @@ class GhosttyApp {
return true
}
private func loadReleaseAppSupportGhosttyConfigIfNeeded(_ config: ghostty_config_t) {
#if os(macOS)
let fm = FileManager.default
guard let appSupport = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { return }
guard let currentBundleIdentifier = Bundle.main.bundleIdentifier,
!currentBundleIdentifier.isEmpty else { return }
let currentAppSupportDir = appSupport.appendingPathComponent(currentBundleIdentifier, isDirectory: true)
let releaseAppSupportDir = appSupport.appendingPathComponent(Self.releaseBundleIdentifier, isDirectory: true)
let currentConfig = currentAppSupportDir.appendingPathComponent("config.ghostty", isDirectory: false)
let currentLegacyConfig = currentAppSupportDir.appendingPathComponent("config", isDirectory: false)
let releaseConfig = releaseAppSupportDir.appendingPathComponent("config.ghostty", isDirectory: false)
let releaseLegacyConfig = releaseAppSupportDir.appendingPathComponent("config", isDirectory: false)
func fileSize(_ url: URL) -> Int? {
guard let attrs = try? fm.attributesOfItem(atPath: url.path),
let size = attrs[.size] as? NSNumber else { return nil }
return size.intValue
}
let releaseConfigSize = fileSize(releaseConfig)
let releaseLegacyConfigSize = fileSize(releaseLegacyConfig)
guard Self.shouldLoadReleaseAppSupportGhosttyConfig(
currentBundleIdentifier: currentBundleIdentifier,
currentConfigFileSize: fileSize(currentConfig),
currentLegacyConfigFileSize: fileSize(currentLegacyConfig),
releaseConfigFileSize: releaseConfigSize,
releaseLegacyConfigFileSize: releaseLegacyConfigSize
) else { return }
if let releaseLegacyConfigSize, releaseLegacyConfigSize > 0 {
releaseLegacyConfig.path.withCString { path in
ghostty_config_load_file(config, path)
}
}
if let releaseConfigSize, releaseConfigSize > 0 {
releaseConfig.path.withCString { path in
ghostty_config_load_file(config, path)
}
}
#if DEBUG
Self.initLog(
"loaded release app support ghostty config fallback from: \(releaseAppSupportDir.path)"
)
#endif
#endif
}
private func loadLegacyGhosttyConfigIfNeeded(_ config: ghostty_config_t) {
#if os(macOS)
// Ghostty 1.3+ prefers `config.ghostty`, but some users still have their real

View file

@ -258,6 +258,52 @@ final class GhosttyConfigTests: XCTestCase {
)
}
func testReleaseAppSupportFallbackLoadsForDebugWhenOnlyReleaseConfigExists() {
XCTAssertTrue(
GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig(
currentBundleIdentifier: "com.cmuxterm.app.debug",
currentConfigFileSize: nil,
currentLegacyConfigFileSize: nil,
releaseConfigFileSize: 128,
releaseLegacyConfigFileSize: nil
)
)
}
func testReleaseAppSupportFallbackSkipsWhenDebugConfigAlreadyExists() {
XCTAssertFalse(
GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig(
currentBundleIdentifier: "com.cmuxterm.app.debug.issue-829",
currentConfigFileSize: nil,
currentLegacyConfigFileSize: 64,
releaseConfigFileSize: 128,
releaseLegacyConfigFileSize: nil
)
)
}
func testReleaseAppSupportFallbackSkipsForNonDebugBundleOrMissingReleaseConfig() {
XCTAssertFalse(
GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig(
currentBundleIdentifier: "com.cmuxterm.app",
currentConfigFileSize: nil,
currentLegacyConfigFileSize: nil,
releaseConfigFileSize: 128,
releaseLegacyConfigFileSize: nil
)
)
XCTAssertFalse(
GhosttyApp.shouldLoadReleaseAppSupportGhosttyConfig(
currentBundleIdentifier: "com.cmuxterm.app.debug",
currentConfigFileSize: nil,
currentLegacyConfigFileSize: nil,
releaseConfigFileSize: nil,
releaseLegacyConfigFileSize: 0
)
)
}
func testDefaultBackgroundUpdateScopePrioritizesSurfaceOverAppAndUnscoped() {
XCTAssertTrue(
GhosttyApp.shouldApplyDefaultBackgroundUpdate(