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
14 changes: 14 additions & 0 deletions Resources/shell-integration/.zshenv
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ fi
# zsh treats unset ZDOTDIR as if it were HOME. We do the same.
builtin typeset _cmux_file="${ZDOTDIR-$HOME}/.zshenv"
[[ ! -r "$_cmux_file" ]] || builtin source -- "$_cmux_file"

if [[ -o interactive \
&& -z "${ZSH_EXECUTION_STRING:-}" \
&& "${CMUX_SHELL_INTEGRATION:-1}" != "0" \
&& -n "${CMUX_SHELL_INTEGRATION_DIR:-}" \
&& -r "${CMUX_SHELL_INTEGRATION_DIR}/cmux-zsh-integration.zsh" \
&& "${TERM:-}" == "xterm-256color" \
&& -z "${CMUX_ZSH_RESTORE_TERM:-}" ]]; then
# Keep startup TERM-compatible prompt/theme selection during shell init,
# then restore the managed xterm-256color identity before the first
# interactive command executes.
builtin export CMUX_ZSH_RESTORE_TERM="$TERM"
builtin export TERM="xterm-ghostty"
fi
} always {
if [[ -o interactive ]]; then
# We overwrote GhosttyKit's injected ZDOTDIR, so manually load Ghostty's
Expand Down
8 changes: 8 additions & 0 deletions Resources/shell-integration/cmux-zsh-integration.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ _cmux_command_starts_nested_shell() {
}

_cmux_preexec() {
_cmux_restore_terminal_identity_after_startup
_cmux_tmux_sync_cmux_environment

if [[ -z "$_CMUX_TTY_NAME" ]]; then
Expand Down Expand Up @@ -1214,6 +1215,13 @@ _cmux_fix_path() {
add-zsh-hook -d precmd _cmux_fix_path
}

_cmux_restore_terminal_identity_after_startup() {
if [[ -n "${CMUX_ZSH_RESTORE_TERM:-}" ]]; then
builtin export TERM="$CMUX_ZSH_RESTORE_TERM"
builtin unset CMUX_ZSH_RESTORE_TERM
fi
}

_cmux_zshexit() {
_cmux_stop_git_head_watch
_cmux_stop_pr_poll_loop
Expand Down
Binary file added Resources/terminfo-overlay/78/xterm-256color
Binary file not shown.
11 changes: 5 additions & 6 deletions Resources/terminfo-overlay/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# cmux terminfo overlay

cmux ships Ghostty's `xterm-ghostty` terminfo entry, but the embedded
renderer in cmux has differed from Ghostty's app renderer in how it treats
the "bright" SGR 90-97/100-107 sequences.
cmux ships patched `xterm-ghostty` and `xterm-256color` terminfo entries so
the embedded renderer keeps the same color behavior even when the reported
`TERM` changes.

This overlay patches the terminfo capabilities so that `tput setaf 8` (and
These overlays patch the terminfo capabilities so that `tput setaf 8` (and
similar "bright" colors) uses 256-color indexed sequences (`38;5;<n>m` /
`48;5;<n>m`) rather than SGR 90-97/100-107. This avoids relying on bright SGR
handling and fixes zsh-autosuggestions (default `fg=8`) visibility issues in
cmux.
cmux while preserving Ghostty's truecolor capabilities.

The build phase `Copy Ghostty Resources` overlays this directory onto the app
bundle's `Contents/Resources/terminfo` after copying Ghostty's resources.

32 changes: 25 additions & 7 deletions Sources/GhosttyTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3133,15 +3133,14 @@ final class TerminalSurface: Identifiable, ObservableObject {
private var backgroundSurfaceStartQueued = false
private var surfaceCallbackContext: Unmanaged<GhosttySurfaceCallbackContext>?
/// The desired focus state for the Ghostty C surface. May be set before the
/// C surface exists (e.g. during layout restoration); `createSurface` seeds
/// the initial runtime focus state from this value, then keeps using it as a
/// dedup guard to avoid redundant `ghostty_surface_set_focus` calls
/// C surface exists (e.g. during layout restoration); `createSurface`
/// reapplies this value once the runtime surface exists, then keeps using it
/// as a dedup guard to avoid redundant `ghostty_surface_set_focus` calls
/// (prevents prompt redraws with P10k).
///
/// Start unfocused and only opt into focus when the workspace/AppKit focus
/// path explicitly requests it. `createSurface` passes this through as the
/// runtime surface's initial focus state so background panes never need a
/// synthetic focus-loss transition during creation.
/// path explicitly requests it so background panes do not keep a focused
/// state unless the workspace focus path requests it.
private var desiredFocusState: Bool = false
#if DEBUG
private var needsConfirmCloseOverrideForTesting: Bool?
Expand Down Expand Up @@ -3255,6 +3254,22 @@ final class TerminalSurface: Identifiable, ObservableObject {
return merged
}

static let managedTerminalType = "xterm-256color"
static let managedTerminalProgram = "ghostty"
static let managedColorTerm = "truecolor"

static func applyManagedTerminalIdentityEnvironment(
to environment: inout [String: String],
protectedKeys: inout Set<String>
) {
environment["TERM"] = managedTerminalType
protectedKeys.insert("TERM")
environment["COLORTERM"] = managedColorTerm
protectedKeys.insert("COLORTERM")
environment["TERM_PROGRAM"] = managedTerminalProgram
protectedKeys.insert("TERM_PROGRAM")
}

static func mergedStartupEnvironment(
base: [String: String],
protectedKeys: Set<String>,
Expand Down Expand Up @@ -3769,7 +3784,6 @@ final class TerminalSurface: Identifiable, ObservableObject {
surfaceCallbackContext?.release()
surfaceCallbackContext = callbackContext
surfaceConfig.scale_factor = scaleFactors.layer
surfaceConfig.focused = desiredFocusState
surfaceConfig.context = surfaceContext
#if DEBUG
let templateFontText = String(format: "%.2f", surfaceConfig.font_size)
Expand All @@ -3790,6 +3804,10 @@ final class TerminalSurface: Identifiable, ObservableObject {
var env = baseConfig.environmentVariables

var protectedStartupEnvironmentKeys: Set<String> = []
Self.applyManagedTerminalIdentityEnvironment(
to: &env,
protectedKeys: &protectedStartupEnvironmentKeys
)
func setManagedEnvironmentValue(_ key: String, _ value: String) {
env[key] = value
protectedStartupEnvironmentKeys.insert(key)
Expand Down
8 changes: 6 additions & 2 deletions Sources/cmuxApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,15 @@ struct cmuxApp: App {
}

if getenv("TERM") == nil {
setenv("TERM", "xterm-ghostty", 1)
setenv("TERM", TerminalSurface.managedTerminalType, 1)
}

if getenv("COLORTERM") == nil {
setenv("COLORTERM", TerminalSurface.managedColorTerm, 1)
}

if getenv("TERM_PROGRAM") == nil {
setenv("TERM_PROGRAM", "ghostty", 1)
setenv("TERM_PROGRAM", TerminalSurface.managedTerminalProgram, 1)
}

if let resourcesDir = getenv("GHOSTTY_RESOURCES_DIR").flatMap({ String(cString: $0) }) {
Expand Down
Loading
Loading