diff options
| author | Karl Bunch <karlbunch@users.noreply.github.com> | 2026-03-04 08:26:26 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-04 14:26:26 +0100 |
| commit | f77cdec298576164f34baf0456e4b986bde269e6 (patch) | |
| tree | af9fb93cfc6edf465378952c0aff38d48820e142 /zellij-server/src/panes/unit/grid_tests.rs | |
| parent | docs(changelog): native Windows support (!!) (diff) | |
| download | sl0ppij-f77cdec298576164f34baf0456e4b986bde269e6.tar.gz sl0ppij-f77cdec298576164f34baf0456e4b986bde269e6.tar.xz sl0ppij-f77cdec298576164f34baf0456e4b986bde269e6.zip | |
feat: add per-pane default foreground/background color support (#4737)
* 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>
Diffstat (limited to '')
| -rw-r--r-- | zellij-server/src/panes/unit/grid_tests.rs | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/zellij-server/src/panes/unit/grid_tests.rs b/zellij-server/src/panes/unit/grid_tests.rs index 310c3dc4..e942caa8 100644 --- a/zellij-server/src/panes/unit/grid_tests.rs +++ b/zellij-server/src/panes/unit/grid_tests.rs @@ -4362,3 +4362,197 @@ fn single_click_drag_selection_preserved_after_scroll() { assert_eq!(grid.selection.start.column, start_before.column); assert_eq!(grid.selection.end.column, end_before.column); } + +#[test] +fn osc_11_set_and_query_pane_default_bg() { + use crate::panes::terminal_character::AnsiCode; + + let mut vte_parser = vte::Parser::new(); + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let mut grid = Grid::new( + 10, + 20, + Rc::new(RefCell::new(Palette::default())), + terminal_emulator_color_codes, + Rc::new(RefCell::new(LinkHandler::new())), + Rc::new(RefCell::new(None)), + sixel_image_store, + Style::default(), + false, + true, + true, + true, + false, + ); + + // Set background via OSC 11 + let set_bg = b"\x1b]11;#001a3a\x07"; + for byte in set_bg.iter() { + vte_parser.advance(&mut grid, *byte); + } + + assert_eq!(grid.pane_default_bg, Some(AnsiCode::RgbCode((0, 26, 58)))); + + // Query background via OSC 11 + let query_bg = b"\x1b]11;?\x07"; + for byte in query_bg.iter() { + vte_parser.advance(&mut grid, *byte); + } + + assert_eq!(grid.pending_messages_to_pty.len(), 1); + let response = String::from_utf8(grid.pending_messages_to_pty[0].clone()).unwrap(); + assert!( + response.contains("11;rgb:0000/1a1a/3a3a"), + "Response was: {}", + response + ); +} + +#[test] +fn osc_10_set_and_query_pane_default_fg() { + use crate::panes::terminal_character::AnsiCode; + + let mut vte_parser = vte::Parser::new(); + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let mut grid = Grid::new( + 10, + 20, + Rc::new(RefCell::new(Palette::default())), + terminal_emulator_color_codes, + Rc::new(RefCell::new(LinkHandler::new())), + Rc::new(RefCell::new(None)), + sixel_image_store, + Style::default(), + false, + true, + true, + true, + false, + ); + + // Set foreground via OSC 10 + let set_fg = b"\x1b]10;#00e000\x07"; + for byte in set_fg.iter() { + vte_parser.advance(&mut grid, *byte); + } + + assert_eq!(grid.pane_default_fg, Some(AnsiCode::RgbCode((0, 224, 0)))); + + // Query foreground via OSC 10 + let query_fg = b"\x1b]10;?\x07"; + for byte in query_fg.iter() { + vte_parser.advance(&mut grid, *byte); + } + + assert_eq!(grid.pending_messages_to_pty.len(), 1); + let response = String::from_utf8(grid.pending_messages_to_pty[0].clone()).unwrap(); + assert!( + response.contains("10;rgb:0000/e0e0/0000"), + "Response was: {}", + response + ); +} + +#[test] +fn osc_110_111_reset_pane_default_colors() { + use crate::panes::terminal_character::AnsiCode; + + let mut vte_parser = vte::Parser::new(); + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let mut grid = Grid::new( + 10, + 20, + Rc::new(RefCell::new(Palette::default())), + terminal_emulator_color_codes, + Rc::new(RefCell::new(LinkHandler::new())), + Rc::new(RefCell::new(None)), + sixel_image_store, + Style::default(), + false, + true, + true, + true, + false, + ); + + // Set both fg and bg + let set_fg = b"\x1b]10;#00e000\x07"; + for byte in set_fg.iter() { + vte_parser.advance(&mut grid, *byte); + } + let set_bg = b"\x1b]11;#001a3a\x07"; + for byte in set_bg.iter() { + vte_parser.advance(&mut grid, *byte); + } + + assert_eq!(grid.pane_default_fg, Some(AnsiCode::RgbCode((0, 224, 0)))); + assert_eq!(grid.pane_default_bg, Some(AnsiCode::RgbCode((0, 26, 58)))); + + // Reset foreground via OSC 110 + let reset_fg = b"\x1b]110\x07"; + for byte in reset_fg.iter() { + vte_parser.advance(&mut grid, *byte); + } + assert_eq!(grid.pane_default_fg, None); + assert_eq!(grid.pane_default_bg, Some(AnsiCode::RgbCode((0, 26, 58)))); + + // Reset background via OSC 111 + let reset_bg = b"\x1b]111\x07"; + for byte in reset_bg.iter() { + vte_parser.advance(&mut grid, *byte); + } + assert_eq!(grid.pane_default_fg, None); + assert_eq!(grid.pane_default_bg, None); +} + +#[test] +fn osc_11_set_bg_produces_ansi_in_render_output() { + use crate::panes::terminal_character::AnsiCode; + + let mut vte_parser = vte::Parser::new(); + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let mut grid = Grid::new( + 5, + 10, + Rc::new(RefCell::new(Palette::default())), + terminal_emulator_color_codes, + Rc::new(RefCell::new(LinkHandler::new())), + Rc::new(RefCell::new(None)), + sixel_image_store, + Style::default(), + false, + true, + true, + true, + false, + ); + + // Set background via OSC 11 + let set_bg = b"\x1b]11;#001a3a\x07"; + for byte in set_bg.iter() { + vte_parser.advance(&mut grid, *byte); + } + + assert_eq!(grid.pane_default_bg, Some(AnsiCode::RgbCode((0, 26, 58)))); + + // Render the grid and check that the pane defaults are stamped on chunks + let style = Style::default(); + let render_result = grid.render(0, 0, &style).unwrap(); + assert!(render_result.is_some(), "Expected render output"); + + let (chunks, _, _) = render_result.unwrap(); + assert!(!chunks.is_empty(), "Expected at least one character chunk"); + + // All chunks should carry the pane default bg + for chunk in &chunks { + assert_eq!( + chunk.pane_default_bg, + Some(AnsiCode::RgbCode((0, 26, 58))), + "Chunk should carry pane default background" + ); + } +} |
