aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-utils/src/data.rs (follow)
Commit message (Collapse)AuthorAge
* sl0p: --purpose flag and tab-level purposeuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Completes the two pieces deferred from the first workspace-hints pass. `zellij run --purpose X` / `zellij action new-pane --purpose X` tag a pane at spawn time, which is what an agent wants for panes it creates itself; the OSC wrapper still works for programs that declare their own role. Not supported with --in-place, which replaces another pane rather than being a pane of its own: clap rejects the combination rather than accepting a flag that would do nothing. `tab name="work" purpose="agent-work"` gives a whole tab a role, reported by `list-tabs --json`. It rides the tab's ROOT layout node, reusing the pane field because a tab's root is the tab container and never itself a pane -- asserted by a test for the tightest case (a tab holding exactly one pane), since a leak there would silently mislabel a pane. The interesting part is why the flag first shipped broken. There are TWO protobuf boundaries -- the plugin API and the client->server IPC contract -- and a CLI flag must cross the second one. Defaulting the field in the conversion (what "no protoc" usually forces) compiles cleanly and then silently drops the value in transit, so --purpose parsed, printed a pane id, and did nothing. Fixed by adding the field to both contracts by hand, in the .proto AND the committed generated prost, kept in lockstep so a future regeneration reproduces it: protoc is only needed to regenerate, not to build. Both boundaries now have a round-trip test, and the IPC one was confirmed to fail when the wire read is removed. Sanitising moved to Grid::set_authoritative_purpose, the single choke point every out-of-band source passes through, after the CLI path was observed delivering `bad purpose; rm -rf /` intact into list-panes --json.
* sl0p: per-pane workspace hints (purpose=)user2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Implements the zellij half of sl0p/workspace-hints-proposal.md: a pane can carry an opaque agent-facing hint for what it is FOR, so a project can ship a layout that an agent is able to navigate instead of guessing. zellij never interprets a purpose. The consuming agent owns the entire vocabulary, which is what keeps this to one string threaded along the existing status_bar path rather than a policy engine, and lets the vocabulary change without a rebuild. Two ways in, because declared panes and agent-created panes are different: layout: pane name="server" purpose="dev-server" in-band: printf '\033]5577;1;purpose;agent:build\033\\' The second exists because tagging at spawn time would mean threading a field through 28 SpawnTerminal call sites, and a new action variant is impossible while protobuf enums cannot be regenerated. Wrapping the command costs one printf. A declared purpose outranks an in-band one and cannot be overridden or cleared by it. OSC arrives from whatever the pane happens to be showing, so otherwise a `cat hostile.txt` in the human's editor pane could relabel it as the agent's scratch pane and invite an agent to run commands where the human is working. A layout is written by a human in a file they can read; in-band data only fills a blank. Exposed on PaneListEntry (`list-panes --json`) rather than PaneInfo, which crosses the plugin protobuf API we cannot regenerate; resolved in the screen thread where panes live rather than via route.rs's per-pane PTY round-trips. Values are sanitised to a bounded [A-Za-z0-9_.:/-] token at both entry points, because a purpose is never rendered -- it is matched against and pasted into consumers' shell and JSON pipelines. Verified end to end rather than by inspection: purposes from a layout appear in list-panes --json against the right pane ids, an agent-spawned pane tags itself, a declared purpose survives an in-band override attempt, all of it comes back after kill + resurrect, an unknown pane property is still a parse error, and a layout with no purpose leaves dump-layout unchanged.
* sl0p: add an indexed style for status bar buttonsuser2026-08-12
| | | | | | | | | | | | | | | | | | | | | `status_bar_button_style "indexed"` renders `1:Approve 2:Reject` instead of `[Approve] [Reject]`, so the bar doubles as a key legend for a harness that binds alt+1..9 to the same actions. A config option rather than a protocol field: whether the numbers mean anything depends on what the USER has bound, not on what the program knows. It needs no wire change and so no version bump, and the click report still carries the id -- never the index -- so a program is unaffected by which style is in force. The style is threaded into layout_status_bar, the one function the renderer draws from and the hit-test resolves against. That is what makes this safe: changing a button's rendered width necessarily changes its clickable span too, verified by a test asserting span width equals the drawn width and that every drawn column resolves back, for both styles. Also fixes the web client's Style construction to read the option from config rather than defaulting it -- my first pass claimed that code had no pane_frames config available, which was wrong; it uses it two lines further down.
* sl0p: optionally hide the 'Pane #N' placeholder titleuser2026-08-08
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | `ui { pane_frames { hide_default_pane_names true } }`, default false. A pane now shows no title at all until something actually names it: the user renaming it (including by double-clicking the title row) or the running program setting a title. Upstream always shows an auto-generated 'Pane #N'. Implemented by tracking whether pane_title is still the constructor's placeholder, NOT by matching the string: a program that legitimately titles itself 'Pane #3' must not be mistaken for an unnamed pane. set_title clears the flag, since something then chose that title. current_title reads the option at render time rather than baking an empty title in at construction, so it hot-reloads and turning the option back off restores the placeholder on existing panes. Display precedence is unchanged and now explicit in the code: user-given name > title set by the running program > placeholder render_terminal_title already fell back to an empty string rather than the placeholder, so the terminal WINDOW title is unaffected. Empty titles were already handled by the frame renderer (render_title_left_side returns None for an empty title), including the sl0p centred/right alignment path. Baselines held: zellij-server 1436/30, zellij-utils 436/5.
* sl0p: grayscale the contents of unfocused panesuser2026-08-08
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `ui { pane_frames { inactive_grayscale 0-100 } }`, default 0 (off). Real colour transformation, not the SGR 'faint' attribute zellij already uses for frames: faint is terminal-dependent, several terminals only apply it to the 16 ANSI colours, and it does not remove colour. Each cell's colour is resolved to RGB and blended toward its own Rec.709 luminance, so an unfocused pane keeps its brightness but drains of colour. Colour resolution covers the cases that actually occur: RgbCode directly, ColorIndex via eightbit_to_rgb, and NamedColor by mapping to its 0-15 index. Cells with NO explicit colour are deliberately left alone - they render in the terminal default, which is already unsaturated, and rewriting them would mean emitting an explicit colour for nearly every cell for no visible gain. Two non-obvious pieces were needed to make it actually work: - The output buffer only emits CHANGED cells, so an idle pane would keep its old colours indefinitely after losing focus. Pane::refresh_for_focus_change forces a full repaint, called from ActivePanes::insert (the single place focus changes). It is a no-op when the feature is off, so it costs nothing by default. - Terminal panes render through the SHARED multi-client path. Focus is per client, so once unfocused panes are desaturated two clients may need different output for the same pane. Both tiled and floating panes now fall back to per-client rendering when grayscale is enabled - the reason this is opt-in. Known limitation: sixel/kitty image chunks are not colour-transformed and stay in colour. Hot-reloads like the other sl0p options. Tests cover the transform: full strength yields a true neutral grey, partial sits between original and grey, indexed and named colours resolve rather than being skipped, luminance is perceptual (green brighter than blue) not a flat average, already-grey is unchanged, and non-colour codes are untouched. Baselines held: zellij-server 1436/30, zellij-utils 436/5.
* sl0p: fix getting stuck in rename mode (base_mode wiped)user2026-08-08
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Double-click-to-rename trapped the client: once in RenamePane, Esc and Enter did nothing and there was no way out without killing the session. Cause: Screen::change_mode() does `mode_info.base_mode = base_mode`, i.e. it OVERWRITES the client's base mode with whatever it is handed. The double-click path passed None, believing that meant 'leave it alone'. It does not - ModeInfo::change_to_default_mode() is: if let Some(base_mode) = self.base_mode { self.mode = base_mode; } so a None base_mode makes leaving ANY temporary mode a silent no-op. The legitimate caller in lib.rs passes Some(base_mode) from the session config; this path had no equivalent. Fix: Tab::client_base_mode() reads the client's existing base_mode (falling back to the tab default) and the rename path passes it through, so entering rename via the mouse leaves the return path exactly as the keybind does. Added a test on ModeInfo pinning the trap itself: change_to_default_mode is a no-op with base_mode None, and returns to the base mode when it is set. That is the property that makes passing None dangerous, and it now fails loudly if anyone assumes otherwise. Immediate escape hatch for a session stuck on an older build: zellij action switch-mode locked (that goes through the server path, which sets base_mode correctly) Baselines held: zellij-server 1429/30, zellij-utils 436/5.
* sl0p: CSS-style 1/2/4-value shorthand for inner paddinguser2026-08-08
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | `padding` now accepts: padding 1 -> all four sides padding 1 2 -> up/down = 1, left/right = 2 padding 1 2 3 4 -> up=1 down=2 left=3 right=4 NOTE the 4-value order is UP DOWN LEFT RIGHT as requested, deliberately NOT CSS's clockwise top-right-bottom-left. Documented on PanePadding and at the parser, since the similarity to CSS invites the wrong assumption. 3 or 5 values are rejected with a message naming the accepted forms, rather than being silently reinterpreted. Negative values clamp to 0. ASPECT: only the 1-value form is aspect-corrected (left/right multiplied by gap_aspect) so `padding 1` looks square on a non-square cell grid. The 2- and 4-value forms are literal: specifying axes separately means the caller is already controlling horizontal size, and silently doubling it would surprise. Style now carries a PanePadding {top,bottom,left,right} instead of an x/y pair, which also collapses the reconfigure plumbing back to a single field rather than growing another parameter per side. to_kdl always emits the explicit 4-value form: emitting the 1-value form would re-apply gap_aspect on the next read and silently double the padding on every config round-trip. Baselines held: zellij-utils 435/5, zellij-server 1421/30.
* sl0p: uniform pane gap + aspect-corrected horizontal gapuser2026-08-08
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two changes to how the gap is defined. 1. UNIFORM GAP. Previously every pane inset every side by `gap`, so two neighbours each gave up a cell and the space between them was 2*gap while the screen edge got only gap. Now a pane insets RIGHT and BOTTOM always, and LEFT/TOP only where that edge is the viewport boundary. Two neighbours contribute exactly one gap between them, and the outer screen edges get the same gap. `gap 1` now means 1 everywhere. The per-side offset is computed in tiled_panes, which is the layer that knows the viewport, and pushed to panes via Pane::set_gap_offset before the content-offset sweep. Floating panes keep a zero gap: they are positioned explicitly by the user, so an inter-pane gap does not apply to them (they still get padding). 2. ASPECT CORRECTION. Terminal cells are roughly twice as tall as they are wide, so an equal number of rows and columns does not look like an equal distance. `gap` and `padding` are now expressed in ROWS, and columns are multiplied by `gap_aspect` (default 2, minimum 1). Style carries the resolved pane_gap_x/y and pane_padding_x/y. FrameConfig::Default is now hand-written rather than derived: gap_aspect must default to 2 to match the KDL parser, otherwise a parsed config disagrees with a default-constructed one and it surfaces as config round-trip diffs (this broke can_define_ui_configuration_in_configfile and both bare_config_from_default_assets_to_string tests). Tests assert the actual invariants: stacked and side-by-side neighbours get exactly one gap between them, screen edges get the same gap as between panes, and inset_by refuses to collapse a pane too small to survive it. Baselines held: zellij-server 1421/30, zellij-utils 433/5.
* sl0p: configurable pane gap (outer) and padding (inner)user2026-08-08
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Adds `ui { pane_frames { gap N; padding N } }`, both defaulting to 0 so stock configs are unaffected. gap - cells each pane gives up on every side, leaving real empty terminal between neighbouring panes (WM-style outer gaps). Two adjacent panes therefore show 2*gap between them. padding - cells between a pane's frame and its content; breathing room inside the pane without moving the frame. Design: the pane KEEPS its allocated PaneGeom. Only two things move: 1. the drawn frame, via Viewport::inset(gap), reusing the existing frame_geom_override mechanism already used for stacked panes; 2. the content, via Offset::expanded_by(gap + padding) inside set_content_offset. Because the allocation is untouched, the kasuari resize solver and mouse hit-testing need no changes at all, and a click landing in a gap still resolves to the owning pane. Content position, size and cursor are all derived from content_offset, so widening it in one place keeps every consumer consistent. Viewport::inset refuses to shrink a pane below a frame's minimum viable extent (two borders + one content cell) rather than emitting a degenerate or zero-sized frame. Negative KDL values clamp to 0 instead of wrapping. Tests cover: inset identity/shrink/recentre, the containment invariant (an inset frame never escapes its allocation), small-pane refusal, and the key safety property that content inset by gap+border+padding lands strictly inside the frame drawn at inset(gap).
* sl0p: configurable pane title alignmentuser2026-08-08
| | | | | | | | | | | | | | | | | | | | | | | Adds `ui { pane_frames { title_alignment "left"|"center"|"right" } }`. Full-frame panes previously hardcoded the title flush left, while frameless panes (pane_frame_style "titles") already centered it via compose_bracketed_title. This brings full frames up to parity and makes the position configurable. Implementation notes: - The value rides on `Style`, which is already threaded to FrameParams, so no new plumbing through Tab/PaneContentsAndUi was needed. - It is deliberately NOT round-tripped over the plugin protobuf boundary: plugins have no use for frame title alignment and adding a proto field would force regenerating the committed prost assets in assets/prost/. - Left keeps the original code path untouched, so default configs render exactly as before. - The geometry loop clamps the title start against the right-side indicators and breaks on `>=` rather than `==`, so a mis-clamped segment cannot spin. Tests: alignment ordering, frame width preservation across widths, and narrow-pane safety (cols 1..24 must not panic or corrupt width).
* Dep upgrades for debian (#5420)Aram Drevekenin2026-07-28
| | | | | | | | | | | | | * chore(deps): upgrade base64 0.22, colored 3.1, crossterm 0.29, dialoguer 0.11, directories 6.0, highway 1.2, prost 0.12.6, strip-ansi-escapes 0.2.1, suggest 0.5.1 * chore(deps): upgrade miette 7.6, nix 0.30, notify 8.2, notify-debouncer-full 0.5 * vte 0.15 * clap 4.6 * workspaceify plugin deps * don't send empty fs watch events
* Nested session handling (#5417)Aram Drevekenin2026-07-27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * fix: theme query and updated pixel dimensions * add no-ui-fullscreen key/action/capability * protocol core * integration test harness * fix integration tests * fix flaky tests * nested ui (dimming) and directional focus initial implementation * integration tests for nested ui dimming and directional focus * initial full screen nesteed implementation * fix: keep hidden floating panes hidden across nested no-ui fullscreen * fix integration tests * no ui fullscreen integration tests * hardening behavior/tests * fix(nested): re-derive descend/dim on host tab switch and new-tab so clicking dimmed chrome ascends * initial modal implementation * initial ui improvements * fix integration tests * improve nested selection ui * nested session modal refactoring and tests * improve integration test speed somewhat * some ui adjustments * simplify full/auto nested session handling * hints in compact-bar * guest stdin leak fix * allow floating panes to be fullscreen * make nested session handling configurable * protocol reannounce * refactor: naming confusion * improve ux somewhat * rustfmt * merge fixes * improve integration test rate limiting (fix ci) * add pr
* Title frames (#5318)Aram Drevekenin2026-06-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * initial implementation * improve single line title design * hint text plugin infra * add proto regen task * display alt text in status bar * don't display text with full frames * style alt hover text * fix + style control own pane hover * make multiplayer indications responsive * update snapshots * plugin api * single pane as tab * better scroll styling * fix tests * tab-bar plus hover * plugin surface tests * integration tests * adjust all integration tests * reduce hover noise * add pane id to in place * more integration tests * style: rustfmt * update plugin assets * update assets and snapshots * add pr
* feat(panes): `FocusLastPane` action (#4241)DawidPietrykowski2026-06-25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * feat(panes): focus last pane (#4142) * add tests * add client id parameter to `add_floating_pane` * add action for floating panes * add tests for floating panes * add keybinds to configs * rebase fixes * simplify focus logic when adding stacked pane * formatting fix * formatting fix 2 * fix: make sure expanding pane always focuses it for all clients in stack * add pr * fix rebase --------- Co-authored-by: Aram Drevekenin <aram@poor.dev>
* Mobile layout (#5241)Aram Drevekenin2026-06-10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * phase 2+3 * initial mobile plugin (phases 4 + 5 + 6) * some bugfixes * implement stage 7 * mobile interface improvements * improve mobile pane/tab picker * more ui improvements and fixes * various mobile fixes * some mobile fixes * mobile keyboard triggering fix * some cleanups * initial plugin keyboard implementation * improve keyboard hit zones * improve keyboard * pinch resize * pinching fixes * implement scrolling + panning * initial keyboard resizing * fix: mobile first render issues * keyboard responsiveness * fix some issues with the keyboard * implement fit * fix mobile scrolling * add tilde cell to compact * improve mobile keyboard * fix starting mobile terminal font to natural keyboard size * fix mobile resize blank screen flash * shadow focus for mobile * more keyboard fixes * improve mobile status bar * fix: render plugins to mobile clients * allow opening new panes/tabs from the mobile plugin * allow configuring * fix dot slash * improve mobile native soft keyboard interaction * switch to native keyboard - initial implementation * dirty hacks, but phone keyboard works now * adjust keyboard help strip * fix cursor in mobile * fix keyboard and help line * rename keyboard to modifier bar * allow starting new session * make clicking session name bring us to new session menu * allow switching to desktop from the mobile ui * mobile welcome screen initial implementation * mobile welcome screen fixes * various welcome screen fixes * more mobile fixes * moar mobile fixes * mobile ui redesign * fix: swallowed backspace bug * switch session screen improvement * touch up some interfaces * refactor: move mobile state to own struct * refactor: move fit resizing to the server * remove dead flash code * some cleanups * refactor: clear fit on close * refactor: improve fit api * refactor: pane sync functions * refactor: mouse * refactor: break into modules * some cleanups for the sessions screen * fix scrolling and other stuff * refactor: sessions screen * refactor: panes screen * refactor: new session screen * refactor: menu screen * refactor: render * cleanups * refactor: plugin structure * remove unused welcome screen path * Rename api * refactor input.js * cleanup useless comments * fit/mobile refactoring * take out the garbage * remove more comments * remove mobile flash on startup/attach * fix: various mobile startup issues * fix space bugs * cleanup * rustfmt * fix windows build * adjust modifier bar ui * add pr
* Welcome screen del fix (#5160)Aram Drevekenin2026-05-12
| | | | | | | * fix: session-manager delete * rustfmt * add pr
* fix: incorrect interpretation of unicode keys (#5110)Ivan Smoliakov2026-04-29
|
* Support CSI2031 (dark/light theme switching) + remove STDIN cache to avoid ↵Aram Drevekenin2026-04-28
| | | | | | | | | | | | | | | | | | | | | | | | | | | stale color queries (#5105) * initial phase 1 implementation * various fies * some fixes * fix fragmented stdin cases * some fixes * renaming * guard against old clients * implement csi 2031 * allow toggling theme with actions * rustfmt * fix: finalize trailing characters in ansi parser as well * add pr
* Some idle CPU and disk/io fixes, new CommandChanged plugin Event (#5063)Aram Drevekenin2026-04-17
| | | | | | | | | | | | | | | * initial implementation * fix mouse cpu spam * prevent excess metadata write and prune sessions on startup * consolidate CommandChanged and PaneCommandChanged * tests * rustfmt * add pr
* fix: performance regression when switching tabs (#4918)Aram Drevekenin2026-03-26
| | | | | | | | | | | | | | | | | | | * remove pane render report * only collect pane contents if pane should render * add logs * conditional keybinding serialization * remove logs * rustfmt * fix tests * tests * rustfmt
* Add windows support in strider (#4854)divens2026-03-21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Add Windows path support to strider plugin Strider is a WASM plugin where std::path::PathBuf uses Unix semantics, so Windows backslash paths break parent(), join(), strip_prefix(), etc. Fix by normalizing all incoming paths to forward slashes at plugin boundaries and converting back to native backslashes for display. A new platform.rs module detects the host OS at runtime from the initial_cwd format and centralizes all platform-aware logic. Also fix scan_host_folder on the server side: canonicalize plugin_host_folder so the starts_with security check works on Windows, where canonicalize() adds the \?\ prefix. * Add virtual root view for drive and WSL navigation in strider On Windows, navigating up from a drive root (e.g. C:\) now shows a "Computer" view listing all available drives and WSL distributions. Drives are enumerated via GetLogicalDriveStringsW and WSL distros via `wsl -l -q` (since read_dir on \wsl.localhost fails with OS error 64/67). Drive letters appear before WSL entries, which are displayed as "Ubuntu-24.04 (WSL)" etc. On non-Windows platforms, behavior is unchanged. * Rename ListHostEntries to ListWindowsVolumes and gate behind FullHdAccess The old name was misleading — on Unix it just returned `/`, which isn't "listing host entries." Rename to ListWindowsVolumes to make the Windows-specific intent explicit, gate behind FullHdAccess permission, and replace the Unix implementation with a log::error since the command is not meaningful on non-Windows platforms. * Replace direct array indexing with .get() in strider platform detection Use safe accessors and matches! macro instead of direct byte indexing for bounds-checked access patterns. --------- Co-authored-by: divens <divens.dev@gmail.com>
* Release notes and fixes (#4879)Aram Drevekenin2026-03-20
| | | | | | | | | * release notes * fix some link issues * rustfmt * fix click wording
* Ansi dump support (#4848)Aram Drevekenin2026-03-14
| | | | | | | | | | | | | | | | | | | | | * add missing pane-id and tab-id arguments to all cli actions * rustfmt * dump-screen --ansi * edit-scrollback --ansi * add --ansi to subscribe * add tests for ansi additions * add ansi render report to plugins * rustfmt * fix e2e tests * add pr
* Highlight priority levels (#4827)Aram Drevekenin2026-03-10
| | | | | * highlight priority levels * add pr
* Single screen session manager (#4821)Aram Drevekenin2026-03-09
| | | | | | | | | | | | | | | | | | | | | | | * initial implementation * adjust ui * preserve selection * width responsiveness * behavior fixes * fix: do not count suppressed panes * ui improvements * multi-screen config and prompt hint * tests + refactoring * improve performance * add pr
* Fix partial sequences parsing (#4623)Jonathan Giannuzzi2026-03-06
| | | | | | | | | | | | | | | | | * Vendor termwiz 0.23.3 * Apply fix for parsing partial SGR mouse sequences to termwiz * Add stdin debounce for fragmented input handling Parse with maybe_more=true and send events immediately. Finalization is deferred until 50ms of inactivity, avoiding latency while correctly handling escape sequences split across multiple reads (e.g., over SSH). * Remove unused parts of termwiz --------- Co-authored-by: Aram Drevekenin <aram@poor.dev>
* fix: click links with alt-click (#4809)Aram Drevekenin2026-03-06
|
* Click file paths to open in editor, allow plugins to change styles and hover ↵Aram Drevekenin2026-03-05
| | | | | | | | | | | | | | | | | | | | | | | | | | | of viewport (#4802) * initial implementation * implement hover * add styles and foreground/background emphases, fix upsert hover collisions * initial implementation of link plugin * suppress highlighting + clicks on unfocused and mouse mode panes * also make italic * fix env var and ~ expansions * fix regex issues and add defensive validations * fix tests and add plugin assets * add tests * refactor all the things * add pr
* feat: add per-pane default foreground/background color support (#4737)Karl Bunch2026-03-04
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * feat: add per-pane default foreground/background color support Add per-pane default foreground and background colors, configurable through four surfaces: - OSC 10/11 escape sequences (set/query) and OSC 110/111 (reset) - CLI action: zellij action set-pane-color --pane-id <id> --fg/--bg - Layout config: default_fg/default_bg properties on pane nodes - Plugin API: set_pane_color() SDK function and PaneInfo read-side Colors are applied at render time as a fallback when cells have no explicit foreground/background, without mutating stored grid data. Closes #4712 * refactor: address review feedback for pane color support - Extract adjust_styles_for_custom_bg_fg in output/mod.rs - Move color parsing (xparse_color) from tab into Grid - Move color serialization (ansi_code_to_color_string) into Grid - Pane trait now uses Option<String> for color getters/setters - Add colors to session serialization (dump-layout) - Add default_fg/default_bg to protobuf IPC schema to fix layout-in-session color loss - Update example layout with tab-bar and status-bar * fix stack overflow and ordering * rustfmt * update e2e snapshots --------- Co-authored-by: Karl Bunch <karl@karlbunch.com> Co-authored-by: Aram Drevekenin <aram@poor.dev>
* Visual indication for terminal bells (#4781)Aram Drevekenin2026-03-03
| | | | | | | | | | | | | * initial implementation * rustfmt * allow disabling with config * add missing kdl * add pr * fix tests
* Atomic show hide floating panes (#4764)Aram Drevekenin2026-02-25
| | | | | | | * atomic show/hide floating panes (cli, plugin API, keybinding) * rustfmt * add pr
* Revamp the sequence plugin (#4713)Aram Drevekenin2026-02-17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * initial pipe interface * initial open_x_pane_in_new_tab plugin api implementation * fix: send CommandPaneExited for command panes opened as initial_terminals in new tabs from plugins * open panes in place of specific pane id plugin commands - initial implementation * sequence and plugin api ux * focus self when starting * combine ui and pipe paths * highlight selected pane * initial sequence removal of editing capabilities * fix: translate plugin paths in api * properly trim commands when loaded from pipe or pasted text * minor ux fixes * get ctrl c to work properly * behavior fixes * more ui fixes * fix tests * close tab and end sequence properly * default to AND * zellij sequence alias * add alias * consolidate paths * rustfmt * fix ui padding * add pr * fix: reset scrolling when resetting terminal state
* Allow plugins to read env vars present upon session creation (behind ↵Aram Drevekenin2026-02-12
| | | | | | | | | permission) (#4700) * allow plugins to read environment variables * rustfmt * add pr
* Propagate plugin alias config changes to plugins (#4698)Aram Drevekenin2026-02-12
| | | | | | | * propagate plugin alias config changes to plugins * rustfmt * add pr
* CLI commands to query tab ids, names and info (#4695)Aram Drevekenin2026-02-11
| | | | | | | | | * add equivalent tab_id commands to tab_position commands (cli and plugins) * add list-tabs and current-tab-info cli commands * rustfmt * add pr
* Improve plugin API ergonomics and add CLI goodies (#4690)Aram Drevekenin2026-02-10
| | | | | | | | | | | | | | | | | | | | | | | * return created tab_id from relevant plugin API methods * add to cli * return Option<PaneId> in plugin API methods that create panes * return created pane_id in cli for relevant actions * allow plugins to query for pane_info based on pane_id * allow plugins to query for TabInfo by its id * allow plugins to query pane command/cwd synchronously * zellij action list-panes * style and formatting * fix e2e tests * add pr
* refactor: make tab_ids stable (#4685)Aram Drevekenin2026-02-06
| | | | | | | | | | | * refactor: clear distinction between tab_id and tab_position * add tab_id to TabInfo * add test * rustfmt * add pr
* fix(layout): Correctly handle relative layout paths from CLI (#4664)har7an2026-01-31
| | | | | | | | | | | | | | * fix(layout): Correctly handle relative layout paths from CLI since previously they were treated the same as layout names given from the config file. This was incorrect, since layouts on the CLI require resolution relative to the CWD, whereas the config "knows" only the default layout directory for this purpose. Add a regression test to catch behavior like this in the future. * test: Fix test assertions so they don't rely on fragile information such as absolute paths or timestamps.
* Allow saving current session for resurrection on demand (#4654)Aram Drevekenin2026-01-29
| | | | | | | | | | | * initial implementation * session manager ui and plugin api * remove event * cleanups and formatting * add pr
* Fix loading short layouts and custom config dirs (#4640)har7an2026-01-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * test(e2e): Write a regression test to validate behavior w.r.t. loading and determining the session layout file. Compared to release v0.43.1, right now there is a problem where `zellij --layout NAME`, with `NAME` being the filename of a layout in the `$ZELLIJ_DATA_DIR/layouts/` subfolder, doesn't resolve to the correct layout file. Instead, zellij will silently fall back to its default config. It does, however, recognize nonexistent configuration files. * fix(utils): Treat bare layout names as file paths first before defaulting to builtin layout names. This restores the behavior shown by zellij up until 0.43.1, which was altered in a recent extension of the server-client API. * fix: Forward client layout information to startup code and ensure it isn't ignored along the way. Update a bunch of tests to react to the new function interface and write new tests to catch deviations in the future. * test: Fix misconfigured e2e test to ensure that a custom `--config-dir` is indeed respected during application startup. * test: Rewrite config dir test to not use variable metadata since the `LayoutInfo` object previously dumped into the snapshot contains information that isn't platform agnostic, including an absolute file path and a timestamp. * docs(CHANGELOG): Mention PR #4640 in the list of changes for the next release. * fix: Handle bare layouts with name "default" correctly and assume a "default" layout if no layout name is given during layout resolution. "default" may not always be the builtin default layout, since users can write their own "default.kdl" that takes effect when placed in the layout directory an no other layouts are explicitly specified.
* fix: sessions started from the session manager will now default to the ↵Aram Drevekenin2026-01-22
| | | | | | | | | | | | | current cwd (#4625) * fix: properly set cwd for new sessions and filepicker when none is specified * rustfmt * add pr * fix tests * rustfmt
* Fix tiled new pane borderless (#4618)Aram Drevekenin2026-01-20
| | | | | | | | | * allow opening tiled pane as borderless * rustfmt * add pr * fix e2e tests
* Allow toggling pane frames for specific panes (#4615)Aram Drevekenin2026-01-20
| | | | | | | | | | | | | | | | | | | | | | | | | * initial implementation * add borderless to edit and plugin cli commands * allow changing existing pane to borderless * allow setting in layouts and keybindings * get to work through the existing plugin api commands * fix when applying (swap) layouts * fix tests * allow setting pane as bordered (borderful?) when changing its coordinates (eg. through the cli) * allow explicitly setting a pane as bordered or borderless without toggling * rustfmt * adjust snapshots * docs(changelog): add PR
* Fix: allow 0% coordinate specification for floating panes (#4609)Aram Drevekenin2026-01-15
| | | | | | | * fix: allow 0% when specifying x/y coordinates but not when specifying width/height * rustfmt * add pr
* Layout manager (#4601)Aram Drevekenin2026-01-13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * ad plugin override_layout command * watch available layouts and report to plugin thread * Event::AvailableLayoutInfo * save layout command * refactor save_layout * fix api and permissions * delete_layout API command * edit layout in place API * edit layout response through api * propagate layout parsing errors * generate random name plugin api * dump stringified layout API * synchronicity dump_session_layout and include LayoutMetadata in sync response * parse_layout plugin api * fix: default to default layout dir in plugin api * rename_layout plugin command API * initial implementation of multi-tab override * apply to multiple tabs * allow applying to only the active tab * add tab/pane names to layout parsing metadata * indicate is_builtin_plugin in PaneMetadata * get_layout_dir plugin API command * consolidate (and fix) is_builtin_plugin logic * fix bad ret * allow dumping the layout for just a specific tab in the session * get focused pane and tab sync api * color last substring variants * add plugin * update assets * fix tests * add e2e tests for new plugin apis * update ui with new shortcut * rustfmt * docs(changelog): add PR
* Read only web tokens (#4551)Aram Drevekenin2025-12-16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * initial implementation * fix race condition potentially allowing read-only clients to gain write access * clear read only status on disconnect * cleanups and formatting * fix tests * refactor first message + ipc pipe creation out of trait * fix web tests * fix web tests again * refactor: move os_input connection establishment to session manager trait * add read-only-token tests * fix: identify watchers as web-clients when they are and enforce no-web-clients for them as well * identify watcher web clients on the server * fix tests * cleanups and formatting * add read-only-tokens to the plugin api * update share plugin ui for read-only tokens * cleanups and formatting * docs(changelog): add PR
* Command Sequences, Blocking CLI Commands, New Plugin APIs (#4546)Aram Drevekenin2025-12-11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * fix deadlocks * initial logging to plugin implementation * only log actions, include keys as Action::Write * send actions to plugins as a new Event::UserAction * add Action::Write metadata to plugin API * run_action plugin command and permission * block-until-exit/success/failure in the cli * fix tests * add NewBlockingPane to the plugin API * run actions in a different thread so as not to block plugin execution * add custom context to run_action flow * fixes for floating pane z_index sorting and unknown protobuf events not crashing plugins * always render the frame of pinned panes * macros module * add macros to config * KDL macro parsing * thread macros to the plugin thread * get/update/delete macros from plugins * add macros updated event * add ability to rename macros * fix deadlock * fix z_indices with pinned unselectable panes bug * allow plugins to opt-in to show_cursor * change show_cursor(bool) to show_cursor(Option<coordinates>) * test * upgrade strum to allow proper enum iteration and case insensitive fromstr implementation for Actions * better cwd tracking for exited command panes * add initial panes to NewTab Action (no-op for now) * add initial panes to cli newtab action * make initial panes work * add close_on_exit and start_suspended to the cli * properly convert newtab action (potentially breaking for plugins) * allow first pane in tab to block * make distinction between opening panes with the last active client and the current pane in the cli * improve some apis * fix fixed size closing pane bug * fix plugin cursor coordinates * fix issues with launch-or-focus-plugin having to do with in-place and suppressed panes * fix: set affected pane in replace pane * allow setting a pane in place when it is suppressed and being focused (tests failing) * fix tests * some fixes for unsuppression of panes * some fixes for completion_tx in new tab action * allow replace/suppressed rather than replace/close only * fixes for inserting suppressed panes without conflicts * pane focus history and explicit unsuppress plugin api * fix: prefer explicitly provided pane id in placement in actions that provide it * fix floating pane coordinate change and z_index * fix plugin cache dir to persist between sessions * fix some stack focusing issues when adding panes without a client id * make sure pinned panes are always above non-pinned floating panes * allow plugins to copy to clipboard * initial text styling additions implementations - buggy * fix dimming issues * remove extras, stay with just dim and unbold * unbold and dim as indices * allow ui components to render success/error colors * allow plugins to send sigint/sigkill to processes, as well as receive their pid * ui component fixes and send originating plugin context with run_action * originating plugin for new tab too * make plugn show pane command also expand stacked panes * add sequence plugin * remove macros * remove snapshots * add shortcut for sequence plugin * fix plugin tests * plugin tests for new apis * update snapshots for sequence shortcut * cleanups and formatting * Event::CwdChanged * moar cleanups * remove some warnings * remove mroe warnings * remove dead code * remove more dead code * fix minor bugs in sequence plugin * update sequence help and format codebase * fix conditional blocking cli commands * fix initial-command new-tab cli flag * add client_ids to CwdChanged Event * last fixes * add pr
* Improve CLI interface (add detach, switch session, blocking zellij run and ↵Aram Drevekenin2025-10-31
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | more) (#4492) * initial implementation * fix tests * move more ations * screen adjustments (does not compile) * plugin/pty adjustments (does not compile) * more progress, tests not compiling * fix tests * more progress, does not compile * more progress, compiles, tests failing * tests passing * rely on Drop for NotificationEnd * atomicify some more actions * fmt * move unblock input to route * don't pass hard-coded action names * refactor: consolidate completion channel logic * comment * track last active client * track cli clients explicitly * use the last active client for cli commands when possible * fix focus-next-pane for multiple clients * various fixes for multi-client actions from the cli * add detach action * fix tests * add switch session cli action * fix switch session deadlock * add and layout to switch-session cli action * blocking zellij run: add NewBlockingPane variant * blocking zellij run: wait forever mechanism * blocking zellij run: thread NotificationEnd as needed until TerminalPane (tests not compiling) * blocking zellij run: update cli * blocking zellij run: add command * blocking zellij run: finish implementation * fix tests * e2e test and close_on_exit fixes * fmt * fix e2e tests * fix plugin tests
* Feature: watcher clients (#4487)Aram Drevekenin2025-10-27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * initial implementation * make Screen aware of watchers - slightly buggy * fix: only render focused tab to watcher * track watcher sizes on Screen * pad watcher output as needed * truncate watcher sizes as needed * render on watcher resize * fix watcher first resize * allow watchers to quit with esc, ctrl-c or ctrl-q * fix: send watcher size on initial connection message * refactor: move padding logic to output * fix watcher rendering after all clients disconnected * fix watcher rendering caching issues * crop watcher cursor when out of bounds * remove watchers cleanly on session close * standardize watcher messages ignoring * add e2e test for watcher functionality * update snapshots * add output tests * rustfmt * cleanups * docs(changelog): add PR
* Give plugins the ability to read pane contents (#4465)Aram Drevekenin2025-10-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * initial implementation * add Selection and better types * event to plugins * only send viewport diff * simplify data structure sent over the wire * cleanups * rustfmt * remove dead code and fix tests * commit tests * get pane scrollback plugin command without implementation * add lines above and lines below to pane contents * implement get scrollback plugin command * selection => selected_text * add convenience method to get the selected text * move Selection back to zellij-server * rustfmt * bring back client id to get contents command * rustfmt * add pr