Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: auto switch between dark and light icons/flavors based on terminal backgrounds #1946

Merged
merged 5 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes"],"version":"0.2"}
{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes","luma"],"language":"en"}
23 changes: 16 additions & 7 deletions scripts/icons/generate.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,25 @@ function dump(map)
list[#list + 1] = { name = k, text = v.icon, fg_dark = v.fg_dark, fg_light = v.fg_light }
end
table.sort(list, function(a, b) return a.name:lower() < b.name:lower() end)
local dark, light = "", ""
for _, v in ipairs(list) do
-- stylua: ignore
print(string.format('\t{ name = "%s", text = "%s", fg_dark = "%s", fg_light = "%s" },', v.name, v.text, v.fg_dark, v.fg_light))
dark = dark .. string.format('\t{ name = "%s", text = "%s", fg = "%s" },\n', v.name, v.text, v.fg_dark)
light = light .. string.format('\t{ name = "%s", text = "%s", fg = "%s" },\n', v.name, v.text, v.fg_light)
end
return dark, light
end

print("files = [")
dump(rearrange("files"))
print("]")
function save(typ, files, exts)
local p = string.format("../../yazi-config/preset/flavor-%s.toml", typ)
local s = io.open(p, "r"):read("*a")
s = s:gsub("files = %[\n(.-)\n%]", string.format("files = [\n%s]", files))
s = s:gsub("exts = %[\n(.-)\n%]", string.format("exts = [\n%s]", exts))
io.open(p, "w"):write(s)
end

local dark_files, light_files = dump(rearrange("files"))
local dark_exts, light_exts = dump(rearrange("exts"))

print("exts = [")
dump(rearrange("exts"))
print("]")
save("dark", dark_files, dark_exts)
save("light", light_files, light_exts)
40 changes: 19 additions & 21 deletions yazi-adapter/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use ratatui::layout::Rect;
use tracing::warn;
use yazi_shared::env_exists;

use super::{Iip, Kgp, KgpOld};
use crate::{Chafa, Emulator, SHOWN, Sixel, TMUX, Ueberzug, WSL};
use crate::{Brand, Emulator, SHOWN, TMUX, WSL, drivers};

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Adapter {
Expand Down Expand Up @@ -42,12 +41,12 @@ impl Adapter {
}

match self {
Self::Kgp => Kgp::image_show(path, max).await,
Self::KgpOld => KgpOld::image_show(path, max).await,
Self::Iip => Iip::image_show(path, max).await,
Self::Sixel => Sixel::image_show(path, max).await,
Self::X11 | Self::Wayland => Ueberzug::image_show(path, max).await,
Self::Chafa => Chafa::image_show(path, max).await,
Self::Kgp => drivers::Kgp::image_show(path, max).await,
Self::KgpOld => drivers::KgpOld::image_show(path, max).await,
Self::Iip => drivers::Iip::image_show(path, max).await,
Self::Sixel => drivers::Sixel::image_show(path, max).await,
Self::X11 | Self::Wayland => drivers::Ueberzug::image_show(path, max).await,
Self::Chafa => drivers::Chafa::image_show(path, max).await,
}
}

Expand All @@ -57,12 +56,12 @@ impl Adapter {

pub fn image_erase(self, area: Rect) -> Result<()> {
match self {
Self::Kgp => Kgp::image_erase(area),
Self::KgpOld => KgpOld::image_erase(area),
Self::Iip => Iip::image_erase(area),
Self::Sixel => Sixel::image_erase(area),
Self::X11 | Self::Wayland => Ueberzug::image_erase(area),
Self::Chafa => Chafa::image_erase(area),
Self::Kgp => drivers::Kgp::image_erase(area),
Self::KgpOld => drivers::KgpOld::image_erase(area),
Self::Iip => drivers::Iip::image_erase(area),
Self::Sixel => drivers::Sixel::image_erase(area),
Self::X11 | Self::Wayland => drivers::Ueberzug::image_erase(area),
Self::Chafa => drivers::Chafa::image_erase(area),
}
}

Expand All @@ -72,7 +71,7 @@ impl Adapter {
#[inline]
pub(super) fn shown_store(area: Rect) { SHOWN.set(Some(area)); }

pub(super) fn start(self) { Ueberzug::start(self); }
pub(super) fn start(self) { drivers::Ueberzug::start(self); }

#[inline]
pub(super) fn needs_ueberzug(self) -> bool {
Expand All @@ -81,15 +80,14 @@ impl Adapter {
}

impl Adapter {
pub fn matches() -> Self {
let emulator = Emulator::detect();
if matches!(emulator, Emulator::Microsoft) {
pub fn matches(emulator: Emulator) -> Self {
if matches!(emulator.kind.left(), Some(Brand::Microsoft)) {
return Self::Sixel;
} else if *WSL && matches!(emulator, Emulator::WezTerm) {
} else if *WSL && matches!(emulator.kind.left(), Some(Brand::WezTerm)) {
return Self::KgpOld;
}

let mut protocols = emulator.adapters();
let mut protocols = emulator.adapters().to_owned();
#[cfg(windows)]
protocols.retain(|p| *p == Self::Iip);
if env_exists("ZELLIJ_SESSION_NAME") {
Expand All @@ -101,7 +99,7 @@ impl Adapter {
return *p;
}

let supported_compositor = Ueberzug::supported_compositor();
let supported_compositor = drivers::Ueberzug::supported_compositor();
match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() {
"x11" => return Self::X11,
"wayland" if supported_compositor => return Self::Wayland,
Expand Down
119 changes: 119 additions & 0 deletions yazi-adapter/src/brand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use tracing::warn;
use yazi_shared::env_exists;

use crate::Mux;

#[derive(Clone, Copy, Debug)]
pub enum Brand {
Kitty,
Konsole,
Iterm2,
WezTerm,
Foot,
Ghostty,
Microsoft,
Rio,
BlackBox,
VSCode,
Tabby,
Hyper,
Mintty,
Neovim,
Apple,
Urxvt,
}

impl Brand {
pub fn from_env() -> Option<Self> {
use Brand as B;

if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") {
return Some(Self::Neovim);
}

let vars = [
("KITTY_WINDOW_ID", B::Kitty),
("KONSOLE_VERSION", B::Konsole),
("ITERM_SESSION_ID", B::Iterm2),
("WEZTERM_EXECUTABLE", B::WezTerm),
("GHOSTTY_RESOURCES_DIR", B::Ghostty),
("WT_Session", B::Microsoft),
("VSCODE_INJECTION", B::VSCode),
("TABBY_CONFIG_DIRECTORY", B::Tabby),
];
match vars.into_iter().find(|&(s, _)| env_exists(s)) {
Some((_, brand)) => return Some(brand),
None => warn!("[Adapter] No special environment variables detected"),
}

let (term, program) = B::env();
match program.as_str() {
"iTerm.app" => return Some(B::Iterm2),
"WezTerm" => return Some(B::WezTerm),
"ghostty" => return Some(B::Ghostty),
"rio" => return Some(B::Rio),
"BlackBox" => return Some(B::BlackBox),
"vscode" => return Some(B::VSCode),
"Tabby" => return Some(B::Tabby),
"Hyper" => return Some(B::Hyper),
"mintty" => return Some(B::Mintty),
"Apple_Terminal" => return Some(B::Apple),
_ => warn!("[Adapter] Unknown TERM_PROGRAM: {program}"),
}
match term.as_str() {
"xterm-kitty" => return Some(B::Kitty),
"foot" => return Some(B::Foot),
"foot-extra" => return Some(B::Foot),
"xterm-ghostty" => return Some(B::Ghostty),
"rio" => return Some(B::Rio),
"rxvt-unicode-256color" => return Some(B::Urxvt),
_ => warn!("[Adapter] Unknown TERM: {term}"),
}
None
}

pub(super) fn from_csi(resp: &str) -> Option<Self> {
let names = [
("kitty", Self::Kitty),
("Konsole", Self::Konsole),
("iTerm2", Self::Iterm2),
("WezTerm", Self::WezTerm),
("foot", Self::Foot),
("ghostty", Self::Ghostty),
];
names.into_iter().find(|&(n, _)| resp.contains(n)).map(|(_, b)| b)
}

pub(super) fn adapters(self) -> &'static [crate::Adapter] {
use Brand as B;

use crate::Adapter as A;

match self {
B::Kitty => &[A::Kgp],
B::Konsole => &[A::KgpOld],
B::Iterm2 => &[A::Iip, A::Sixel],
B::WezTerm => &[A::Iip, A::Sixel],
B::Foot => &[A::Sixel],
B::Ghostty => &[A::Kgp],
B::Microsoft => &[A::Sixel],
B::Rio => &[A::Iip, A::Sixel],
B::BlackBox => &[A::Sixel],
B::VSCode => &[A::Iip, A::Sixel],
B::Tabby => &[A::Iip, A::Sixel],
B::Hyper => &[A::Iip, A::Sixel],
B::Mintty => &[A::Iip],
B::Neovim => &[],
B::Apple => &[],
B::Urxvt => &[],
}
}

fn env() -> (String, String) {
let (term, program) = Mux::term_program();
(
term.unwrap_or(std::env::var("TERM").unwrap_or_default()),
program.unwrap_or(std::env::var("TERM_PROGRAM").unwrap_or_default()),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use tokio::process::Command;

use crate::{Adapter, Emulator};

pub(super) struct Chafa;
pub(crate) struct Chafa;

impl Chafa {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
let output = Command::new("chafa")
.args([
"-f",
Expand Down Expand Up @@ -64,7 +64,7 @@ impl Chafa {
})
}

pub(super) fn image_erase(area: Rect) -> Result<()> {
pub(crate) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |stderr| {
for y in area.top()..area.bottom() {
Expand Down
9 changes: 4 additions & 5 deletions yazi-adapter/src/iip.rs → yazi-adapter/src/drivers/iip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEn
use ratatui::layout::Rect;
use yazi_config::PREVIEW;

use super::image::Image;
use crate::{CLOSE, Emulator, START, adapter::Adapter};
use crate::{CLOSE, Emulator, Image, START, adapter::Adapter};

pub(super) struct Iip;
pub(crate) struct Iip;

impl Iip {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b = Self::encode(img).await?;
Expand All @@ -26,7 +25,7 @@ impl Iip {
})
}

pub(super) fn image_erase(area: Rect) -> Result<()> {
pub(crate) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |stderr| {
for y in area.top()..area.bottom() {
Expand Down
9 changes: 4 additions & 5 deletions yazi-adapter/src/kgp.rs → yazi-adapter/src/drivers/kgp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use crossterm::{cursor::MoveTo, queue};
use image::DynamicImage;
use ratatui::layout::Rect;

use super::image::Image;
use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter};
use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter, image::Image};

static DIACRITICS: [char; 297] = [
'\u{0305}',
Expand Down Expand Up @@ -310,10 +309,10 @@ static DIACRITICS: [char; 297] = [
'\u{1D244}',
];

pub(super) struct Kgp;
pub(crate) struct Kgp;

impl Kgp {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);

Expand All @@ -329,7 +328,7 @@ impl Kgp {
})
}

pub(super) fn image_erase(area: Rect) -> Result<()> {
pub(crate) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |stderr| {
for y in area.top()..area.bottom() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ use base64::{Engine, engine::general_purpose};
use image::DynamicImage;
use ratatui::layout::Rect;

use super::image::Image;
use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter};
use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};

pub(super) struct KgpOld;
pub(crate) struct KgpOld;

impl KgpOld {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b = Self::encode(img).await?;
Expand All @@ -26,7 +25,7 @@ impl KgpOld {
}

#[inline]
pub(super) fn image_erase(_: Rect) -> Result<()> {
pub(crate) fn image_erase(_: Rect) -> Result<()> {
let mut stderr = LineWriter::new(stderr());
write!(stderr, "{}_Gq=2,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?;
stderr.flush()?;
Expand Down
1 change: 1 addition & 0 deletions yazi-adapter/src/drivers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yazi_macro::mod_flat!(chafa iip kgp kgp_old sixel ueberzug);
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use yazi_config::PREVIEW;

use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};

pub(super) struct Sixel;
pub(crate) struct Sixel;

impl Sixel {
pub(super) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
let img = Image::downscale(path, max).await?;
let area = Image::pixel_area((img.width(), img.height()), max);
let b = Self::encode(img).await?;
Expand All @@ -25,7 +25,7 @@ impl Sixel {
})
}

pub(super) fn image_erase(area: Rect) -> Result<()> {
pub(crate) fn image_erase(area: Rect) -> Result<()> {
let s = " ".repeat(area.width as usize);
Emulator::move_lock((0, 0), |stderr| {
for y in area.top()..area.bottom() {
Expand Down
Loading
Loading