Skip to content

v0.9.0: Tasks 74, 75, 98 — broadcast input, per-pane env, close guard#349

Merged
fredclausen merged 13 commits into
mainfrom
task-74-75-98/v090-finish
Jun 11, 2026
Merged

v0.9.0: Tasks 74, 75, 98 — broadcast input, per-pane env, close guard#349
fredclausen merged 13 commits into
mainfrom
task-74-75-98/v090-finish

Conversation

@fredclausen

Copy link
Copy Markdown
Member

Closes the remaining v0.9.0 "Modern Workflow Terminal" work on the combined
task-74-75-98/v090-finish branch. See Documents/PLAN_VERSION_090.md for the
full per-subtask breakdown and completion notes.

Task 74 — Broadcast Input to Panes ✅

Per-tab toggle that fans out keyboard InputEvent::Key events to every leaf
pane in the active tab. Mouse/scroll/resize/focus events stay pane-local.

  • Tab::broadcast_input flag + KeyAction::ToggleBroadcastInput (Ctrl+Shift+I).
  • Fan-out at the two real keyboard send sites; tab-bar antenna glyph + per-pane
    "BROADCAST" indicator.
  • [tabs] confirm_broadcast opt-in confirmation dialog (mirrors paste guard).

Task 75 — Verify Per-Pane Env Round-Trip ✅

Verification + documentation only (no production code change). The reduced
v0.9.0 scope: confirm the existing LayoutPane::env plumbing round-trips and
document it.

  • Regression tests: load → variable-substitute → resolve, serialize → reparse,
    and empty-env omission.
  • Documented per-pane env substitution ($1, ${named}, $ENV{…}) in
    LAYOUT_FORMAT.md.

Task 98 — Block Close on Running Commands ✅

Confirmation dialog when closing a pane/tab/window that has a running
foreground command (detected via OSC 133 markers).

  • [close_guard] config section (enabled / unknown_blocks /
    guard_app_quit) with full ConfigPartial merge wiring + Nix mirror.
  • New close_guard module: lock-free snapshot detection (a pane is "running"
    only when its latest OSC 133 block saw C but not D — an idle prompt never
    blocks), Cancel / Force-Close dialog, and wiring into pane/tab/window close.
  • KeyAction::ForceClose (unbound) + a Close Guard section in the Security
    settings tab.

Notable deviations (documented in the plan)

  • No multi-window app-quit path exists in freminal, so guard_app_quit has no
    separate gate — window-close guarding covers every exit.
  • The dialog command label is the pane title (or <unknown command>) because
    CommandBlock does not capture the command-line text.
  • The optional "Close Other Panes" dialog button is deferred.

Verification

  • cargo test --all — 103 suites green, 0 failures
  • cargo clippy --all-targets --all-features -- -D warnings — clean
  • cargo fmt --all -- --check — clean
  • cargo machete — clean
  • nixfmt --check / statix check / deadnix --fail on the home-manager module — clean

Add a per-tab broadcast_input bool (default false) to Tab, initialize it
in Tab::new, and add a toggle_broadcast() helper that flips the flag and
returns the new state. Include the field in the Debug impl. Two unit
tests cover the default and the toggle behavior.
Register the ToggleBroadcastInput key action following the BindingMap
convention: enum variant, name(), display_label(), FromStr, ALL, and a
default Ctrl+Shift+I binding in register_tab_bindings. Dispatch in
actions.rs toggles the active tab's broadcast_input flag and surfaces an
info toast. Document the binding in config_example.toml. Update the ALL
count (60->61) and default-binding count (47->48) assertions; add
round-trip and default-binding tests.
When the active tab has broadcast_input enabled, mirror the active pane's
keyboard input to every other leaf pane. write_input_to_terminal gains a
key_broadcast_targets slice; a broadcast_key_bytes helper fans out each
encoded InputEvent::Key (KKP release path and the main text/key/paste
encode path). Mouse, scroll-derived, resize, focus, and selection events
are never broadcast. The app_impl render loop collects per-pane senders
once per frame and passes the other-pane senders only to the active
pane's show() call. The tab bar prepends a satellite-antenna glyph when
broadcast is active. Four unit tests cover the fan-out helper.
When the active tab is broadcasting input, tint every split border yellow
(instead of the usual blue/gray) and paint a small "BROADCAST" tag in the
top-right corner of each visible pane (including a zoomed pane). Top-right
placement avoids colliding with the password-prompt lock icon in the tab
bar. Purely visual; verified manually.
Add a confirm_broadcast bool to TabsConfig (default false), wired through
config_example.toml, the Settings Tabs panel, the Nix home-manager module,
and a round-trip test. The Tabs settings tab gains a Broadcast Input
section showing the current shortcut (with a jump-to-keybindings button)
and the confirm toggle. A new broadcast_guard module provides a per-window
confirm dialog (mirroring the paste-guard pattern); when confirm_broadcast
is set, enabling broadcast opens the dialog instead of toggling
immediately. The dialog is registered in ui_overlay_open so Escape/Enter
do not leak to the terminal. Disabling broadcast never prompts.
Record all five 74.x subtask commits in PLAN_VERSION_090.md with design
notes, update the task-summary status to Complete, and point tasks 74/75/98
at the combined task-74-75-98/v090-finish branch. Update MASTER_PLAN.md
task-summary status (Pending merge) and add a Completion Tracking row.
Add regression tests for the existing LayoutPane env plumbing (load,
variable substitution, resolve, and serialize round-trips) and document
the per-pane env substitution behavior in LAYOUT_FORMAT.md. No production
code change -- Task 75 in v0.9.0 is verification + documentation only.
Read-only audit mapping every pane/tab/window close and app-quit entry
point, the modal/dialog pattern to follow (broadcast_guard.rs), snapshot
read path, and the key finding that there is no multi-window app-quit
path (Quit closes only the focused window). No code change.
Add CloseGuardConfig (enabled / unknown_blocks / guard_app_quit) with
the full ConfigPartial + apply_partial merge wiring, the partial-merge
guard test entry, two dedicated round-trip tests, config_example.toml
documentation, and the Nix home-manager mkOption mirror.
…ands

Add the close-on-running-command guard: a new close_guard module with
lock-free snapshot detection of running foreground commands, a
confirmation dialog (Cancel / Force Close) mirroring broadcast_guard,
and wiring into pane close, tab close, and window close. A pane counts
as running only when its most recent OSC 133 block has seen C (output
start) and not D -- an idle prompt with no command does not block close.
Add an unbound ForceClose key action that resolves an open close-guard
dialog as Force Close without the mouse or Ctrl+Enter. Sets a per-window
pending_force_close flag drained where the dialog resolves.
Add a Close Guard section to the Security settings tab with enabled /
unknown_blocks / guard_app_quit toggles, mirroring the Paste Guard
section. Mark Task 98 (and Task 75) complete in the plan and MASTER_PLAN.
@fredclausen fredclausen merged commit 2820db3 into main Jun 11, 2026
15 of 16 checks passed
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