summaryrefslogtreecommitdiffstats
path: root/default-plugins
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2023-02-17 12:05:50 +0100
committerGitHub <noreply@github.com>2023-02-17 12:05:50 +0100
commitf1ff272b0b65f6d328fef24531ada67ea585ce85 (patch)
tree0e33aa4a1af21c0785fa30118ee42a21972497cf /default-plugins
parent1517036c2489b2a7dc43230e2ba8a05841a69dbe (diff)
feat(ui): swap layouts and stacked panes (#2167)
* relayout working with hard coded layout * work * refactor(layout): PaneLayout => TiledPaneLayout * tests passing * tests passing * tests passing * stacked panes and passing tests * tests for stacked panes * refactor(panes): stacked panes * fix: focusing into stacked panes from the left/right * fix(layouts): handle stacked layouts in the middle of the screen * fix(pane-stack): focus correctly when coming to stack from above/below * fix(stacked-panes): resize stack * fix(stacked-panes): focus with mouse * fix(stacked-panes): focus next pane * fix(layout-applier): sane focus order * fix(stacked-panes): better titles for one-liners * fix(stacked-panes): handle moving pane location in stack * fix(relayout): properly calculate display area * fix(relayout): properly calculate rounding errors * fix(stacked-panes): properly handle closing a pane near a stack * fix(swap-layouts): adjust swap layout sort order * feat(swap-layouts): ui + ux * fix(swap-layouts): include base layout * refactor(layout): remove unused method * fix(swap-layouts): respect pane contents and focus * work * fix(swap-layouts): load swap layouts from external file * fix(swap-layouts): properly truncate layout children * fix(stacked-panes): allow stacked panes to become fullscreen * fix(swap-layouts): work with multiple tabs * fix(swap-layouts): embed/eject panes properly with auto-layout * fix(stacked-panes): close last pane in stack * fix(stacked-panes): move focus for all clients in stack * fix(floating-panes): set layout damaged when moving panes * fix(relayout): move out of unfitting layout when resizing whole tab * fix(ui): background color for swap layout indicator * fix(keybinds): add switch next layout in tmux * fix(ui): swap layout indication in compact layout * fix(compact): correct swap constraint * fix(tests): tmux swap config shortcut * fix(resizes): cache resizes so as not to confuse panes (eg. vim) with multiple resizes that it debounces weirdly * feat(cli): dump swap layouts * fix(ui): stacked panes without pane frames * fix(ux): move pane forward/backwards also with floating panes * refactor(lint): remove unused stuff * refactor(tab): move swap layouts to separate file * style(fmt): rustfmt * style(fmt): rustfmt * refactor(panes): various cleanups * chore(deps): upgrade termwiz to get alt left-bracket * fix(assets): merge conflicts of binary files * style(fmt): rustfmt * style(clippy): no thank you! * chore(repo): remove garbage file
Diffstat (limited to 'default-plugins')
-rw-r--r--default-plugins/compact-bar/src/line.rs94
-rw-r--r--default-plugins/compact-bar/src/main.rs6
-rw-r--r--default-plugins/status-bar/src/first_line.rs146
-rw-r--r--default-plugins/status-bar/src/main.rs103
-rw-r--r--default-plugins/status-bar/src/second_line.rs7
-rw-r--r--default-plugins/status-bar/src/tip/data/compact_layout.rs8
-rw-r--r--default-plugins/status-bar/src/tip/data/edit_scrollbuffer.rs8
-rw-r--r--default-plugins/status-bar/src/tip/data/floating_panes_mouse.rs3
-rw-r--r--default-plugins/status-bar/src/tip/data/move_focus_hjkl_tab_switch.rs4
-rw-r--r--default-plugins/status-bar/src/tip/data/quicknav.rs8
-rw-r--r--default-plugins/status-bar/src/tip/data/sync_tab.rs8
11 files changed, 350 insertions, 45 deletions
diff --git a/default-plugins/compact-bar/src/line.rs b/default-plugins/compact-bar/src/line.rs
index 16d64d071..6bcbab18a 100644
--- a/default-plugins/compact-bar/src/line.rs
+++ b/default-plugins/compact-bar/src/line.rs
@@ -256,6 +256,8 @@ pub fn tab_line(
palette: Palette,
capabilities: PluginCapabilities,
mode: InputMode,
+ active_swap_layout_name: &Option<String>,
+ is_swap_layout_dirty: bool,
) -> Vec<LinePart> {
let mut tabs_after_active = all_tabs.split_off(active_tab_index);
let mut tabs_before_active = all_tabs;
@@ -283,5 +285,97 @@ pub fn tab_line(
capabilities,
);
prefix.append(&mut tabs_to_render);
+
+ let current_title_len = get_current_title_len(&prefix);
+ if current_title_len < cols {
+ let mut remaining_space = cols - current_title_len;
+ if let Some(swap_layout_status) = swap_layout_status(
+ remaining_space,
+ active_swap_layout_name,
+ is_swap_layout_dirty,
+ mode,
+ &palette,
+ tab_separator(capabilities),
+ ) {
+ remaining_space -= swap_layout_status.len;
+ let mut buffer = String::new();
+ for _ in 0..remaining_space {
+ buffer.push_str(&style!(palette.black, palette.black).paint(" ").to_string());
+ }
+ prefix.push(LinePart {
+ part: buffer,
+ len: remaining_space,
+ tab_index: None,
+ });
+ prefix.push(swap_layout_status);
+ }
+ }
+
prefix
}
+
+fn swap_layout_status(
+ max_len: usize,
+ swap_layout_name: &Option<String>,
+ is_swap_layout_damaged: bool,
+ input_mode: InputMode,
+ palette: &Palette,
+ separator: &str,
+) -> Option<LinePart> {
+ match swap_layout_name {
+ Some(swap_layout_name) => {
+ let mut swap_layout_name = format!(" {} ", swap_layout_name);
+ swap_layout_name.make_ascii_uppercase();
+ let swap_layout_name_len = swap_layout_name.len() + 3;
+
+ let (prefix_separator, swap_layout_name, suffix_separator) =
+ if input_mode == InputMode::Locked {
+ (
+ style!(palette.black, palette.fg).paint(separator),
+ style!(palette.black, palette.fg)
+ .italic()
+ .paint(&swap_layout_name),
+ style!(palette.fg, palette.black).paint(separator),
+ )
+ } else if is_swap_layout_damaged {
+ (
+ style!(palette.black, palette.fg).paint(separator),
+ style!(palette.black, palette.fg)
+ .bold()
+ .paint(&swap_layout_name),
+ style!(palette.fg, palette.black).paint(separator),
+ )
+ } else {
+ (
+ style!(palette.black, palette.green).paint(separator),
+ style!(palette.black, palette.green)
+ .bold()
+ .paint(&swap_layout_name),
+ style!(palette.green, palette.black).paint(separator),
+ )
+ };
+ let swap_layout_indicator = format!(
+ "{}{}{}",
+ prefix_separator, swap_layout_name, suffix_separator
+ );
+ let (part, full_len) = (format!("{}", swap_layout_indicator), swap_layout_name_len);
+ let short_len = swap_layout_name_len + 1; // 1 is the space between
+ if full_len <= max_len {
+ Some(LinePart {
+ part,
+ len: full_len,
+ tab_index: None,
+ })
+ } else if short_len <= max_len && input_mode != InputMode::Locked {
+ Some(LinePart {
+ part: swap_layout_indicator,
+ len: short_len,
+ tab_index: None,
+ })
+ } else {
+ None
+ }
+ },
+ None => None,
+ }
+}
diff --git a/default-plugins/compact-bar/src/main.rs b/default-plugins/compact-bar/src/main.rs
index c6ce9bbcd..71652f0f1 100644
--- a/default-plugins/compact-bar/src/main.rs
+++ b/default-plugins/compact-bar/src/main.rs
@@ -92,6 +92,8 @@ impl ZellijPlugin for State {
}
let mut all_tabs: Vec<LinePart> = vec![];
let mut active_tab_index = 0;
+ let mut active_swap_layout_name = None;
+ let mut is_swap_layout_dirty = false;
let mut is_alternate_tab = false;
for t in &mut self.tabs {
let mut tabname = t.name.clone();
@@ -102,6 +104,8 @@ impl ZellijPlugin for State {
active_tab_index = t.position;
} else if t.active {
active_tab_index = t.position;
+ is_swap_layout_dirty = t.is_swap_layout_dirty;
+ active_swap_layout_name = t.active_swap_layout_name.clone();
}
let tab = tab_style(
tabname,
@@ -121,6 +125,8 @@ impl ZellijPlugin for State {
self.mode_info.style.colors,
self.mode_info.capabilities,
self.mode_info.mode,
+ &active_swap_layout_name,
+ is_swap_layout_dirty,
);
let mut s = String::new();
let mut len_cnt = 0;
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs
index ae8fc70e7..23a6d5869 100644
--- a/default-plugins/status-bar/src/first_line.rs
+++ b/default-plugins/status-bar/src/first_line.rs
@@ -1,9 +1,11 @@
-use ansi_term::ANSIStrings;
+use ansi_term::{unstyled_len, ANSIStrings};
use zellij_tile::prelude::actions::Action;
use zellij_tile::prelude::*;
use crate::color_elements;
-use crate::{action_key, get_common_modifier, TO_NORMAL};
+use crate::{
+ action_key, action_key_group, get_common_modifier, style_key_with_modifier, TO_NORMAL,
+};
use crate::{ColoredElements, LinePart};
struct KeyShortcut {
@@ -232,6 +234,102 @@ fn key_indicators(
line_part
}
+fn swap_layout_keycode(mode_info: &ModeInfo, palette: &Palette) -> LinePart {
+ let mode_keybinds = mode_info.get_mode_keybinds();
+ let prev_next_keys = action_key_group(
+ &mode_keybinds,
+ &[&[Action::PreviousSwapLayout], &[Action::NextSwapLayout]],
+ );
+ let prev_next_keys_indicator =
+ style_key_with_modifier(&prev_next_keys, palette, Some(palette.black));
+ let keycode = ANSIStrings(&prev_next_keys_indicator);
+ let len = unstyled_len(&keycode);
+ let part = keycode.to_string();
+ LinePart { part, len }
+}
+
+fn swap_layout_status(
+ max_len: usize,
+ swap_layout_name: &Option<String>,
+ is_swap_layout_damaged: bool,
+ mode_info: &ModeInfo,
+ colored_elements: ColoredElements,
+ palette: &Palette,
+ separator: &str,
+) -> Option<LinePart> {
+ match swap_layout_name {
+ Some(swap_layout_name) => {
+ let mut swap_layout_name = format!(" {} ", swap_layout_name);
+ swap_layout_name.make_ascii_uppercase();
+ let keycode = swap_layout_keycode(mode_info, palette);
+ let swap_layout_name_len = swap_layout_name.len() + 3; // 2 for the arrow separators, one for the screen end buffer
+ //
+ macro_rules! style_swap_layout_indicator {
+ ($style_name:ident) => {{
+ (
+ colored_elements
+ .$style_name
+ .prefix_separator
+ .paint(separator),
+ colored_elements
+ .$style_name
+ .styled_text
+ .paint(&swap_layout_name),
+ colored_elements
+ .$style_name
+ .suffix_separator
+ .paint(separator),
+ )
+ }};
+ }
+ let (prefix_separator, swap_layout_name, suffix_separator) =
+ if mode_info.mode == InputMode::Locked {
+ style_swap_layout_indicator!(disabled)
+ } else if is_swap_layout_damaged {
+ style_swap_layout_indicator!(unselected)
+ } else {
+ style_swap_layout_indicator!(selected)
+ };
+ let swap_layout_indicator = format!(
+ "{}{}{}",
+ prefix_separator, swap_layout_name, suffix_separator
+ );
+ let (part, full_len) = if mode_info.mode == InputMode::Locked {
+ (
+ format!("{}", swap_layout_indicator),
+ swap_layout_name_len, // 1 is the space between
+ )
+ } else {
+ (
+ format!(
+ "{}{}{}{}",
+ keycode,
+ colored_elements.superkey_prefix.paint(" "),
+ swap_layout_indicator,
+ colored_elements.superkey_prefix.paint(" ")
+ ),
+ keycode.len + swap_layout_name_len + 1, // 1 is the space between
+ )
+ };
+ let short_len = swap_layout_name_len + 1; // 1 is the space between
+ if full_len <= max_len {
+ Some(LinePart {
+ part,
+ len: full_len,
+ })
+ } else if short_len <= max_len && mode_info.mode != InputMode::Locked {
+ Some(LinePart {
+ part: swap_layout_indicator,
+ len: short_len,
+ })
+ } else {
+ None
+ }
+ },
+ None => None,
+ }
+}
+
/// Get the keybindings for switching `InputMode`s and `Quit` visible in status bar.
///
/// Return a Vector of `Key`s where each `Key` is a shortcut to switch to some `InputMode` or Quit
@@ -351,7 +449,12 @@ fn get_key_shortcut_for_mode<'a>(
None
}
-pub fn first_line(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
+pub fn first_line(
+ help: &ModeInfo,
+ tab_info: Option<&TabInfo>,
+ max_len: usize,
+ separator: &str,
+) -> LinePart {
let supports_arrow_fonts = !help.capabilities.arrow_fonts;
let colored_elements = color_elements(help.style.colors, !supports_arrow_fonts);
let binds = &help.get_mode_keybinds();
@@ -432,7 +535,32 @@ pub fn first_line(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart
));
}
- key_indicators(max_len, &default_keys, colored_elements, separator, help)
+ let mut key_indicators =
+ key_indicators(max_len, &default_keys, colored_elements, separator, help);
+ if key_indicators.len < max_len {
+ if let Some(tab_info) = tab_info {
+ let mut remaining_space = max_len - key_indicators.len;
+ if let Some(swap_layout_status) = swap_layout_status(
+ remaining_space,
+ &tab_info.active_swap_layout_name,
+ tab_info.is_swap_layout_dirty,
+ help,
+ colored_elements,
+ &help.style.colors,
+ separator,
+ ) {
+ remaining_space -= swap_layout_status.len;
+ for _ in 0..remaining_space {
+ key_indicators.part.push_str(
+ &ANSIStrings(&[colored_elements.superkey_prefix.paint(" ")]).to_string(),
+ );
+ key_indicators.len += 1;
+ }
+ key_indicators.append(&swap_layout_status);
+ }
+ }
+ }
+ key_indicators
}
#[cfg(test)]
@@ -735,7 +863,7 @@ mod tests {
..ModeInfo::default()
};
- let ret = first_line(&mode_info, 500, ">");
+ let ret = first_line(&mode_info, None, 500, ">");
let ret = unstyle(ret);
assert_eq!(
@@ -759,7 +887,7 @@ mod tests {
..ModeInfo::default()
};
- let ret = first_line(&mode_info, 500, ">");
+ let ret = first_line(&mode_info, None, 500, ">");
let ret = unstyle(ret);
assert_eq!(
@@ -785,7 +913,7 @@ mod tests {
..ModeInfo::default()
};
- let ret = first_line(&mode_info, 500, ">");
+ let ret = first_line(&mode_info, None, 500, ">");
let ret = unstyle(ret);
assert_eq!(
@@ -812,7 +940,7 @@ mod tests {
..ModeInfo::default()
};
- let ret = first_line(&mode_info, 50, ">");
+ let ret = first_line(&mode_info, None, 50, ">");
let ret = unstyle(ret);
assert_eq!(ret, " Ctrl + >> a >> b >> c >> d >> e >".to_string());
@@ -833,7 +961,7 @@ mod tests {
..ModeInfo::default()
};
- let ret = first_line(&mode_info, 30, "");
+ let ret = first_line(&mode_info, None, 30, "");
let ret = unstyle(ret);
assert_eq!(ret, " Ctrl + a b c ".to_string());
diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs
index 603cdf351..3e6685596 100644
--- a/default-plugins/status-bar/src/main.rs
+++ b/default-plugins/status-bar/src/main.rs
@@ -44,6 +44,13 @@ pub struct LinePart {
len: usize,
}
+impl LinePart {
+ pub fn append(&mut self, to_append: &LinePart) {
+ self.part.push_str(&to_append.part);
+ self.len += to_append.len;
+ }
+}
+
impl Display for LinePart {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.part)
@@ -236,7 +243,8 @@ impl ZellijPlugin for State {
""
};
- let first_line = first_line(&self.mode_info, cols, separator);
+ let active_tab = self.tabs.iter().find(|t| t.active);
+ let first_line = first_line(&self.mode_info, active_tab, cols, separator);
let second_line = self.second_line(cols);
let background = match self.mode_info.style.colors.theme_hue {
@@ -396,7 +404,11 @@ pub fn action_key_group(keymap: &[(Key, Vec<Action>)], actions: &[&[Action]]) ->
///
/// The returned Vector of [`ANSIString`] is suitable for transformation into an [`ANSIStrings`]
/// type.
-pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIString<'static>> {
+pub fn style_key_with_modifier(
+ keyvec: &[Key],
+ palette: &Palette,
+ background: Option<PaletteColor>,
+) -> Vec<ANSIString<'static>> {
// Nothing to do, quit...
if keyvec.is_empty() {
return vec![];
@@ -419,13 +431,32 @@ pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIStr
let painted_modifier = if modifier_str.is_empty() {
Style::new().paint("")
} else {
- Style::new().fg(orange_color).bold().paint(modifier_str)
+ if let Some(background) = background {
+ let background = palette_match!(background);
+ Style::new()
+ .fg(orange_color)
+ .on(background)
+ .bold()
+ .paint(modifier_str)
+ } else {
+ Style::new().fg(orange_color).bold().paint(modifier_str)
+ }
};
ret.push(painted_modifier);
// Prints key group start
let group_start_str = if no_modifier { "<" } else { " + <" };
- ret.push(Style::new().fg(text_color).paint(group_start_str));
+ if let Some(background) = background {
+ let background = palette_match!(background);
+ ret.push(
+ Style::new()
+ .fg(text_color)
+ .on(background)
+ .paint(group_start_str),
+ );
+ } else {
+ ret.push(Style::new().fg(text_color).paint(group_start_str));
+ }
// Prints the keys
let key = keyvec
@@ -451,18 +482,50 @@ pub fn style_key_with_modifier(keyvec: &[Key], palette: &Palette) -> Vec<ANSIStr
"←↓↑→" => "",
"←→" => "",
"↓↑" => "",
+ "[]" => "",
_ => "|",
};
for (idx, key) in key.iter().enumerate() {
if idx > 0 && !key_separator.is_empty() {
- ret.push(Style::new().fg(text_color).paint(key_separator));
+ if let Some(background) = background {
+ let background = palette_match!(background);
+ ret.push(
+ Style::new()
+ .fg(text_color)
+ .on(background)
+ .paint(key_separator),
+ );
+ } else {
+ ret.push(Style::new().fg(text_color).paint(key_separator));
+ }
+ }
+ if let Some(background) = background {
+ let background = palette_match!(background);
+ ret.push(
+ Style::new()
+ .fg(green_color)
+ .on(background)
+ .bold()
+ .paint(key.clone()),
+ );
+ } else {
+ ret.push(Style::new().fg(green_color).bold().paint(key.clone()));
}
- ret.push(Style::new().fg(green_color).bold().paint(key.clone()));
}
let group_end_str = ">";
- ret.push(Style::new().fg(text_color).paint(group_end_str));
+ if let Some(background) = background {
+ let background = palette_match!(background);
+ ret.push(
+ Style::new()
+ .fg(text_color)
+ .on(background)
+ .paint(group_end_str),
+ );
+ } else {
+ ret.push(Style::new().fg(text_color).paint(group_end_str));
+ }
ret
}
@@ -623,7 +686,7 @@ pub mod tests {
let keyvec = vec![Key::Char('a'), Key::Char('b'), Key::Char('c')];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "<a|b|c>".to_string())
@@ -639,7 +702,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "<hjkl>".to_string())
@@ -656,7 +719,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "<h|k|j|l>".to_string())
@@ -672,7 +735,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "<←↓↑→>".to_string())
@@ -683,7 +746,7 @@ pub mod tests {
let keyvec = vec![Key::Char('←'), Key::Char('→')];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "<←→>".to_string())
@@ -694,7 +757,7 @@ pub mod tests {
let keyvec = vec![Key::Char('↓'), Key::Char('↑')];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "<↓↑>".to_string())
@@ -710,7 +773,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "Ctrl + <a|b|c|d>".to_string())
@@ -726,7 +789,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "Alt + <a|b|c|d>".to_string())
@@ -742,7 +805,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "Alt + <←↓↑→>".to_string())
@@ -757,7 +820,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "<Alt+a|Ctrl+b|c>".to_string())
@@ -780,7 +843,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(
@@ -794,7 +857,7 @@ pub mod tests {
let keyvec = vec![Key::Ctrl('\n'), Key::Ctrl(' '), Key::Ctrl('\t')];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));
assert_eq!(ret, "Ctrl + <ENTER|SPACE|TAB>".to_string())
@@ -809,7 +872,7 @@ pub mod tests {
];
let palette = get_palette();
- let ret = style_key_with_modifier(&keyvec, &palette);
+ let ret = style_key_with_modifier(&keyvec, &palette, None);
let ret = unstyle(&ANSIStrings(&ret));