aboutsummaryrefslogtreecommitdiffstats
path: root/zellij-server/src/panes/unit/grid_tests.rs
diff options
context:
space:
mode:
authorKarl Bunch <karlbunch@users.noreply.github.com>2026-03-04 08:26:26 -0500
committerGitHub <noreply@github.com>2026-03-04 14:26:26 +0100
commitf77cdec298576164f34baf0456e4b986bde269e6 (patch)
treeaf9fb93cfc6edf465378952c0aff38d48820e142 /zellij-server/src/panes/unit/grid_tests.rs
parentdocs(changelog): native Windows support (!!) (diff)
downloadsl0ppij-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.rs194
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"
+ );
+ }
+}