Skip to content

feat(action): add --no-focus flag to new-pane - #5109

Draft
zh4ngx wants to merge 1 commit into
zellij-org:mainfrom
zh4ngx:no-focus-flag
Draft

feat(action): add --no-focus flag to new-pane#5109
zh4ngx wants to merge 1 commit into
zellij-org:mainfrom
zh4ngx:no-focus-flag

Conversation

@zh4ngx

@zh4ngx zh4ngx commented Apr 29, 2026

Copy link
Copy Markdown

Motivation

Scripted multi-pane workflows (orchestrators that spawn helpers via
zellij action new-pane …) currently steal focus from the interactive
user every time a new pane is spawned. The internal Tab::new_pane
already takes a should_focus_pane: bool, but the action layer hardcodes
true regardless of caller intent — see zellij-server/src/screen.rs
ClientId and TabIndex branches. This PR exposes that switch at the CLI.

Change

Add a --no-focus flag to zellij action new-pane (and zellij action edit for parity, since EditFile follows the same focus path).

Internally, a new should_focus_pane: bool field is added to:

  • Action::NewPane (legacy KDL keybind variant)
  • Action::NewBlockingPane
  • Action::NewFloatingPane
  • Action::NewTiledPane
  • Action::NewStackedPane
  • Action::EditFile

The field is threaded through PtyInstruction::SpawnTerminal and
ScreenInstruction::NewPane, replacing the hardcoded true literals at
screen.rs ClientId and TabIndex branches. The PaneId branch (used when
--near-current-pane resolves to a pane) is unchanged — it already
hardcodes false.

Action::NewInPlacePane is intentionally not threaded: in-place
replaces the focused pane, so "no focus" is semantically meaningless.
The CLI enforces this via clap's conflicts_with(\"in-place\").

Backward compatibility

  • CLI default for --no-focus is false, so omitting the flag yields
    the existing focus-stealing behavior.
  • All in-tree callsites (KDL parser, plugin APIs, default-plugin keybinds)
    set should_focus_pane: true to preserve current behavior.
  • Protobuf fields are added at the next free tag as optional bool
    older clients deserialize as None, which maps back to true on the
    receiver side. No wire-format break.

Compatibility with related flags

  • --no-focus --near-current-pane: allowed. Orthogonal: focus vs.
    placement.
  • --no-focus --blocking: allowed. Caller wants to wait for command
    exit but not steal focus from interactive user.
  • --no-focus --in-place: rejected at CLI parse time (clap conflict).

Tests added/updated

  • New round-trip tests in zellij-utils/src/input/actions.rs:
    • test_new_pane_no_focus_threads_through_tiled
    • test_new_pane_default_focuses_pane
    • test_new_pane_no_focus_threads_through_floating
    • test_new_pane_no_focus_threads_through_stacked
    • test_new_pane_no_focus_threads_through_blocking
    • test_edit_no_focus_threads_through
  • zellij-utils/src/ipc/tests/roundtrip_tests.rs fixtures updated.
  • zellij-server/src/unit/screen_tests.rs fixtures updated.
  • Insta snapshots regenerated for KDL serialization and screen instruction
    debug output.

Test results

  • cargo test -p zellij-utils --lib: 384 passed
  • cargo test -p zellij-server --lib: 1066 passed
  • cargo test -p zellij-client --lib: 111 passed

Verification

$ zellij action new-pane --help | grep -A1 no-focus
        --no-focus
            Spawn the new pane without transferring keyboard focus to it

$ zellij action new-pane --no-focus --in-place
error: The argument '--no-focus' cannot be used with '--in-place'

Out of scope

  • Plugin pane variants (NewTiledPluginPane, NewFloatingPluginPane,
    NewInPlacePluginPane) use a separate code path through
    PtyInstruction::FillPluginCwd, which already has its own
    Option<bool> for plugin focus. Threading --no-focus through plugin
    panes would be a larger change touching the plugin-loading flow; not
    covered here. CLI --no-focus only affects terminal panes.

Add a `--no-focus` CLI flag to `zellij action new-pane` (and `zellij action edit`)
so scripted multi-pane workflows can spawn new panes without stealing focus from
the user's interactive pane.

Threading: a new `should_focus_pane: bool` field is added to `Action::NewPane`,
`Action::NewBlockingPane`, `Action::NewFloatingPane`, `Action::NewTiledPane`,
`Action::NewStackedPane`, and `Action::EditFile`. The field is wired through
`PtyInstruction::SpawnTerminal` -> `ScreenInstruction::NewPane`, replacing
the previously hardcoded `true` literals at screen.rs ClientId and TabIndex
branches. The PaneId branch is unchanged (already used `false`).

CLI flag conflicts with `--in-place` (in-place replaces the focused pane,
making "no focus" semantically meaningless). Compatible with `--blocking`
and `--near-current-pane`. `Action::NewInPlacePane` is intentionally not
threaded — its semantics imply focus.

Backward compat: legacy default is `should_focus_pane: true` everywhere.
Protobuf fields added at the next free tag as `optional bool` so older
clients deserialize as `None`, mapped back to `true`. KDL keybindings
preserve the legacy default.

Tests: 6 new round-trip tests in actions.rs covering Tiled/Floating/Stacked/
Blocking/Edit paths plus default-focuses-pane regression test. Existing
roundtrip_tests.rs and screen_tests.rs fixtures updated. Insta snapshots
regenerated for KDL and screen instruction debug output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant