use rpi_tui::{bold as tui_bold, theme as current_theme, Component};
use crate::args::is_truthy_env_flag;
pub const NO_EMOJI_ENV: &str = "RPI_NO_EMOJI";
pub const CRAB: &str = "🦀";
const SEPARATOR: &str = "·";
pub const MARK: &str = "▃ ▅ ▂";
pub(crate) fn crab_enabled_from(value: Option<&str>) -> bool {
!is_truthy_env_flag(value)
}
fn crab_enabled() -> bool {
crab_enabled_from(std::env::var(NO_EMOJI_ENV).ok().as_deref())
}
fn plain_name(crab: bool) -> String {
if crab {
format!("rpi {SEPARATOR} {CRAB}")
} else {
"rpi".to_string()
}
}
pub fn window_title(status: &str) -> String {
window_title_from(crab_enabled(), status)
}
fn window_title_from(crab: bool, status: &str) -> String {
let head = format!("{MARK} {}", plain_name(crab));
if status.is_empty() {
head
} else {
format!("{head} {status}")
}
}
fn render_lockup(crab: bool) -> Vec<String> {
let c = current_theme().colors;
let bar = c.dim.fg("██");
let bar_accent = c.accent.fg("██");
let name = if crab {
format!(
"{} {} {CRAB}",
c.accent.fg(&tui_bold("rpi")),
c.muted.fg(SEPARATOR)
)
} else {
c.accent.fg(&tui_bold("rpi"))
};
vec![
format!(" {bar_accent}"),
format!(" {bar_accent}"),
format!(" {bar} {bar_accent}"),
format!(" {bar} {bar_accent} {bar}"),
format!(" {bar} {bar_accent} {bar} {name}"),
]
}
pub struct BrandLockup;
impl BrandLockup {
pub fn new() -> Self {
Self
}
}
impl Default for BrandLockup {
fn default() -> Self {
Self::new()
}
}
impl Component for BrandLockup {
fn render(&self, _width: usize) -> Vec<String> {
render_lockup(crab_enabled())
}
fn invalidate(&self) {
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use rpi_tui::ansi::{strip_ansi, visible_width};
fn plain(crab: bool) -> Vec<String> {
render_lockup(crab).iter().map(|r| strip_ansi(r)).collect()
}
#[test]
fn mark_rows_match_the_site_brand() {
let rows = plain(true);
assert_eq!(rows[0], " ██");
assert_eq!(rows[1], " ██");
assert_eq!(rows[2], " ██ ██");
assert_eq!(rows[3], " ██ ██ ██");
let rows: Vec<usize> = (0..3)
.map(|i| {
let col = 3 + i * 3;
plain(true)
.iter()
.filter(|r| r.chars().skip(col).take(2).collect::<String>() == "██")
.count()
})
.collect();
assert_eq!(rows, vec![3, 5, 2], "lockup bar profile changed");
let title: Vec<usize> = MARK
.chars()
.filter_map(|c| "▁▂▃▄▅▆▇█".chars().position(|b| b == c).map(|h| h + 1))
.collect();
assert_eq!(title, rows, "the tab title must mirror the lockup mark");
}
#[test]
fn crab_stands_in_for_the_language_name() {
let rows = plain(true);
assert_eq!(rows[4], " ██ ██ ██ rpi · 🦀");
let without = visible_width(&plain(false)[4]);
assert_eq!(
visible_width(&rows[4]) - without,
visible_width(" · ") + visible_width(CRAB)
);
}
#[test]
fn title_flattens_the_lockup_and_keeps_status_last() {
assert_eq!(window_title_from(true, ""), "▃ ▅ ▂ rpi · 🦀");
assert_eq!(window_title_from(true, "⟳"), "▃ ▅ ▂ rpi · 🦀 ⟳");
assert_eq!(window_title_from(true, "↻"), "▃ ▅ ▂ rpi · 🦀 ↻");
}
#[test]
fn title_shares_the_lockup_opt_out() {
assert_eq!(window_title_from(false, ""), "▃ ▅ ▂ rpi");
assert_eq!(window_title_from(false, "⟳"), "▃ ▅ ▂ rpi ⟳");
assert!(!window_title_from(false, "").contains(CRAB));
}
#[test]
fn title_has_no_theme_escapes_and_no_cjk_ambiguity() {
let title = window_title_from(true, "⟳");
assert_eq!(title, strip_ansi(&title), "OSC 2 titles must be plain text");
assert_eq!(visible_width(MARK), 5, "mark width assumption changed");
}
#[test]
fn emoji_width_is_the_one_thing_the_layout_depends_on() {
assert_eq!(visible_width(CRAB), 2, "crab width assumption changed");
}
#[test]
fn disabling_drops_the_crab_and_its_separator() {
assert!(crab_enabled_from(None));
for value in ["1", "true", "yes", "TRUE", "Yes"] {
assert!(!crab_enabled_from(Some(value)), "{value} should disable");
}
for value in ["", "0", "false", "no", "off"] {
assert!(crab_enabled_from(Some(value)), "{value} should not disable");
}
let rows = plain(false);
assert_eq!(rows.len(), 5);
assert!(!rows[4].contains(CRAB));
assert!(!rows[4].contains(SEPARATOR), "got {:?}", rows[4]);
assert!(rows[4].ends_with("rpi"), "got {:?}", rows[4]);
}
}