mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: new kitty graphics protocol implementation for better compatibility with tmux through Unicode placeholders (#365)
This commit is contained in:
parent
0931bc2bc6
commit
c3446cd941
9 changed files with 452 additions and 31 deletions
|
|
@ -33,7 +33,7 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265
|
|||
|
||||
| Platform | Protocol | Support |
|
||||
| ----------------- | -------------------------------------------------------------------------------- | --------------------- |
|
||||
| Kitty | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in |
|
||||
| kitty | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in |
|
||||
| WezTerm | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in |
|
||||
| Konsole | [Terminal graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/) | ✅ Built-in |
|
||||
| iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"language":"en","flagWords":[],"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","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"]}
|
||||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","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"],"version":"0.2","language":"en","flagWords":[]}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ use ratatui::prelude::Rect;
|
|||
use tokio::{fs, sync::mpsc::UnboundedSender};
|
||||
use tracing::warn;
|
||||
use yazi_config::PREVIEW;
|
||||
use yazi_shared::RoCell;
|
||||
use yazi_shared::{env_exists, RoCell};
|
||||
|
||||
use super::{Iterm2, Kitty};
|
||||
use super::{Iterm2, Kitty, KittyOld};
|
||||
use crate::{ueberzug::Ueberzug, Sixel, TMUX};
|
||||
|
||||
static IMAGE_SHOWN: AtomicBool = AtomicBool::new(false);
|
||||
|
|
@ -18,6 +18,7 @@ static UEBERZUG: RoCell<Option<UnboundedSender<Option<(PathBuf, Rect)>>>> = RoCe
|
|||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Adaptor {
|
||||
Kitty,
|
||||
KittyOld,
|
||||
Iterm2,
|
||||
Sixel,
|
||||
|
||||
|
|
@ -39,10 +40,15 @@ enum Emulator {
|
|||
VSCode,
|
||||
Hyper,
|
||||
Mintty,
|
||||
Neovim,
|
||||
}
|
||||
|
||||
impl Adaptor {
|
||||
fn emulator() -> Emulator {
|
||||
if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") {
|
||||
return Emulator::Neovim;
|
||||
}
|
||||
|
||||
let vars = [
|
||||
("KITTY_WINDOW_ID", Emulator::Kitty),
|
||||
("KONSOLE_VERSION", Emulator::Konsole),
|
||||
|
|
@ -50,7 +56,7 @@ impl Adaptor {
|
|||
("WEZTERM_EXECUTABLE", Emulator::WezTerm),
|
||||
("VSCODE_INJECTION", Emulator::VSCode),
|
||||
];
|
||||
match vars.into_iter().find(|v| env::var_os(v.0).is_some_and(|s| !s.is_empty())) {
|
||||
match vars.into_iter().find(|v| env_exists(v.0)) {
|
||||
Some(var) => return var.1,
|
||||
None => warn!("[Adaptor] No special environment variables detected"),
|
||||
}
|
||||
|
|
@ -80,21 +86,22 @@ impl Adaptor {
|
|||
Emulator::Kitty => vec![Self::Kitty],
|
||||
Emulator::Konsole => vec![Self::Kitty, Self::Iterm2, Self::Sixel],
|
||||
Emulator::Iterm2 => vec![Self::Iterm2, Self::Sixel],
|
||||
Emulator::WezTerm => vec![Self::Kitty, Self::Iterm2, Self::Sixel],
|
||||
Emulator::WezTerm => vec![Self::KittyOld, Self::Iterm2, Self::Sixel],
|
||||
Emulator::Foot => vec![Self::Sixel],
|
||||
Emulator::BlackBox => vec![Self::Sixel],
|
||||
Emulator::VSCode => vec![Self::Sixel],
|
||||
Emulator::Hyper => vec![Self::Sixel],
|
||||
Emulator::Mintty => vec![Self::Iterm2],
|
||||
Emulator::Neovim => vec![],
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
protocols.retain(|p| *p == Self::Iterm2);
|
||||
if env::var_os("ZELLIJ_SESSION_NAME").is_some_and(|s| !s.is_empty()) {
|
||||
if env_exists("ZELLIJ_SESSION_NAME") {
|
||||
protocols.retain(|p| *p == Self::Sixel);
|
||||
}
|
||||
if *TMUX && protocols.len() > 1 {
|
||||
protocols.retain(|p| *p != Self::Kitty);
|
||||
protocols.retain(|p| *p != Self::KittyOld);
|
||||
}
|
||||
if let Some(p) = protocols.first() {
|
||||
return *p;
|
||||
|
|
@ -105,10 +112,10 @@ impl Adaptor {
|
|||
"wayland" => return Self::Wayland,
|
||||
_ => warn!("[Adaptor] Could not identify XDG_SESSION_TYPE"),
|
||||
}
|
||||
if env::var_os("WAYLAND_DISPLAY").is_some_and(|s| !s.is_empty()) {
|
||||
if env_exists("WAYLAND_DISPLAY") {
|
||||
return Self::Wayland;
|
||||
}
|
||||
if env::var_os("DISPLAY").is_some_and(|s| !s.is_empty()) {
|
||||
if env_exists("DISPLAY") {
|
||||
return Self::X11;
|
||||
}
|
||||
if std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok() {
|
||||
|
|
@ -145,6 +152,7 @@ impl ToString for Adaptor {
|
|||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Kitty => "kitty",
|
||||
Self::KittyOld => "kitty",
|
||||
Self::Iterm2 => "iterm2",
|
||||
Self::Sixel => "sixel",
|
||||
Self::X11 => "x11",
|
||||
|
|
@ -171,6 +179,7 @@ impl Adaptor {
|
|||
|
||||
match self {
|
||||
Self::Kitty => Kitty::image_show(path, rect).await,
|
||||
Self::KittyOld => KittyOld::image_show(path, rect).await,
|
||||
Self::Iterm2 => Iterm2::image_show(path, rect).await,
|
||||
Self::Sixel => Sixel::image_show(path, rect).await,
|
||||
_ => Ok(if let Some(tx) = &*UEBERZUG {
|
||||
|
|
@ -185,7 +194,8 @@ impl Adaptor {
|
|||
}
|
||||
|
||||
match self {
|
||||
Self::Kitty => Kitty::image_hide(),
|
||||
Self::Kitty => Kitty::image_hide(rect),
|
||||
Self::KittyOld => KittyOld::image_hide(),
|
||||
Self::Iterm2 => Iterm2::image_hide(rect),
|
||||
Self::Sixel => Sixel::image_hide(rect),
|
||||
_ => Ok(if let Some(tx) = &*UEBERZUG {
|
||||
|
|
@ -196,6 +206,6 @@ impl Adaptor {
|
|||
|
||||
#[inline]
|
||||
pub(super) fn needs_ueberzug(self) -> bool {
|
||||
!matches!(self, Self::Kitty | Self::Iterm2 | Self::Sixel)
|
||||
!matches!(self, Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,11 @@ impl Iterm2 {
|
|||
|
||||
pub(super) fn image_hide(rect: Rect) -> Result<()> {
|
||||
let stdout = BufWriter::new(stdout().lock());
|
||||
let s = " ".repeat(rect.width as usize);
|
||||
Term::move_lock(stdout, (0, 0), |stdout| {
|
||||
for y in rect.top()..rect.bottom() {
|
||||
Term::move_to(stdout, rect.x, y)?;
|
||||
Term::kill_to_end(stdout)?;
|
||||
stdout.write_all(s.as_bytes())?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{io::{stdout, Write}, path::Path};
|
||||
use std::{io::{stdout, BufWriter, Write}, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
|
|
@ -9,6 +9,306 @@ use yazi_shared::Term;
|
|||
use super::image::Image;
|
||||
use crate::{CLOSE, ESCAPE, START};
|
||||
|
||||
static DIACRITICS: [char; 297] = [
|
||||
'\u{0305}',
|
||||
'\u{030D}',
|
||||
'\u{030E}',
|
||||
'\u{0310}',
|
||||
'\u{0312}',
|
||||
'\u{033D}',
|
||||
'\u{033E}',
|
||||
'\u{033F}',
|
||||
'\u{0346}',
|
||||
'\u{034A}',
|
||||
'\u{034B}',
|
||||
'\u{034C}',
|
||||
'\u{0350}',
|
||||
'\u{0351}',
|
||||
'\u{0352}',
|
||||
'\u{0357}',
|
||||
'\u{035B}',
|
||||
'\u{0363}',
|
||||
'\u{0364}',
|
||||
'\u{0365}',
|
||||
'\u{0366}',
|
||||
'\u{0367}',
|
||||
'\u{0368}',
|
||||
'\u{0369}',
|
||||
'\u{036A}',
|
||||
'\u{036B}',
|
||||
'\u{036C}',
|
||||
'\u{036D}',
|
||||
'\u{036E}',
|
||||
'\u{036F}',
|
||||
'\u{0483}',
|
||||
'\u{0484}',
|
||||
'\u{0485}',
|
||||
'\u{0486}',
|
||||
'\u{0487}',
|
||||
'\u{0592}',
|
||||
'\u{0593}',
|
||||
'\u{0594}',
|
||||
'\u{0595}',
|
||||
'\u{0597}',
|
||||
'\u{0598}',
|
||||
'\u{0599}',
|
||||
'\u{059C}',
|
||||
'\u{059D}',
|
||||
'\u{059E}',
|
||||
'\u{059F}',
|
||||
'\u{05A0}',
|
||||
'\u{05A1}',
|
||||
'\u{05A8}',
|
||||
'\u{05A9}',
|
||||
'\u{05AB}',
|
||||
'\u{05AC}',
|
||||
'\u{05AF}',
|
||||
'\u{05C4}',
|
||||
'\u{0610}',
|
||||
'\u{0611}',
|
||||
'\u{0612}',
|
||||
'\u{0613}',
|
||||
'\u{0614}',
|
||||
'\u{0615}',
|
||||
'\u{0616}',
|
||||
'\u{0617}',
|
||||
'\u{0657}',
|
||||
'\u{0658}',
|
||||
'\u{0659}',
|
||||
'\u{065A}',
|
||||
'\u{065B}',
|
||||
'\u{065D}',
|
||||
'\u{065E}',
|
||||
'\u{06D6}',
|
||||
'\u{06D7}',
|
||||
'\u{06D8}',
|
||||
'\u{06D9}',
|
||||
'\u{06DA}',
|
||||
'\u{06DB}',
|
||||
'\u{06DC}',
|
||||
'\u{06DF}',
|
||||
'\u{06E0}',
|
||||
'\u{06E1}',
|
||||
'\u{06E2}',
|
||||
'\u{06E4}',
|
||||
'\u{06E7}',
|
||||
'\u{06E8}',
|
||||
'\u{06EB}',
|
||||
'\u{06EC}',
|
||||
'\u{0730}',
|
||||
'\u{0732}',
|
||||
'\u{0733}',
|
||||
'\u{0735}',
|
||||
'\u{0736}',
|
||||
'\u{073A}',
|
||||
'\u{073D}',
|
||||
'\u{073F}',
|
||||
'\u{0740}',
|
||||
'\u{0741}',
|
||||
'\u{0743}',
|
||||
'\u{0745}',
|
||||
'\u{0747}',
|
||||
'\u{0749}',
|
||||
'\u{074A}',
|
||||
'\u{07EB}',
|
||||
'\u{07EC}',
|
||||
'\u{07ED}',
|
||||
'\u{07EE}',
|
||||
'\u{07EF}',
|
||||
'\u{07F0}',
|
||||
'\u{07F1}',
|
||||
'\u{07F3}',
|
||||
'\u{0816}',
|
||||
'\u{0817}',
|
||||
'\u{0818}',
|
||||
'\u{0819}',
|
||||
'\u{081B}',
|
||||
'\u{081C}',
|
||||
'\u{081D}',
|
||||
'\u{081E}',
|
||||
'\u{081F}',
|
||||
'\u{0820}',
|
||||
'\u{0821}',
|
||||
'\u{0822}',
|
||||
'\u{0823}',
|
||||
'\u{0825}',
|
||||
'\u{0826}',
|
||||
'\u{0827}',
|
||||
'\u{0829}',
|
||||
'\u{082A}',
|
||||
'\u{082B}',
|
||||
'\u{082C}',
|
||||
'\u{082D}',
|
||||
'\u{0951}',
|
||||
'\u{0953}',
|
||||
'\u{0954}',
|
||||
'\u{0F82}',
|
||||
'\u{0F83}',
|
||||
'\u{0F86}',
|
||||
'\u{0F87}',
|
||||
'\u{135D}',
|
||||
'\u{135E}',
|
||||
'\u{135F}',
|
||||
'\u{17DD}',
|
||||
'\u{193A}',
|
||||
'\u{1A17}',
|
||||
'\u{1A75}',
|
||||
'\u{1A76}',
|
||||
'\u{1A77}',
|
||||
'\u{1A78}',
|
||||
'\u{1A79}',
|
||||
'\u{1A7A}',
|
||||
'\u{1A7B}',
|
||||
'\u{1A7C}',
|
||||
'\u{1B6B}',
|
||||
'\u{1B6D}',
|
||||
'\u{1B6E}',
|
||||
'\u{1B6F}',
|
||||
'\u{1B70}',
|
||||
'\u{1B71}',
|
||||
'\u{1B72}',
|
||||
'\u{1B73}',
|
||||
'\u{1CD0}',
|
||||
'\u{1CD1}',
|
||||
'\u{1CD2}',
|
||||
'\u{1CDA}',
|
||||
'\u{1CDB}',
|
||||
'\u{1CE0}',
|
||||
'\u{1DC0}',
|
||||
'\u{1DC1}',
|
||||
'\u{1DC3}',
|
||||
'\u{1DC4}',
|
||||
'\u{1DC5}',
|
||||
'\u{1DC6}',
|
||||
'\u{1DC7}',
|
||||
'\u{1DC8}',
|
||||
'\u{1DC9}',
|
||||
'\u{1DCB}',
|
||||
'\u{1DCC}',
|
||||
'\u{1DD1}',
|
||||
'\u{1DD2}',
|
||||
'\u{1DD3}',
|
||||
'\u{1DD4}',
|
||||
'\u{1DD5}',
|
||||
'\u{1DD6}',
|
||||
'\u{1DD7}',
|
||||
'\u{1DD8}',
|
||||
'\u{1DD9}',
|
||||
'\u{1DDA}',
|
||||
'\u{1DDB}',
|
||||
'\u{1DDC}',
|
||||
'\u{1DDD}',
|
||||
'\u{1DDE}',
|
||||
'\u{1DDF}',
|
||||
'\u{1DE0}',
|
||||
'\u{1DE1}',
|
||||
'\u{1DE2}',
|
||||
'\u{1DE3}',
|
||||
'\u{1DE4}',
|
||||
'\u{1DE5}',
|
||||
'\u{1DE6}',
|
||||
'\u{1DFE}',
|
||||
'\u{20D0}',
|
||||
'\u{20D1}',
|
||||
'\u{20D4}',
|
||||
'\u{20D5}',
|
||||
'\u{20D6}',
|
||||
'\u{20D7}',
|
||||
'\u{20DB}',
|
||||
'\u{20DC}',
|
||||
'\u{20E1}',
|
||||
'\u{20E7}',
|
||||
'\u{20E9}',
|
||||
'\u{20F0}',
|
||||
'\u{2CEF}',
|
||||
'\u{2CF0}',
|
||||
'\u{2CF1}',
|
||||
'\u{2DE0}',
|
||||
'\u{2DE1}',
|
||||
'\u{2DE2}',
|
||||
'\u{2DE3}',
|
||||
'\u{2DE4}',
|
||||
'\u{2DE5}',
|
||||
'\u{2DE6}',
|
||||
'\u{2DE7}',
|
||||
'\u{2DE8}',
|
||||
'\u{2DE9}',
|
||||
'\u{2DEA}',
|
||||
'\u{2DEB}',
|
||||
'\u{2DEC}',
|
||||
'\u{2DED}',
|
||||
'\u{2DEE}',
|
||||
'\u{2DEF}',
|
||||
'\u{2DF0}',
|
||||
'\u{2DF1}',
|
||||
'\u{2DF2}',
|
||||
'\u{2DF3}',
|
||||
'\u{2DF4}',
|
||||
'\u{2DF5}',
|
||||
'\u{2DF6}',
|
||||
'\u{2DF7}',
|
||||
'\u{2DF8}',
|
||||
'\u{2DF9}',
|
||||
'\u{2DFA}',
|
||||
'\u{2DFB}',
|
||||
'\u{2DFC}',
|
||||
'\u{2DFD}',
|
||||
'\u{2DFE}',
|
||||
'\u{2DFF}',
|
||||
'\u{A66F}',
|
||||
'\u{A67C}',
|
||||
'\u{A67D}',
|
||||
'\u{A6F0}',
|
||||
'\u{A6F1}',
|
||||
'\u{A8E0}',
|
||||
'\u{A8E1}',
|
||||
'\u{A8E2}',
|
||||
'\u{A8E3}',
|
||||
'\u{A8E4}',
|
||||
'\u{A8E5}',
|
||||
'\u{A8E6}',
|
||||
'\u{A8E7}',
|
||||
'\u{A8E8}',
|
||||
'\u{A8E9}',
|
||||
'\u{A8EA}',
|
||||
'\u{A8EB}',
|
||||
'\u{A8EC}',
|
||||
'\u{A8ED}',
|
||||
'\u{A8EE}',
|
||||
'\u{A8EF}',
|
||||
'\u{A8F0}',
|
||||
'\u{A8F1}',
|
||||
'\u{AAB0}',
|
||||
'\u{AAB2}',
|
||||
'\u{AAB3}',
|
||||
'\u{AAB7}',
|
||||
'\u{AAB8}',
|
||||
'\u{AABE}',
|
||||
'\u{AABF}',
|
||||
'\u{AAC1}',
|
||||
'\u{FE20}',
|
||||
'\u{FE21}',
|
||||
'\u{FE22}',
|
||||
'\u{FE23}',
|
||||
'\u{FE24}',
|
||||
'\u{FE25}',
|
||||
'\u{FE26}',
|
||||
'\u{10A0F}',
|
||||
'\u{10A38}',
|
||||
'\u{1D185}',
|
||||
'\u{1D186}',
|
||||
'\u{1D187}',
|
||||
'\u{1D188}',
|
||||
'\u{1D189}',
|
||||
'\u{1D1AA}',
|
||||
'\u{1D1AB}',
|
||||
'\u{1D1AC}',
|
||||
'\u{1D1AD}',
|
||||
'\u{1D242}',
|
||||
'\u{1D243}',
|
||||
'\u{1D244}',
|
||||
];
|
||||
|
||||
pub(super) struct Kitty;
|
||||
|
||||
impl Kitty {
|
||||
|
|
@ -16,16 +316,41 @@ impl Kitty {
|
|||
let img = Image::crop(path, (rect.width, rect.height)).await?;
|
||||
let b = Self::encode(img).await?;
|
||||
|
||||
Self::image_hide()?;
|
||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?))
|
||||
Self::image_hide(rect)?;
|
||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
||||
stdout.write_all(&b)?;
|
||||
|
||||
let mut buf = String::with_capacity(rect.width as usize * 3 + 20);
|
||||
for y in 0..rect.height {
|
||||
Term::move_to(stdout, rect.x, rect.y + y)?;
|
||||
|
||||
buf.clear();
|
||||
buf.push_str("\x1b[38;5;1m");
|
||||
for x in 0..rect.width {
|
||||
buf.push('\u{10EEEE}');
|
||||
buf.push(*DIACRITICS.get(y as usize).unwrap_or(&DIACRITICS[0]));
|
||||
buf.push(*DIACRITICS.get(x as usize).unwrap_or(&DIACRITICS[0]));
|
||||
}
|
||||
buf.push_str("\x1b[0m");
|
||||
stdout.write_all(buf.as_bytes())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn image_hide() -> Result<()> {
|
||||
let mut stdout = stdout().lock();
|
||||
stdout.write_all(format!("{}_Ga=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?;
|
||||
stdout.flush()?;
|
||||
Ok(())
|
||||
pub(super) fn image_hide(rect: Rect) -> Result<()> {
|
||||
let stdout = BufWriter::new(stdout().lock());
|
||||
let s = " ".repeat(rect.width as usize);
|
||||
Term::move_lock(stdout, (0, 0), |stdout| {
|
||||
for y in rect.top()..rect.bottom() {
|
||||
Term::move_to(stdout, rect.x, y)?;
|
||||
stdout.write_all(s.as_bytes())?;
|
||||
}
|
||||
|
||||
stdout.write_all(format!("{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
|
||||
|
|
@ -37,7 +362,7 @@ impl Kitty {
|
|||
if let Some(first) = it.next() {
|
||||
write!(
|
||||
buf,
|
||||
"{}_Ga=T,f={},s={},v={},m={};{}{}\\{}",
|
||||
"{}_Gq=1,a=T,i=1,C=1,U=1,f={},s={},v={},m={};{}{}\\{}",
|
||||
START,
|
||||
format,
|
||||
size.0,
|
||||
|
|
|
|||
76
yazi-adaptor/src/kitty_old.rs
Normal file
76
yazi-adaptor/src/kitty_old.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
use std::{io::{stdout, Write}, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use image::DynamicImage;
|
||||
use ratatui::prelude::Rect;
|
||||
use yazi_shared::Term;
|
||||
|
||||
use super::image::Image;
|
||||
use crate::{CLOSE, ESCAPE, START};
|
||||
|
||||
pub(super) struct KittyOld;
|
||||
|
||||
impl KittyOld {
|
||||
pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<()> {
|
||||
let img = Image::crop(path, (rect.width, rect.height)).await?;
|
||||
let b = Self::encode(img).await?;
|
||||
|
||||
Self::image_hide()?;
|
||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn image_hide() -> Result<()> {
|
||||
let mut stdout = stdout().lock();
|
||||
stdout.write_all(format!("{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?;
|
||||
stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {
|
||||
fn output(raw: &[u8], format: u8, size: (u32, u32)) -> Result<Vec<u8>> {
|
||||
let b64 = general_purpose::STANDARD.encode(raw).chars().collect::<Vec<_>>();
|
||||
|
||||
let mut it = b64.chunks(4096).peekable();
|
||||
let mut buf = Vec::with_capacity(b64.len() + it.len() * 50);
|
||||
if let Some(first) = it.next() {
|
||||
write!(
|
||||
buf,
|
||||
"{}_Gq=1,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}",
|
||||
START,
|
||||
format,
|
||||
size.0,
|
||||
size.1,
|
||||
it.peek().is_some() as u8,
|
||||
first.iter().collect::<String>(),
|
||||
ESCAPE,
|
||||
CLOSE
|
||||
)?;
|
||||
}
|
||||
|
||||
while let Some(chunk) = it.next() {
|
||||
write!(
|
||||
buf,
|
||||
"{}_Gm={};{}{}\\{}",
|
||||
START,
|
||||
it.peek().is_some() as u8,
|
||||
chunk.iter().collect::<String>(),
|
||||
ESCAPE,
|
||||
CLOSE
|
||||
)?;
|
||||
}
|
||||
|
||||
buf.write_all(CLOSE.as_bytes())?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
let size = (img.width(), img.height());
|
||||
tokio::task::spawn_blocking(move || match img {
|
||||
DynamicImage::ImageRgb8(v) => output(v.as_raw(), 24, size),
|
||||
DynamicImage::ImageRgba8(v) => output(v.as_raw(), 32, size),
|
||||
v => output(v.to_rgb8().as_raw(), 24, size),
|
||||
})
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
|
@ -4,14 +4,16 @@ mod adaptor;
|
|||
mod image;
|
||||
mod iterm2;
|
||||
mod kitty;
|
||||
mod kitty_old;
|
||||
mod sixel;
|
||||
mod ueberzug;
|
||||
|
||||
use adaptor::*;
|
||||
use iterm2::*;
|
||||
use kitty::*;
|
||||
use kitty_old::*;
|
||||
use sixel::*;
|
||||
use yazi_shared::RoCell;
|
||||
use yazi_shared::{env_exists, RoCell};
|
||||
|
||||
pub use crate::image::*;
|
||||
|
||||
|
|
@ -24,11 +26,20 @@ static START: RoCell<&'static str> = RoCell::new();
|
|||
static CLOSE: RoCell<&'static str> = RoCell::new();
|
||||
|
||||
pub fn init() {
|
||||
TMUX.init(std::env::var_os("TMUX").is_some());
|
||||
TMUX.init(env_exists("TMUX"));
|
||||
START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" });
|
||||
CLOSE.init(if *TMUX { "\x1b\\" } else { "" });
|
||||
ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" });
|
||||
|
||||
ADAPTOR.init(Adaptor::detect());
|
||||
ADAPTOR.start();
|
||||
|
||||
if *TMUX {
|
||||
_ = std::process::Command::new("tmux")
|
||||
.args(["set", "-p", "allow-passthrough", "on"])
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.spawn();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ use std::{collections::VecDeque, path::{Path, PathBuf}};
|
|||
use anyhow::Result;
|
||||
use tokio::{fs, io, select, sync::{mpsc, oneshot}, time};
|
||||
|
||||
#[inline]
|
||||
pub fn env_exists(name: &str) -> bool { std::env::var_os(name).is_some_and(|s| !s.is_empty()) }
|
||||
|
||||
pub async fn calculate_size(path: &Path) -> u64 {
|
||||
let mut total = 0;
|
||||
let mut stack = VecDeque::from([path.to_path_buf()]);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
use std::io::{stdout, Write};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, execute, queue, terminal::{Clear, ClearType}};
|
||||
use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, execute, queue};
|
||||
|
||||
use crate::Term;
|
||||
|
||||
impl Term {
|
||||
#[inline]
|
||||
pub fn kill_to_end(stdout: &mut impl Write) -> Result<()> {
|
||||
Ok(queue!(stdout, Clear(ClearType::UntilNewLine))?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn move_to(stdout: &mut impl Write, x: u16, y: u16) -> Result<()> {
|
||||
Ok(queue!(stdout, MoveTo(x, y))?)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue