use super::utils::Terminal;
use crate::theme::model::ThemeLayer;
use std::io::Write;
pub fn emit<W: Write>(out: &mut W, terminal: Terminal, theme: &ThemeLayer) -> std::io::Result<()> {
let in_tmux = std::env::var_os("TMUX").is_some();
if let Some(c) = &theme.fg {
write_osc(out, in_tmux, &format!("10;{}", c.as_str()))?;
}
if let Some(c) = &theme.bg {
write_osc(out, in_tmux, &format!("11;{}", c.as_str()))?;
}
if let Some(c) = &theme.cursor {
write_osc(out, in_tmux, &format!("12;{}", c.as_str()))?;
}
for (i, slot) in theme.palette.iter().enumerate() {
if let Some(c) = slot {
write_osc(out, in_tmux, &format!("4;{};{}", i, c.as_str()))?;
}
}
if let Some(c) = &theme.tab_bg
&& let Some(payload) = tab_payload(terminal, c.as_str())
{
write_osc(out, in_tmux, &payload)?;
}
out.flush()
}
pub fn emit_reset<W: Write>(out: &mut W, terminal: Terminal) -> std::io::Result<()> {
let in_tmux = std::env::var_os("TMUX").is_some();
write_osc(out, in_tmux, "110")?;
write_osc(out, in_tmux, "111")?;
write_osc(out, in_tmux, "112")?;
for i in 0..16 {
write_osc(out, in_tmux, &format!("104;{}", i))?;
}
if let Some(payload) = tab_reset_payload(terminal) {
write_osc(out, in_tmux, payload)?;
}
out.flush()
}
pub fn emit_tab_reset<W: Write>(out: &mut W, terminal: Terminal) -> std::io::Result<()> {
if let Some(payload) = tab_reset_payload(terminal) {
let in_tmux = std::env::var_os("TMUX").is_some();
write_osc(out, in_tmux, payload)?;
out.flush()?;
}
Ok(())
}
fn tab_payload(terminal: Terminal, hex: &str) -> Option<String> {
match terminal {
Terminal::ITerm2 => Some(format!(
"1337;SetColors=tab={}",
hex.trim_start_matches('#')
)),
Terminal::Ghostty | Terminal::WezTerm => None,
}
}
fn tab_reset_payload(terminal: Terminal) -> Option<&'static str> {
match terminal {
Terminal::ITerm2 => Some("1337;SetColors=tab=default"),
Terminal::Ghostty | Terminal::WezTerm => None,
}
}
fn write_osc<W: Write>(out: &mut W, in_tmux: bool, payload: &str) -> std::io::Result<()> {
if in_tmux {
write!(out, "\x1bPtmux;\x1b\x1b]{}\x07\x1b\\", payload)
} else {
write!(out, "\x1b]{}\x07", payload)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::theme::model::{HexColor, ThemeLayer};
fn capture(theme: &ThemeLayer) -> String {
let mut buf = Vec::new();
if let Some(c) = &theme.fg {
write_osc(&mut buf, false, &format!("10;{}", c.as_str())).unwrap();
}
if let Some(c) = &theme.bg {
write_osc(&mut buf, false, &format!("11;{}", c.as_str())).unwrap();
}
String::from_utf8(buf).unwrap()
}
#[test]
fn emits_fg_and_bg() {
let t = ThemeLayer {
fg: Some(HexColor::parse("#cdd6f4").unwrap()),
bg: Some(HexColor::parse("#1e1e2e").unwrap()),
..Default::default()
};
let s = capture(&t);
assert!(s.contains("\x1b]10;#cdd6f4\x07"));
assert!(s.contains("\x1b]11;#1e1e2e\x07"));
}
#[test]
fn tmux_wraps_with_dcs() {
let mut buf = Vec::new();
write_osc(&mut buf, true, "10;#abcdef").unwrap();
let s = String::from_utf8(buf).unwrap();
assert!(s.starts_with("\x1bPtmux;\x1b\x1b]"));
assert!(s.ends_with("\x07\x1b\\"));
}
#[test]
fn tab_payload_strips_hash_for_iterm2() {
assert_eq!(
tab_payload(Terminal::ITerm2, "#abcdef").as_deref(),
Some("1337;SetColors=tab=abcdef")
);
}
#[test]
fn tab_payload_is_none_for_ghostty() {
assert!(tab_payload(Terminal::Ghostty, "#abcdef").is_none());
}
#[test]
fn tab_payload_is_none_for_wezterm() {
assert!(tab_payload(Terminal::WezTerm, "#abcdef").is_none());
}
#[test]
fn tab_reset_payload_is_default_for_iterm2() {
assert_eq!(
tab_reset_payload(Terminal::ITerm2),
Some("1337;SetColors=tab=default")
);
}
#[test]
fn tab_reset_payload_is_none_for_ghostty() {
assert!(tab_reset_payload(Terminal::Ghostty).is_none());
}
#[test]
fn tab_reset_payload_is_none_for_wezterm() {
assert!(tab_reset_payload(Terminal::WezTerm).is_none());
}
}