Add tmux 3.7 feature parity: floating panes, options, formats#694
Merged
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #694 +/- ##
==========================================
+ Coverage 51.38% 51.89% +0.50%
==========================================
Files 25 25
Lines 3493 3623 +130
Branches 688 733 +45
==========================================
+ Hits 1795 1880 +85
- Misses 1403 1439 +36
- Partials 295 304 +9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
5b1bf74 to
47f5b7b
Compare
why: Expose tmux 3.7's new options as typed dataclass fields so users get autocomplete and type-checking, matching the existing typed options surface. what: - ServerOptions: get_clipboard - SessionOptions: focus_follows_mouse, message_format, prompt_command_cursor_style - WindowOptions: copy_mode_line_numbers (+ _style, _current_line_number_style), tree_mode_preview_format/_style, window_pane_status_format, window_pane_current_status_format - PaneOptions: remain_on_exit gains the "key" value
why: Verify libtmux can set and show tmux 3.7's new options end to end. what: - Add OptionTestCase entries (min_version="3.7") feeding the existing parametrized set/show cycle, so they auto-skip on tmux < 3.7 - Cover get-clipboard, focus-follows-mouse, message-format, prompt-command-cursor-style, copy-mode line-number options, tree-mode preview options, window-pane status formats, and the new remain-on-exit "key" value
why: Record the new typed-options surface for the forthcoming release. what: - Add ### What's new entry under the unreleased section
why: Expose tmux 3.7's new pane format variables (floating-pane geometry, OSC 9;4 progress, screen-mode flags) as typed Obj fields so they hydrate on Pane objects. what: - Declare 11 new fields on neo.Obj (pane_floating_flag, pane_x/y/z, pane_flags, pane_zoomed_flag, pane_pb_progress, pane_pb_state, pane_pipe_pid, bracket_paste_flag, synchronized_output_flag) - Gate each in FIELD_VERSION="3.7" so older tmux keeps a clean -F - Route the two non-pane_-prefixed tokens via _SCOPE_OVERRIDES - List them in formats.py PANE_FORMATS reference
why: Ensure the new 3.7 pane format tokens are version-gated and the non-prefixed ones classify to pane scope. what: - Parametrized test that each 3.7 token is absent from a 3.6 -F template and present on 3.7 (FIELD_VERSION="3.7") - Test that bracket_paste_flag and synchronized_output_flag resolve to pane scope and appear in list-panes
why: Record the new pane format-variable surface for the forthcoming release. what: - Add ### What's new deliverable under the unreleased section
why: Expose new tmux 3.7 flags on existing wrappers so users can reach them through the typed API. what: - Pane.capture_pane: hyperlinks (-H), line_numbers (-L), line_flags (-F) - Pane.split / Window.split: empty (-E), an empty pane - Pane.paste_buffer: no_vis (-S), paste raw bytes (no vis(3) escaping) - Session.kill: group (-g), kill the whole session group - Each gated on tmux 3.7+, warning and ignoring on older tmux
why: Verify the new flags work on tmux 3.7 and warn-and-ignore below. what: - capture_pane -H/-L/-F, split empty (-E), paste_buffer no_vis (-S) - kill group (-g) - Each test is version-aware: functional path on 3.7+, UserWarning assertion on older tmux, so both CI legs are covered
why: Record the new command-flag surface for the forthcoming release. what: - Add ### What's new deliverable under the unreleased section
why: Expose tmux 3.7's flagship feature, floating panes, through the typed API. A floating pane overlays the tiled layout like a popup but behaves like a real pane. what: - Pane.new_pane(): issue `new-pane` with geometry (width/height -> -x/-y, x/y -> -X/-Y), shell/start_directory/environment, zoom, empty, and style/active_border_style/inactive_border_style; returns the floating Pane (pane_floating_flag == "1") - Window.new_pane(): delegate to the active pane, mirroring Window.split -> Pane.split - Raise LibTmuxException on tmux < 3.7 (new-pane is unavailable)
why: Verify new_pane creates a floating pane on tmux 3.7 and raises on older tmux. what: - Pane.new_pane / Window.new_pane create a floating pane (geometry honored, pane_floating_flag == "1") on tmux 3.7+ - Assert LibTmuxException on tmux < 3.7, so both CI legs are covered
why: Record the flagship floating-pane API for the forthcoming release. what: - Add ### What's new deliverable under the unreleased section
why: Give users a task-oriented walkthrough of tmux 3.7 floating panes alongside the API reference. what: - Add docs/topics/floating_panes.md (create, size/position, style, and identify floating panes; version-gated doctests) - Link it from the topics index grid and toctree - Render Window.new_pane()/Pane.new_pane() with distinct link text
why: The tmux 3.7 feature PR now exists, and a dedicated guide ships with it; the changelog should reference both. what: - Add the PR reference to each tmux 3.7 What's new deliverable - Cross-link the floating-panes topic guide from its entry - Render Window.new_pane()/Pane.new_pane() with distinct link text
why: new_pane() raised a bare LibTmuxException, so new-pane failures lacked the .subcommand tag and "new-pane:" message prefix that the sibling break_pane and the other migrated raise sites provide for downstream consumers. what: - Replace the bare raise in Pane.new_pane with raise_if_stderr(pane_cmd, "new-pane") - Add test_new_pane_error_tags_subcommand asserting .subcommand and the tagged str() on a provoked new-pane stderr (gated to tmux 3.7+)
why: Window.new_pane only cross-referenced Pane.new_pane, while sibling wrappers such as Window.split restate every parameter in full (the house NumPy-docstring pattern). what: - Add a Parameters section to Window.new_pane with a concise one-line entry per parameter, keeping the delegation note and doctest
why: Window.split's empty docstring stopped at "Requires tmux 3.7+", omitting the graceful-degradation contract the delegate Pane.split documents. what: - Append the warn-and-ignore-on-tmux<3.7 clause to Window.split's empty parameter, mirroring Pane.split
why: tmux 3.7 capture-pane -H returns hyperlink targets only, not normal pane text with OSC 8 escape sequences preserved. what: - Describe hyperlinks=True as hyperlink-target capture - Point users to escape_sequences=True for preserved escapes - Align the changelog command-flag summary
why: tmux 3.7 accepts several preview, copy-mode, and pane-status options at pane scope, but PaneOptions omitted the typed fields. what: - Add pane-scope dataclass fields for shared tmux 3.7 options - Cover the fields and pane set/show behavior in tests - Align the changelog option summary
why: tmux new-pane -m keeps the pane open after command exit and sets remain-on-exit-format, so describing it as a display-only message is misleading. what: - Document message as keep-open remain-on-exit text
why: The format-token topic linked from the floating panes guide still said only tmux 3.3 tokens are version-gated. what: - Add the tmux 3.7 FIELD_VERSION tokens to the version table
why: Window.new_pane's message docstring said only "Message line shown", understating tmux new-pane -m, which keeps the pane open after the command exits (remain-on-exit-format). Pane.new_pane, which it delegates to, already documents this. what: - Match Window.new_pane's message docstring to Pane.new_pane's accurate description
why: The hyperlinks docstring's second sentence advised preserving
escape sequences "in normal pane text", which reads as a contradiction
under a flag that captures only hyperlink targets.
what:
- Reframe the escape_sequences pointer as an accurate alternative
("for full pane text use escape_sequences instead")
why: test_capture_pane_3_7_flags asserted the tmux <3.7 warn-and-ignore path for hyperlinks only; line_numbers and line_flags warn paths were uncovered. what: - Parametrize the test with a NamedTuple + test_id over all three flags, covering both the 3.7+ success path and the <3.7 warn path per flag
why: The hyperlinks docstring claimed "for full pane text use escape_sequences instead", which is inaccurate: full pane text is the default capture, and escape_sequences (-e) only adds text attributes. The two flags are independent, not a binary choice. what: - Remove the second sentence; the first already fully describes -H
why: The message docstrings described -m as keeping the pane open but omitted that tmux then waits for a keypress before closing it (man page: -m is "equivalent to -k"). what: - Add the keypress-to-close detail to the message param on both Pane.new_pane and Window.new_pane
why: Pane.new_pane's target param docstring lacked a type annotation
("target : optional") that every other param and Window.new_pane carry,
despite the signature being target: int | str | None.
what:
- Type target as "int or str, optional" and drop the redundant leading
"Optional," from the description
why: Empty string values are valid for tmux 3.7 new-pane style and message flags, but combined flag construction turns them into bare flags and lets tmux consume -P as the option value. what: - Pass affected new-pane flags and values as separate argv entries - Cover empty values for style, border style, and message options
why: PaneOptions typed six tmux 3.7 options that tmux's options-table.c scopes OPTIONS_TABLE_WINDOW-only. set -p on such an option silently routes to the window and show -p (list) omits it, so the pane field misrepresents tmux's scoping. Only tree-mode-preview-format is window+pane. what: - Remove copy-mode-line-numbers (+ both styles), tree-mode-preview-style, and window-pane-(current-)status-format from PaneOptions; keep tree_mode_preview_format (remain_on_exit stays, also window+pane) - Drop their OptionScope.Pane set/show cases; add test_window_only_3_7_options_absent_from_pane as a regression guard - Correct the CHANGES typed-options scope wording
why: tmux 3.7 gave split-window new-pane's -s/-S/-R/-m/-k via the shared
cmd_split_window_exec, but Pane/Window.split() exposed only empty (-E).
what:
- Add style (-s), active_border_style (-S), inactive_border_style (-R),
message (-m) and keep (-k) to Pane.split and Window.split, under one
has_gte_version("3.7") gate that warns-and-ignores on older tmux
- Add a NamedTuple-parametrized test_split_3_7_flags over the five flags
- Note the new flags in the CHANGES command-flags entry
why: new-pane's bare -k keeps the pane open with the default remain-on-exit-format, distinct from -m (custom message). new_pane exposed message (-m) but had no way to request plain -k. what: - Add keep (-k) to Pane.new_pane and Window.new_pane - Add test_new_pane_keep asserting the float's remain-on-exit == "key" - Note keep in the CHANGES floating-panes entry
why: split emitted its tmux 3.7 value flags as combined args
(f"-m{value}"), so an empty string collapsed to a bare flag. On tmux
3.7 pane.split(message="") raised "-m expects an argument", and
split(message="", shell=...) consumed the shell as the -m value. This
is the same bug already fixed for new_pane.
what:
- Emit style/active_border_style/inactive_border_style/message as
separate argv entries (flag, value), matching Pane.new_pane
- Add test_split_preserves_empty_option_values regression test
why: tmux 3.7 widened pane-active-border-style and pane-border-style from window-only to window+pane scope (options-table.c; set -p now round-trips). PaneOptions typed them only on WindowOptions, so the pane scope was unmodeled — the last tmux 3.7 option-scope gap. what: - Add pane_active_border_style / pane_border_style to PaneOptions (retained on WindowOptions; mirrors window_active_style/window_style) - Cover both with pane-scope set/show cases and the shared-fields guard - Note the scope widening in the CHANGES typed-options entry
why: tmux 3.7 added command-prompt -C (PROMPT_NOFREEZE) to keep panes
unfrozen while the prompt is open, matching display-message -C.
command_prompt already wrapped the sibling -e but not -C.
what:
- Add no_freeze (-C) to Server.command_prompt, gated has_gte_version
("3.7") with warn-and-ignore below, mirroring bspace_exit
- Add the no_freeze case to the parametrized argv test
- Note it in the CHANGES command-flags entry
why: tmux 3.7 added a custom output format to list-keys (-F); the
wrapper only supported -T (key_table). Note: the 3.7 -O/-r sort/reverse
flags are intentionally not wrapped — list_keys returns a list and
callers sort in Python.
what:
- Add format_ (-F) to Server.list_keys, gated has_gte_version("3.7")
with warn-and-ignore below (param is format_ to avoid shadowing the
builtin)
- Add test_list_keys_format asserting the per-line format expansion
- Note it in the CHANGES command-flags entry
why: tmux 3.7 lets run-shell take positional arguments after the shell
command, expanded as #{1}, #{2}, … inside it. run_shell accepted only a
single command string.
what:
- Add args (trailing positional arguments) to Server.run_shell, appended
after command, gated has_gte_version("3.7") with warn-and-ignore below
- Add test_run_shell_args asserting #{1}/#{2} expansion
- Note it in the CHANGES command-flags entry
why: The floating-panes guide toured create/size/style/identify but omitted keep and message, the remain-on-exit params new_pane gained, leaving a notable user-facing affordance only in the autodoc reference. what: - Add a "Keeping a pane open" section showing keep=True / message set the pane's remain-on-exit to "key", with a runnable doctest
why: The bare name new_pane() reads as a generic pane creator, but it is floating-only (tmux's new-pane defaults to floating; the tiled case is split-window). The floating/version contract lived only further down the docstring. what: - Lead the new_pane docstrings with the floating contract and cross- reference :meth:`split` for tiled panes
why: tmux 3.7 repurposed refresh-client -l from the legacy optional-arg
forward form into a no-arg clipboard query (tty_clipboard_query) — the
runtime companion to the get-clipboard option this branch already types.
refresh_client wrapped only target_client.
what:
- Add request_clipboard (-l) to Server.refresh_client, gated
has_gte_version("3.7") with warn-and-ignore below (a bare -l on older
tmux is the legacy form with different meaning)
- Add an argv-verification test for the emitted -l
- Note it in the CHANGES command-flags entry
why: split's `message` (-m) docstring omitted that the pane waits for a keypress before closing. tmux's -m is equivalent to -k, which keeps the pane open and waits for a key to be pressed — new_pane and the sibling `keep` (-k) param already document this, so split read inconsistently. what: - Add "(until a key is pressed)" to the message param docstring in Pane.split and Window.split, matching new_pane's wording
why: CLAUDE.md asks tests to document why standard fixtures were skipped. test_refresh_client_request_clipboard stubs Server.cmd to inspect argv but didn't say why, unlike its siblings in this file. refresh-client -l calls tty_clipboard_query (an xterm clipboard escape) with no observable effect under the headless test server, so argv is the only thing to assert on. what: - Expand the test docstring with a concise why-note for the cmd stub
tony
added a commit
that referenced
this pull request
Jun 28, 2026
Completes tmux 3.7 feature parity on top of the 0.59.0 compatibility release: floating panes, typed 3.7 options, new pane format variables, and new command flags (#694). All 3.7-only surfaces are version-gated; tmux 3.2a-3.6 remain supported with no breaking changes. - CHANGES: date the 0.60.0 release section and open a fresh 0.61.x unreleased placeholder - Bump version 0.59.0 -> 0.60.0 in pyproject.toml and __about__.py - Refresh uv.lock for the new version
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on #693 (tmux 3.7 compatibility, merged and shipped in v0.59.0) to expose tmux 3.7's new surface as typed, discoverable libtmux API. Targets
master.Window.new_pane()/Pane.new_pane()— tmux 3.7's flagshipnew-pane.Pane(version-gated).capture_pane-H/-L/-F,split-E,kill-g,paste_buffer-S.Changes by area
Floating panes —
src/libtmux/pane.py,src/libtmux/window.pyPane.new_pane()/Window.new_pane()issuenew-paneto create a floating pane that overlays the tiled layout like a popup but behaves like a real pane. Geometry mapswidth/height→-x/-y(size) andx/y→-X/-Y(position); also supportsshell,start_directory,environment,zoom,empty, andstyle/active_border_style/inactive_border_style. Returns the floatingPane(pane_floating_flag == "1"); raisesLibTmuxExceptionon tmux < 3.7.Typed options —
src/libtmux/_internal/constants.pyget-clipboardfocus-follows-mouse,message-format,prompt-command-cursor-stylecopy-mode-line-numbers(+-style,-current-line-number-style),tree-mode-preview-format/-style,window-pane-status-format/-current-status-formatremain-on-exitgains thekeyvalueFormat variables —
src/libtmux/neo.py,src/libtmux/formats.pyDeclare the 11 new tmux 3.7 pane format tokens on
neo.Obj(pane_floating_flag,pane_x/y/z,pane_flags,pane_zoomed_flag,pane_pb_progress/pane_pb_state,pane_pipe_pid,bracket_paste_flag,synchronized_output_flag), each gated to tmux 3.7 inFIELD_VERSIONso older tmux keeps a clean-Ftemplate. The two tokens without apane_prefix are routed via_SCOPE_OVERRIDES.Command flags —
src/libtmux/pane.py,src/libtmux/window.py,src/libtmux/session.pyPane.capture_panehyperlinks/line_numbers/line_flags-H/-L/-FPane.split/Window.splitempty-ESession.killgroup-gPane.paste_bufferno_vis-SEach is gated with
has_gte_version("3.7")and warns-and-ignores on older tmux.Design decisions
new_pane(), notsplit(floating=True): tmux deliberately made floating panes a separate command (new-pane) because they overlay the layout rather than subdivide a pane. Mirroring that keepssplit's semantics clean and avoids position parameters that are meaningless for a non-floating split.new_pane) raises — there is no graceful fallback for a command that doesn't exist. Version-gated method doctests use the project'sif has_gte_version(...) / elsepattern so they run on every CI tmux version.Test plan
uv run pytest— full suite green on tmux 3.7 (new options/formats/flags and floating-pane tests;new_panedoctests)-Ftemplate and present on 3.7 (tests/test_neo.py)uv run ruff check .anduv run ruff format .uv run mypyjust build-docsRefs #691