Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
43 changes: 43 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,49 @@ $ tmuxp@next load yoursession
_Notes on the upcoming release will go here._
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

### What's new

#### Pluggable workspace builders (#1066)

tmuxp can now build a workspace with a builder other than its built-in one. Point
a workspace file's `workspace_builder` key at an installed builder — by registered
name or by Python import path — and tmuxp loads the session through it. Builders
that ship outside tmuxp's environment can be imported from directories listed in
`workspace_builder_paths`, which tmuxp trusts only for that load. The built-in
builder stays the default, so existing workspaces are unaffected. See
{ref}`custom-workspace-builders`.

#### Configurable pane readiness (#1066)

Before sending a pane's layout and commands, tmuxp can wait for the shell to draw
its prompt — a guard against a zsh prompt-redraw artifact. That wait is now a
policy you set per workspace:

```yaml
workspace_builder_options:
pane_readiness: auto # auto | always | never (also true/false)
```

The new default, `auto`, waits only when the session's shell is zsh, so zsh users
see no change while bash, sh, and other shells skip a wait they never needed. Set
`always` to keep the previous wait-everywhere behavior, or `never` to skip it
entirely. See {ref}`custom-workspace-builders`.

#### Workspace builder API (#1066)

`tmuxp.workspace.builder` is now a package. The default builder is
{class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder`, with
`WorkspaceBuilder` kept as a backwards-compatible alias. Third-party builders
implement {class}`~tmuxp.workspace.builder.protocol.WorkspaceBuilderProtocol`
(shaped to allow async builders later) and are resolved by
{func}`~tmuxp.workspace.builder.registry.resolve_builder_class` from an entry
point or import path, with trusted import directories handled by
{func}`~tmuxp.workspace.builder.registry.resolve_builder_paths`. Builder behavior
is configured through {class}`~tmuxp.workspace.options.WorkspaceBuilderOptions`
and {class}`~tmuxp.workspace.options.PaneReadiness`, and resolution failures raise
{exc}`~tmuxp.exc.WorkspaceBuilderError` and its subclasses. See
{ref}`custom-workspace-builders` for the guide.

## tmuxp 1.72.0 (2026-06-28)

tmuxp 1.72.0 bumps libtmux to 0.60.0, completing tmux 3.7 feature parity at the library layer tmuxp builds on. The new libtmux capabilities — floating panes, typed tmux 3.7 options, new pane format variables, and new command flags — are reachable through tmuxp's Python API and `tmuxp shell`, while the YAML workspace format and tmux 3.2a-3.6 support stay unchanged.
Expand Down
2 changes: 1 addition & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def socket_name(request: pytest.FixtureRequest) -> str:

# Modules that actually need tmux fixtures in their doctests
DOCTEST_NEEDS_TMUX = {
"tmuxp.workspace.builder",
"tmuxp.workspace.builder.classic",
}


Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ windows:

tmuxp sessions can be scripted in python. The first way is to use the
ORM in the {ref}`API`. The second is to pass a {py:obj}`dict` into
{class}`~tmuxp.workspace.builder.WorkspaceBuilder` with a correct schema.
{class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder` with a correct schema.
See: {meth}`tmuxp.validation.validate_schema`.

:::
Expand Down
13 changes: 13 additions & 0 deletions docs/configuration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ TMUXP_CONFIGDIR and other env vars.
Sample workspace configurations.
:::

:::{grid-item-card} Workspace builders
:link: workspace-builders
:link-type: doc
Select a builder and tune pane readiness.
:::

::::

tmuxp loads your terminal workspace into tmux using workspace files.
Expand Down Expand Up @@ -211,6 +217,12 @@ TMUXP_CONFIGDIR and other env vars.
Sample workspace configurations.
:::

:::{grid-item-card} Workspace builders
:link: workspace-builders
:link-type: doc
Select a builder and tune pane readiness.
:::

::::

```{toctree}
Expand All @@ -219,4 +231,5 @@ Sample workspace configurations.
top-level
environmental-variables
examples
workspace-builders
```
9 changes: 9 additions & 0 deletions docs/configuration/top-level.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,12 @@ Notes:
```

Above: Use `tmux` directly to attach _banana_.

## Workspace builder keys

A workspace file can also select a custom builder and tune builder behavior with
`workspace_builder`, `workspace_builder_paths`, and `workspace_builder_options`.

```{seealso}
{ref}`workspace-builders`
```
142 changes: 142 additions & 0 deletions docs/configuration/workspace-builders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
(workspace-builders)=

# Workspace builders

```{versionadded} 1.72.0
```

Most workspaces never need these keys. By default tmuxp builds your session with
its built-in *classic* builder and waits for a pane's shell prompt only when that
shell is zsh — existing workspace files keep working unchanged. Set the keys below
to swap in a different builder or to tune the prompt wait. **Omit a key (or remove
it) to restore the default.**

| Key | Type | Default | Purpose |
| --- | --- | --- | --- |
| `workspace_builder` | string | `classic` | Which builder turns the workspace into a session. |
| `workspace_builder_paths` | string or list of strings | _(none)_ | Trusted directories to import a builder from. |
| `workspace_builder_options` | mapping | _(all defaults)_ | Builder-behavior knobs, such as `pane_readiness`. |

For the narrative — writing a builder, packaging one, the trust boundary, and
testing — see {ref}`custom-workspace-builders`.

(workspace-builder-key)=

## `workspace_builder`

Selects the builder. The default, `classic`, is tmuxp's built-in builder. A value
is resolved in this order:

1. absent or empty → the built-in classic builder (nothing is imported);
2. contains `:` → a `module:attr` object reference;
3. no `.` and no `:` → a builder registered under the `tmuxp.workspace_builders`
entry-point group, selected by name;
4. dotted with no `:` → an entry-point name if one is registered, otherwise a
`module.attr` import path.

```yaml
session_name: my-session
workspace_builder: classic
windows:
- panes:
- vim
```

See {ref}`custom-workspace-builders` for selecting and packaging builders, and
{func}`~tmuxp.workspace.builder.registry.resolve_builder_class` for the resolver.

(workspace-builder-paths-key)=

## `workspace_builder_paths`

Directories to import a builder from when it lives outside tmuxp's environment —
for example, a script in your config directory. Accepts a single string or a list
of strings. tmuxp expands `~` and environment variables, resolves relative entries
against the workspace file's directory, and requires each entry to be an existing
directory; the paths are added to `sys.path` only for the import and build.

```yaml
workspace_builder: my_local_builder:CustomBuilder
workspace_builder_paths:
- ~/.config/tmuxp/builders
```

```{warning}
A workspace file that names a builder runs that builder's Python code. Only load
workspace files you trust. See the security note in {ref}`custom-workspace-builders`.
```

(workspace-builder-options-key)=

## `workspace_builder_options`

A catalog of builder-behavior settings, independent of which builder you use.
Today it holds a single key, `pane_readiness`, which controls whether tmuxp waits
for a pane's shell prompt before sending its layout and commands — a guard against
a zsh prompt-redraw artifact:

```yaml
workspace_builder_options:
pane_readiness: auto
```

| Value | Behavior |
| --- | --- |
| `auto` _(default)_ | Wait only when the session's shell is zsh. |
| `always` | Always wait for default-shell panes. |
| `never` | Never wait; fastest, but accepts the prompt/layout race for shells that need it. |

`pane_readiness` also accepts truthy/falsy aliases — `true`/`on`/`yes`/`1` map to
`always`, and `false`/`off`/`no`/`0` map to `never` (full list in
{ref}`custom-workspace-builders`). An unrecognized value fails the load with:

```text
invalid pane_readiness value: 'sometimes'; expected one of: auto, always/true/on/yes/1, never/false/off/no/0
```

Panes that run a custom `shell` or `window_shell` never wait, regardless of policy.
See {class}`~tmuxp.workspace.options.PaneReadiness` and
{class}`~tmuxp.workspace.options.WorkspaceBuilderOptions` for the parsing rules.

## Minimal complete example

````{tab} YAML
```yaml
session_name: my-session
workspace_builder: classic
workspace_builder_paths:
- ~/.config/tmuxp/builders
workspace_builder_options:
pane_readiness: auto
windows:
- window_name: editor
panes:
- vim
```
````

````{tab} JSON
```json
{
"session_name": "my-session",
"workspace_builder": "classic",
"workspace_builder_paths": ["~/.config/tmuxp/builders"],
"workspace_builder_options": {
"pane_readiness": "auto"
},
"windows": [
{
"window_name": "editor",
"panes": ["vim"]
}
]
}
```
````

```{seealso}
{ref}`custom-workspace-builders` — narrative guide to selecting, packaging,
writing, and testing builders ·
{class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder` ·
{class}`~tmuxp.workspace.builder.protocol.WorkspaceBuilderProtocol`
```
8 changes: 0 additions & 8 deletions docs/internals/api/workspace/builder.md

This file was deleted.

8 changes: 8 additions & 0 deletions docs/internals/api/workspace/builder/classic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Classic builder - `tmuxp.workspace.builder.classic`

```{eval-rst}
.. automodule:: tmuxp.workspace.builder.classic
:members:
:show-inheritance:
:undoc-members:
```
40 changes: 40 additions & 0 deletions docs/internals/api/workspace/builder/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Builder - `tmuxp.workspace.builder`

`tmuxp.workspace.builder` is a package. The classic, default builder lives in
{mod}`tmuxp.workspace.builder.classic`; the public contract and the selection
machinery live alongside it.

`WorkspaceBuilder` remains importable from `tmuxp.workspace.builder` as a
backwards-compatible alias of
{class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder`.

::::{grid} 1 1 2 2
:gutter: 2 2 3 3

:::{grid-item-card} Classic builder
:link: classic
:link-type: doc
The built-in, default builder — `tmuxp.workspace.builder.classic`.
:::

:::{grid-item-card} Builder protocol
:link: protocol
:link-type: doc
The contract a builder must satisfy — `tmuxp.workspace.builder.protocol`.
:::

:::{grid-item-card} Builder registry
:link: registry
:link-type: doc
Builder selection and trusted import paths — `tmuxp.workspace.builder.registry`.
:::

::::

```{toctree}
:hidden:

classic
protocol
registry
```
8 changes: 8 additions & 0 deletions docs/internals/api/workspace/builder/protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Builder protocol - `tmuxp.workspace.builder.protocol`

```{eval-rst}
.. automodule:: tmuxp.workspace.builder.protocol
:members:
:show-inheritance:
:undoc-members:
```
8 changes: 8 additions & 0 deletions docs/internals/api/workspace/builder/registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Builder registry - `tmuxp.workspace.builder.registry`

```{eval-rst}
.. automodule:: tmuxp.workspace.builder.registry
:members:
:show-inheritance:
:undoc-members:
```
3 changes: 2 additions & 1 deletion docs/internals/api/workspace/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ If you need an internal API stabilized please [file an issue](https://github.com
:::

```{toctree}
builder
builder/index
constants
finders
freezer
importers
loader
options
validation
```
8 changes: 8 additions & 0 deletions docs/internals/api/workspace/options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Options - `tmuxp.workspace.options`

```{eval-rst}
.. automodule:: tmuxp.workspace.options
:members:
:show-inheritance:
:undoc-members:
```
2 changes: 1 addition & 1 deletion docs/redirects.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"api/cli/shell.md" "internals/api/cli/shell.md"
"api/cli/utils.md" "internals/api/cli/utils.md"
"api/workspace/index.md" "internals/api/workspace/index.md"
"api/workspace/builder.md" "internals/api/workspace/builder.md"
"api/workspace/builder.md" "internals/api/workspace/builder/index.md"
"api/workspace/constants.md" "internals/api/workspace/constants.md"
"api/workspace/finders.md" "internals/api/workspace/finders.md"
"api/workspace/freezer.md" "internals/api/workspace/freezer.md"
Expand Down
Loading