fix: escape hex colors so they survive tmux next-3.7 format expansion by hoblin · Pull Request #597 · catppuccin/tmux · GitHub
Skip to content

fix: escape hex colors so they survive tmux next-3.7 format expansion - #597

Open
hoblin wants to merge 4 commits into
catppuccin:mainfrom
hoblin:fix/render-safe-colors
Open

hoblin wants to merge 4 commits into
catppuccin:mainfrom
hoblin:fix/render-safe-colors

Conversation

@hoblin

@hoblin hoblin commented Jun 1, 2026

Copy link
Copy Markdown

Closes #596.

Problem

tmux next-3.7 (via the run-shell argument feature, tmux/tmux#5121, merged 2026-06-01) added #1–#9 as positional-argument shorthand in format_expand1. Expanding a format at render time now eats the first digit of any literal #RRGGBB whose first hex digit is 1–9 (#15232d → 5232d), producing invalid colors.

Every flavor uses such colors (mocha #1e1e2e / #11111b, surfaces, …), so status modules, the window list, and pane borders all render broken under next-3.7. Released tmux ≤ 3.6 is unaffected.

Fix

Two changes, one render-safety rule:

  1. Resolve theme colors with #{@thm_*} instead of #{E:@thm_*} where the value is a literal hex. The extra #{E:} pass re-scans the resolved hex and eats the leading digit at build time; a single resolution doesn't. (@thm_* always holds a literal hex across every flavor, never a reference.)

  2. Escape #RRGGBB → ##RRGGBB (in catppuccin.tmux) for the formats tmux expands at render time — window-status-format, window-status-current-format, pane-border-format, and the @catppuccin_status_* module strings. The render unescapes them back to intact #RRGGBB. Deferred references and runtime conditionals carry no literal and are untouched. The pass is idempotent (unescape-then-escape), so it survives reloads where -o-set options aren't rebuilt.

Tests

  • The cpu / load / ram module tests now load through the catppuccin.tmux entrypoint, so the escaping pass runs and their #{E:…} assertions exercise the render-safe round-trip. Suite passes on tmux 3.4 and next-3.7.
  • A new non-blocking CI job (continue-on-error) builds tmux master and runs the suite, surfacing upstream breakage early without gating PRs for released tmux.

Manually verified on next-3.7 with a flavor using #1/#3/#8/#9-leading colors: status modules (incl. session's runtime client_prefix conditional), window list, and pane borders all render correct colors.

🤖 Generated with Claude Code

hoblin and others added 3 commits June 1, 2026 19:50
…expansion

tmux's format_expand1 (next-3.7+, via the run-shell argument feature in
tmux/tmux#5121) treats a leading #1-catppuccin#9 as positional-argument shorthand. When a
format is expanded at render time this eats the first digit of any literal
#RRGGBB whose first hex digit is 1-9 (e.g. #15232d -> 5232d), producing invalid
colors. Every flavor uses such colors (mocha #1e1e2e, #11111b, ...), so status
modules, the window list and pane borders render broken.

Two changes keep colors safe:

- Resolve theme colors with #{@thm_*} rather than #{E:@thm_*} where the value is
  a literal hex. The extra #{E:} pass re-scans the resolved hex and eats the
  leading digit at build time; a single resolution does not.

- After building, escape every #RRGGBB to ##RRGGBB in the formats tmux expands
  at render time (window-status-format, window-status-current-format,
  pane-border-format and the @catppuccin_status_* module strings); the render
  unescapes them back. Deferred references and runtime conditionals carry no
  literal and are untouched. The pass is idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Load the cpu/load/ram module tests through the catppuccin.tmux entrypoint
instead of sourcing the conf directly, so the #RRGGBB -> ##RRGGBB escaping
runs and the E: assertions hold on tmux next-3.7.

Add a non-blocking CI job that builds tmux master and runs the suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rogeruiz

Copy link
Copy Markdown
Member

@rogeruiz rogeruiz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ This PR needs changes ...

Just a slight change related to descriptive language in our CI. otherwise this looks to be in a good spot to integrate into main after those changes are made. Great work and thanks for future-proofing the project.

Comment thread .github/workflows/test.yml Outdated
Comment thread catppuccin.tmux
Comment on lines +9 to +20
# Render-safe colors.
#
# tmux's format_expand1 (next-3.7+) treats a leading #1-#9 as positional-argument
# shorthand, so expanding a format at render time eats the first digit of any
# literal #RRGGBB hex (e.g. #15232d -> 5232d). Every option below is a format that
# tmux expands at render time (per window/pane, or via the user's #{E:...} status
# line), so a literal hex baked into one breaks.
#
# Escape every #RRGGBB to ##RRGGBB; the render then unescapes it back to an intact
# #RRGGBB. Deferred references such as #{@thm_*} carry no literal and are left
# untouched (they resolve normally). The pass is idempotent (unescape, then
# escape), so it stays correct if an option is not rebuilt on reload.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this descriptive comment block!

Co-authored-by: Roger Steve Ruiz <hi@rog.gr>
@hoblin

hoblin commented Jun 22, 2026

Copy link
Copy Markdown
Author

Done.

@hoblin

hoblin commented Aug 8, 2026

Copy link
Copy Markdown
Author

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.

Theme colors break on tmux next-3.7: leading hex digit eaten by format expansion

2 participants