mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
f997bfdea0
commit
b910b5f0ab
36 changed files with 239 additions and 145 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -2614,6 +2614,7 @@ name = "yazi-adaptor"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"arc-swap",
|
||||||
"base64",
|
"base64",
|
||||||
"color_quant",
|
"color_quant",
|
||||||
"image",
|
"image",
|
||||||
|
|
@ -2713,6 +2714,7 @@ version = "0.1.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi-to-tui",
|
"ansi-to-tui",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"crossterm",
|
||||||
"futures",
|
"futures",
|
||||||
"libc",
|
"libc",
|
||||||
"md-5",
|
"md-5",
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.1.5" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
anyhow = "^1"
|
anyhow = "^1"
|
||||||
|
arc-swap = "^1"
|
||||||
base64 = "^0"
|
base64 = "^0"
|
||||||
color_quant = "^1"
|
color_quant = "^1"
|
||||||
image = "^0"
|
image = "^0"
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
use std::{env, path::Path, sync::atomic::{AtomicBool, Ordering}};
|
use std::{env, path::Path, sync::Arc};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use ratatui::prelude::Rect;
|
use ratatui::prelude::Rect;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use yazi_shared::env_exists;
|
use yazi_shared::{env_exists, term::Term};
|
||||||
|
|
||||||
use super::{Iterm2, Kitty, KittyOld};
|
use super::{Iterm2, Kitty, KittyOld};
|
||||||
use crate::{ueberzug::Ueberzug, Sixel, TMUX};
|
use crate::{ueberzug::Ueberzug, Sixel, SHOWN, TMUX};
|
||||||
|
|
||||||
static IMAGE_SHOWN: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum Adaptor {
|
pub enum Adaptor {
|
||||||
|
|
@ -162,29 +160,40 @@ impl Adaptor {
|
||||||
pub(super) fn start(self) { Ueberzug::start(self); }
|
pub(super) fn start(self) { Ueberzug::start(self); }
|
||||||
|
|
||||||
pub async fn image_show(self, path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
pub async fn image_show(self, path: &Path, rect: Rect) -> Result<(u32, u32)> {
|
||||||
self.image_hide(rect).ok();
|
self.image_hide().ok();
|
||||||
IMAGE_SHOWN.store(true, Ordering::Relaxed);
|
|
||||||
|
|
||||||
match self {
|
let size = match self {
|
||||||
Self::Kitty => Kitty::image_show(path, rect).await,
|
Self::Kitty => Kitty::image_show(path, rect).await,
|
||||||
Self::KittyOld => KittyOld::image_show(path, rect).await,
|
Self::KittyOld => KittyOld::image_show(path, rect).await,
|
||||||
Self::Iterm2 => Iterm2::image_show(path, rect).await,
|
Self::Iterm2 => Iterm2::image_show(path, rect).await,
|
||||||
Self::Sixel => Sixel::image_show(path, rect).await,
|
Self::Sixel => Sixel::image_show(path, rect).await,
|
||||||
_ => Ueberzug::image_show(path, rect).await,
|
_ => Ueberzug::image_show(path, rect).await,
|
||||||
}
|
}?;
|
||||||
|
|
||||||
|
let shown = Term::ratio()
|
||||||
|
.map(|(r1, r2)| Rect {
|
||||||
|
x: rect.x,
|
||||||
|
y: rect.y,
|
||||||
|
width: (size.0 as f64 / r1).ceil() as u16,
|
||||||
|
height: (size.1 as f64 / r2).ceil() as u16,
|
||||||
|
})
|
||||||
|
.unwrap_or(rect);
|
||||||
|
|
||||||
|
SHOWN.store(Some(Arc::new(shown)));
|
||||||
|
Ok(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn image_hide(self, rect: Rect) -> Result<()> {
|
pub fn image_hide(self) -> Result<()> {
|
||||||
if !IMAGE_SHOWN.swap(false, Ordering::Relaxed) {
|
if let Some(rect) = SHOWN.swap(None) { self.image_erase(*rect) } else { Ok(()) }
|
||||||
return Ok(());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
pub fn image_erase(self, rect: Rect) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Kitty => Kitty::image_hide(rect),
|
Self::Kitty => Kitty::image_erase(rect),
|
||||||
Self::Iterm2 => Iterm2::image_hide(rect),
|
Self::Iterm2 => Iterm2::image_erase(rect),
|
||||||
Self::KittyOld => KittyOld::image_hide(),
|
Self::KittyOld => KittyOld::image_erase(),
|
||||||
Self::Sixel => Sixel::image_hide(rect),
|
Self::Sixel => Sixel::image_erase(rect),
|
||||||
_ => Ueberzug::image_hide(rect),
|
_ => Ueberzug::image_erase(rect),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use ratatui::prelude::Rect;
|
||||||
use yazi_shared::term::Term;
|
use yazi_shared::term::Term;
|
||||||
|
|
||||||
use super::image::Image;
|
use super::image::Image;
|
||||||
use crate::{CLOSE, START};
|
use crate::{adaptor::Adaptor, CLOSE, START};
|
||||||
|
|
||||||
pub(super) struct Iterm2;
|
pub(super) struct Iterm2;
|
||||||
|
|
||||||
|
|
@ -17,14 +17,14 @@ impl Iterm2 {
|
||||||
let size = (img.width(), img.height());
|
let size = (img.width(), img.height());
|
||||||
let b = Self::encode(img).await?;
|
let b = Self::encode(img).await?;
|
||||||
|
|
||||||
Self::image_hide(rect)?;
|
Adaptor::Iterm2.image_hide()?;
|
||||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
||||||
stdout.write_all(&b)?;
|
stdout.write_all(&b)?;
|
||||||
Ok(size)
|
Ok(size)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn image_hide(rect: Rect) -> Result<()> {
|
pub(super) fn image_erase(rect: Rect) -> Result<()> {
|
||||||
let stdout = BufWriter::new(stdout().lock());
|
let stdout = BufWriter::new(stdout().lock());
|
||||||
let s = " ".repeat(rect.width as usize);
|
let s = " ".repeat(rect.width as usize);
|
||||||
Term::move_lock(stdout, (0, 0), |stdout| {
|
Term::move_lock(stdout, (0, 0), |stdout| {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use ratatui::prelude::Rect;
|
||||||
use yazi_shared::term::Term;
|
use yazi_shared::term::Term;
|
||||||
|
|
||||||
use super::image::Image;
|
use super::image::Image;
|
||||||
use crate::{CLOSE, ESCAPE, START};
|
use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START};
|
||||||
|
|
||||||
static DIACRITICS: [char; 297] = [
|
static DIACRITICS: [char; 297] = [
|
||||||
'\u{0305}',
|
'\u{0305}',
|
||||||
|
|
@ -317,7 +317,7 @@ impl Kitty {
|
||||||
let size = (img.width(), img.height());
|
let size = (img.width(), img.height());
|
||||||
let b = Self::encode(img).await?;
|
let b = Self::encode(img).await?;
|
||||||
|
|
||||||
Self::image_hide(rect)?;
|
Adaptor::Kitty.image_hide()?;
|
||||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
||||||
stdout.write_all(&b)?;
|
stdout.write_all(&b)?;
|
||||||
|
|
||||||
|
|
@ -340,7 +340,7 @@ impl Kitty {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn image_hide(rect: Rect) -> Result<()> {
|
pub(super) fn image_erase(rect: Rect) -> Result<()> {
|
||||||
let stdout = BufWriter::new(stdout().lock());
|
let stdout = BufWriter::new(stdout().lock());
|
||||||
let s = " ".repeat(rect.width as usize);
|
let s = " ".repeat(rect.width as usize);
|
||||||
Term::move_lock(stdout, (0, 0), |stdout| {
|
Term::move_lock(stdout, (0, 0), |stdout| {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use ratatui::prelude::Rect;
|
||||||
use yazi_shared::term::Term;
|
use yazi_shared::term::Term;
|
||||||
|
|
||||||
use super::image::Image;
|
use super::image::Image;
|
||||||
use crate::{CLOSE, ESCAPE, START};
|
use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START};
|
||||||
|
|
||||||
pub(super) struct KittyOld;
|
pub(super) struct KittyOld;
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ impl KittyOld {
|
||||||
let size = (img.width(), img.height());
|
let size = (img.width(), img.height());
|
||||||
let b = Self::encode(img).await?;
|
let b = Self::encode(img).await?;
|
||||||
|
|
||||||
Self::image_hide()?;
|
Adaptor::KittyOld.image_hide()?;
|
||||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
||||||
stdout.write_all(&b)?;
|
stdout.write_all(&b)?;
|
||||||
Ok(size)
|
Ok(size)
|
||||||
|
|
@ -25,7 +25,7 @@ impl KittyOld {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn image_hide() -> Result<()> {
|
pub(super) fn image_erase() -> Result<()> {
|
||||||
let mut stdout = stdout().lock();
|
let mut stdout = stdout().lock();
|
||||||
stdout.write_all(format!("{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?;
|
stdout.write_all(format!("{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE).as_bytes())?;
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,17 @@ static ESCAPE: RoCell<&'static str> = RoCell::new();
|
||||||
static START: RoCell<&'static str> = RoCell::new();
|
static START: RoCell<&'static str> = RoCell::new();
|
||||||
static CLOSE: RoCell<&'static str> = RoCell::new();
|
static CLOSE: RoCell<&'static str> = RoCell::new();
|
||||||
|
|
||||||
|
// Image state
|
||||||
|
static SHOWN: RoCell<arc_swap::ArcSwapOption<ratatui::layout::Rect>> = RoCell::new();
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
TMUX.init(env_exists("TMUX"));
|
TMUX.init(env_exists("TMUX"));
|
||||||
START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" });
|
START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" });
|
||||||
CLOSE.init(if *TMUX { "\x1b\\" } else { "" });
|
CLOSE.init(if *TMUX { "\x1b\\" } else { "" });
|
||||||
ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" });
|
ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" });
|
||||||
|
|
||||||
|
SHOWN.with(Default::default);
|
||||||
|
|
||||||
ADAPTOR.init(Adaptor::detect());
|
ADAPTOR.init(Adaptor::detect());
|
||||||
ADAPTOR.start();
|
ADAPTOR.start();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use image::DynamicImage;
|
||||||
use ratatui::prelude::Rect;
|
use ratatui::prelude::Rect;
|
||||||
use yazi_shared::term::Term;
|
use yazi_shared::term::Term;
|
||||||
|
|
||||||
use crate::{Image, CLOSE, ESCAPE, START};
|
use crate::{adaptor::Adaptor, Image, CLOSE, ESCAPE, START};
|
||||||
|
|
||||||
pub(super) struct Sixel;
|
pub(super) struct Sixel;
|
||||||
|
|
||||||
|
|
@ -16,14 +16,14 @@ impl Sixel {
|
||||||
let size = (img.width(), img.height());
|
let size = (img.width(), img.height());
|
||||||
let b = Self::encode(img).await?;
|
let b = Self::encode(img).await?;
|
||||||
|
|
||||||
Self::image_hide(rect)?;
|
Adaptor::Sixel.image_hide()?;
|
||||||
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| {
|
||||||
stdout.write_all(&b)?;
|
stdout.write_all(&b)?;
|
||||||
Ok(size)
|
Ok(size)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn image_hide(rect: Rect) -> Result<()> {
|
pub(super) fn image_erase(rect: Rect) -> Result<()> {
|
||||||
let stdout = BufWriter::new(stdout().lock());
|
let stdout = BufWriter::new(stdout().lock());
|
||||||
let s = " ".repeat(rect.width as usize);
|
let s = " ".repeat(rect.width as usize);
|
||||||
Term::move_lock(stdout, (0, 0), |stdout| {
|
Term::move_lock(stdout, (0, 0), |stdout| {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ impl Ueberzug {
|
||||||
Ok(((w as f64 * ratio).round() as u32, (h as f64 * ratio).round() as u32))
|
Ok(((w as f64 * ratio).round() as u32, (h as f64 * ratio).round() as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn image_hide(_: Rect) -> Result<()> {
|
pub(super) fn image_erase(_: Rect) -> Result<()> {
|
||||||
if let Some(tx) = &*DEMON {
|
if let Some(tx) = &*DEMON {
|
||||||
Ok(tx.send(None)?)
|
Ok(tx.send(None)?)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
use std::ffi::OsString;
|
use std::{cell::RefCell, ffi::OsString};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use yazi_shared::RoCell;
|
use yazi_shared::RoCell;
|
||||||
|
|
||||||
pub static CLIPBOARD: RoCell<Clipboard> = RoCell::new();
|
pub static CLIPBOARD: RoCell<Clipboard> = RoCell::new();
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Clipboard {
|
pub struct Clipboard {
|
||||||
content: Mutex<OsString>,
|
content: RefCell<OsString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clipboard {
|
impl Clipboard {
|
||||||
|
|
@ -19,7 +18,7 @@ impl Clipboard {
|
||||||
use yazi_shared::in_ssh_connection;
|
use yazi_shared::in_ssh_connection;
|
||||||
|
|
||||||
if in_ssh_connection() {
|
if in_ssh_connection() {
|
||||||
return self.content.lock().clone();
|
return self.content.borrow().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let all = [
|
let all = [
|
||||||
|
|
@ -37,7 +36,7 @@ impl Clipboard {
|
||||||
return OsString::from_vec(output.stdout);
|
return OsString::from_vec(output.stdout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.content.lock().clone()
|
self.content.borrow().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|
@ -60,7 +59,7 @@ impl Clipboard {
|
||||||
use tokio::{io::AsyncWriteExt, process::Command};
|
use tokio::{io::AsyncWriteExt, process::Command};
|
||||||
use yazi_shared::in_ssh_connection;
|
use yazi_shared::in_ssh_connection;
|
||||||
|
|
||||||
*self.content.lock() = s.as_ref().to_owned();
|
*self.content.borrow_mut() = s.as_ref().to_owned();
|
||||||
if in_ssh_connection() {
|
if in_ssh_connection() {
|
||||||
execute!(stdout(), osc52::SetClipboard::new(s.as_ref())).ok();
|
execute!(stdout(), osc52::SetClipboard::new(s.as_ref())).ok();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use yazi_adaptor::ADAPTOR;
|
use yazi_adaptor::ADAPTOR;
|
||||||
use yazi_config::{LAYOUT, PLUGIN};
|
use yazi_config::PLUGIN;
|
||||||
use yazi_plugin::{external::Highlighter, utils::PreviewLock};
|
use yazi_plugin::{external::Highlighter, utils::PreviewLock};
|
||||||
use yazi_shared::fs::{Cha, File, Url};
|
use yazi_shared::fs::{Cha, File, Url};
|
||||||
|
|
||||||
|
|
@ -18,11 +18,12 @@ impl Preview {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.abort();
|
|
||||||
let Some(previewer) = PLUGIN.previewer(&file.url, &mime) else {
|
let Some(previewer) = PLUGIN.previewer(&file.url, &mime) else {
|
||||||
|
self.reset();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.abort();
|
||||||
if previewer.sync {
|
if previewer.sync {
|
||||||
yazi_plugin::isolate::peek_sync(&previewer.exec, file, self.skip);
|
yazi_plugin::isolate::peek_sync(&previewer.exec, file, self.skip);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -39,7 +40,7 @@ impl Preview {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reset(&mut self) -> bool {
|
pub fn reset(&mut self) -> bool {
|
||||||
self.abort();
|
self.abort();
|
||||||
ADAPTOR.image_hide(LAYOUT.load().preview).ok();
|
ADAPTOR.image_hide().ok();
|
||||||
self.lock.take().is_some()
|
self.lock.take().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use anyhow::{Ok, Result};
|
use anyhow::{Ok, Result};
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
use ratatui::{backend::Backend, prelude::Rect};
|
use ratatui::backend::Backend;
|
||||||
use yazi_config::{keymap::Key, ARGS};
|
use yazi_config::{keymap::Key, ARGS};
|
||||||
use yazi_core::input::InputMode;
|
use yazi_core::input::InputMode;
|
||||||
use yazi_shared::{emit, event::{Event, Exec}, fs::FilesOp, term::Term, Layer, COLLISION};
|
use yazi_shared::{emit, event::{Event, Exec}, fs::FilesOp, term::Term, Layer, COLLISION};
|
||||||
|
|
@ -36,7 +36,7 @@ impl App {
|
||||||
Event::Key(key) => app.dispatch_key(key),
|
Event::Key(key) => app.dispatch_key(key),
|
||||||
Event::Paste(str) => app.dispatch_paste(str),
|
Event::Paste(str) => app.dispatch_paste(str),
|
||||||
Event::Render(_) => app.dispatch_render()?,
|
Event::Render(_) => app.dispatch_render()?,
|
||||||
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows),
|
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows)?,
|
||||||
Event::Call(exec, layer) => app.dispatch_call(exec, layer),
|
Event::Call(exec, layer) => app.dispatch_call(exec, layer),
|
||||||
event => app.dispatch_module(event),
|
event => app.dispatch_module(event),
|
||||||
}
|
}
|
||||||
|
|
@ -112,15 +112,13 @@ impl App {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_resize(&mut self, cols: u16, rows: u16) {
|
fn dispatch_resize(&mut self, _: u16, _: u16) -> Result<()> {
|
||||||
if let Some(term) = &mut self.term {
|
self.cx.manager.active_mut().preview.reset();
|
||||||
term.resize(Rect::new(0, 0, cols, rows)).ok();
|
self.dispatch_render()?;
|
||||||
}
|
|
||||||
|
|
||||||
self.cx.manager.current_mut().set_page(true);
|
self.cx.manager.current_mut().set_page(true);
|
||||||
self.cx.manager.active_mut().preview.reset();
|
|
||||||
self.cx.manager.peek(());
|
self.cx.manager.peek(());
|
||||||
emit!(Render);
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ impl App {
|
||||||
return self.cx.tasks.plugin_micro(&opt.name);
|
return self.cx.tasks.plugin_micro(&opt.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if LOADED.read().contains_key(&opt.name) {
|
||||||
|
return self.plugin_do(opt);
|
||||||
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if LOADED.ensure(&opt.name).await.is_ok() {
|
if LOADED.ensure(&opt.name).await.is_ok() {
|
||||||
emit!(Call(Exec::call("plugin_do", vec![opt.name]).with_data(opt.data).vec(), Layer::App));
|
emit!(Call(Exec::call("plugin_do", vec![opt.name]).with_data(opt.data).vec(), Layer::App));
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,16 @@ impl<'a> Preview<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Preview<'_> {
|
impl Widget for Preview<'_> {
|
||||||
fn render(self, _: ratatui::layout::Rect, buf: &mut Buffer) {
|
fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) {
|
||||||
let preview = &self.cx.manager.active().preview;
|
let preview = &self.cx.manager.active().preview;
|
||||||
let Some(lock) = &preview.lock else {
|
let Some(lock) = &preview.lock else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (lock.window.rows, lock.window.cols) != (area.height, area.width) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for w in &lock.data {
|
for w in &lock.data {
|
||||||
w.clone_render(buf);
|
w.clone_render(buf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ impl<'a, 'b> Active<'a, 'b> {
|
||||||
|
|
||||||
fn preview(&self, tab: &'a yazi_core::tab::Tab) -> mlua::Result<AnyUserData<'a>> {
|
fn preview(&self, tab: &'a yazi_core::tab::Tab) -> mlua::Result<AnyUserData<'a>> {
|
||||||
let inner = &tab.preview;
|
let inner = &tab.preview;
|
||||||
let window = || inner.lock.as_ref().map(|l| (l.skip, LAYOUT.load().preview.height as usize));
|
let window = Some((inner.skip, LAYOUT.load().preview.height as usize));
|
||||||
|
|
||||||
let ud = self.scope.create_any_userdata_ref(inner)?;
|
let ud = self.scope.create_any_userdata_ref(inner)?;
|
||||||
ud.set_named_user_value(
|
ud.set_named_user_value(
|
||||||
|
|
@ -70,7 +70,7 @@ impl<'a, 'b> Active<'a, 'b> {
|
||||||
.hovered()
|
.hovered()
|
||||||
.filter(|&f| f.is_dir())
|
.filter(|&f| f.is_dir())
|
||||||
.and_then(|f| tab.history(&f.url))
|
.and_then(|f| tab.history(&f.url))
|
||||||
.and_then(|f| Folder::new(self.scope, f).make(window()).ok()),
|
.and_then(|f| Folder::new(self.scope, f).make(window).ok()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ impl<'a, 'b> Folder<'a, 'b> {
|
||||||
|
|
||||||
pub(crate) fn make(&self, window: Option<(usize, usize)>) -> mlua::Result<AnyUserData<'a>> {
|
pub(crate) fn make(&self, window: Option<(usize, usize)>) -> mlua::Result<AnyUserData<'a>> {
|
||||||
let window =
|
let window =
|
||||||
window.unwrap_or_else(|| (self.inner.offset, LAYOUT.load().current.height as usize));
|
window.unwrap_or_else(|| (self.inner.offset, LAYOUT.load().preview.height as usize));
|
||||||
|
|
||||||
let ud = self.scope.create_any_userdata_ref(self.inner)?;
|
let ud = self.scope.create_any_userdata_ref(self.inner)?;
|
||||||
ud.set_named_user_value(
|
ud.set_named_user_value(
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl<'a> Widget for Root<'a> {
|
||||||
components::Header.render(chunks[0], buf);
|
components::Header.render(chunks[0], buf);
|
||||||
components::Manager.render(chunks[1], buf);
|
components::Manager.render(chunks[1], buf);
|
||||||
components::Status.render(chunks[2], buf);
|
components::Status.render(chunks[2], buf);
|
||||||
components::Preview::new(self.cx).render(chunks[2], buf);
|
components::Preview::new(self.cx).render(area, buf);
|
||||||
|
|
||||||
if self.cx.tasks.visible {
|
if self.cx.tasks.visible {
|
||||||
tasks::Layout::new(self.cx).render(area, buf);
|
tasks::Layout::new(self.cx).render(area, buf);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ impl Widget for Clear {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
ADAPTOR.image_hide(r).ok();
|
ADAPTOR.image_erase(r).ok();
|
||||||
COLLISION.store(true, Ordering::Relaxed);
|
COLLISION.store(true, Ordering::Relaxed);
|
||||||
for x in r.left()..r.right() {
|
for x in r.left()..r.right() {
|
||||||
for y in r.top()..r.bottom() {
|
for y in r.top()..r.bottom() {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.1.5" }
|
||||||
# External dependencies
|
# External dependencies
|
||||||
ansi-to-tui = "^3"
|
ansi-to-tui = "^3"
|
||||||
anyhow = "^1"
|
anyhow = "^1"
|
||||||
|
crossterm = "^0"
|
||||||
futures = "^0"
|
futures = "^0"
|
||||||
libc = "^0"
|
libc = "^0"
|
||||||
md-5 = "^0"
|
md-5 = "^0"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
local Archive = {}
|
local Archive = {}
|
||||||
|
|
||||||
function Archive:peek()
|
function Archive:peek()
|
||||||
local _, max = ya.preview_archive(self.area, self.file, self.skip)
|
local _, bound = ya.preview_archive(self)
|
||||||
if max then
|
if bound then
|
||||||
ya.manager_emit("peek", { tostring(max) })
|
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -11,7 +11,10 @@ function Archive:seek(units)
|
||||||
local h = cx.active.current.hovered
|
local h = cx.active.current.hovered
|
||||||
if h and h.url == self.file.url then
|
if h and h.url == self.file.url then
|
||||||
local step = math.floor(units * self.area.h / 10)
|
local step = math.floor(units * self.area.h / 10)
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + step)) })
|
ya.manager_emit("peek", {
|
||||||
|
tostring(math.max(0, cx.active.preview.skip + step)),
|
||||||
|
only_if = tostring(self.file.url),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
local Code = {}
|
local Code = {}
|
||||||
|
|
||||||
function Code:peek()
|
function Code:peek()
|
||||||
local _, max = ya.preview_code(self.area, self.file, self.skip)
|
local _, bound = ya.preview_code(self)
|
||||||
if max then
|
if bound then
|
||||||
ya.manager_emit("peek", { tostring(max) })
|
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -11,7 +11,10 @@ function Code:seek(units)
|
||||||
local h = cx.active.current.hovered
|
local h = cx.active.current.hovered
|
||||||
if h and h.url == self.file.url then
|
if h and h.url == self.file.url then
|
||||||
local step = math.floor(units * self.area.h / 10)
|
local step = math.floor(units * self.area.h / 10)
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + step)) })
|
ya.manager_emit("peek", {
|
||||||
|
tostring(math.max(0, cx.active.preview.skip + step)),
|
||||||
|
only_if = tostring(self.file.url),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,15 @@ local Folder_ = {}
|
||||||
|
|
||||||
function Folder_:peek()
|
function Folder_:peek()
|
||||||
local folder = Folder:by_kind(Folder.PREVIEW)
|
local folder = Folder:by_kind(Folder.PREVIEW)
|
||||||
if folder == nil then
|
if folder == nil or folder.cwd ~= self.file.url then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local bound = math.max(0, #folder.files - self.area.h)
|
||||||
|
if self.skip > bound then
|
||||||
|
ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
|
end
|
||||||
|
|
||||||
local items = {}
|
local items = {}
|
||||||
for _, f in ipairs(folder.window) do
|
for _, f in ipairs(folder.window) do
|
||||||
local item = ui.ListItem(ui.Line { Folder:icon(f), ui.Span(f.name) })
|
local item = ui.ListItem(ui.Line { Folder:icon(f), ui.Span(f.name) })
|
||||||
|
|
@ -16,11 +21,19 @@ function Folder_:peek()
|
||||||
end
|
end
|
||||||
items[#items + 1] = item
|
items[#items + 1] = item
|
||||||
end
|
end
|
||||||
ya.preview_widgets(self.file, self.skip, { ui.List(self.area, items) })
|
ya.preview_widgets(self, { ui.List(self.area, items) })
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder_:seek(units)
|
function Folder_:seek(units)
|
||||||
-- TODO
|
local folder = Folder:by_kind(Folder.PREVIEW)
|
||||||
|
if folder and folder.cwd == self.file.url then
|
||||||
|
local step = math.floor(units * self.area.h / 10)
|
||||||
|
local bound = math.max(0, #folder.files - self.area.h)
|
||||||
|
ya.manager_emit("peek", {
|
||||||
|
tostring(ya.clamp(0, cx.active.preview.skip + step, bound)),
|
||||||
|
only_if = tostring(self.file.url),
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Folder_
|
return Folder_
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ function Image:cache() return ya.cache_file(self.file.url .. tostring(self.file.
|
||||||
function Image:peek()
|
function Image:peek()
|
||||||
local cache = self:cache()
|
local cache = self:cache()
|
||||||
ya.image_show(fs.symlink_metadata(cache) and cache or self.file.url, self.area)
|
ya.image_show(fs.symlink_metadata(cache) and cache or self.file.url, self.area)
|
||||||
ya.preview_widgets(self.file, self.skip, {})
|
ya.preview_widgets(self, {})
|
||||||
end
|
end
|
||||||
|
|
||||||
function Image:seek() end
|
function Image:seek() end
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ function Json:peek()
|
||||||
|
|
||||||
child:start_kill()
|
child:start_kill()
|
||||||
if self.skip > 0 and i < self.skip + limit then
|
if self.skip > 0 and i < self.skip + limit then
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, i - limit)) })
|
ya.manager_emit("peek", { tostring(math.max(0, i - limit)), only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
else
|
else
|
||||||
lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size))
|
lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size))
|
||||||
ya.preview_widgets(self.file, self.skip, { ui.Paragraph.parse(self.area, lines) })
|
ya.preview_widgets(self, { ui.Paragraph.parse(self.area, lines) })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -39,7 +39,10 @@ function Json:seek(units)
|
||||||
local h = cx.active.current.hovered
|
local h = cx.active.current.hovered
|
||||||
if h and h.url == self.file.url then
|
if h and h.url == self.file.url then
|
||||||
local step = math.floor(units * self.area.h / 10)
|
local step = math.floor(units * self.area.h / 10)
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + step)) })
|
ya.manager_emit("peek", {
|
||||||
|
tostring(math.max(0, cx.active.preview.skip + step)),
|
||||||
|
only_if = tostring(self.file.url),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
9
yazi-plugin/preset/plugins/noop.lua
Normal file
9
yazi-plugin/preset/plugins/noop.lua
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
local Noop = {}
|
||||||
|
|
||||||
|
function Noop:peek() end
|
||||||
|
|
||||||
|
function Noop:seek() end
|
||||||
|
|
||||||
|
function Noop:preload() return 1 end
|
||||||
|
|
||||||
|
return Noop
|
||||||
|
|
@ -5,7 +5,7 @@ function Pdf:cache() return ya.cache_file(self.file.url .. self.skip .. tostring
|
||||||
function Pdf:peek()
|
function Pdf:peek()
|
||||||
if self:preload() == 1 then
|
if self:preload() == 1 then
|
||||||
ya.image_show(self:cache(), self.area)
|
ya.image_show(self:cache(), self.area)
|
||||||
ya.preview_widgets(self.file, self.skip, {})
|
ya.preview_widgets(self, {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ function Pdf:seek(units)
|
||||||
local h = cx.active.current.hovered
|
local h = cx.active.current.hovered
|
||||||
if h and h.url == self.file.url then
|
if h and h.url == self.file.url then
|
||||||
local step = ya.clamp(-1, units, 1)
|
local step = ya.clamp(-1, units, 1)
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + step)) })
|
ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + step)), only_if = tostring(self.file.url) })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ function Pdf:preload()
|
||||||
if not output.status:success() then
|
if not output.status:success() then
|
||||||
local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0
|
local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0
|
||||||
if self.skip > 0 and pages > 0 then
|
if self.skip > 0 and pages > 0 then
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, pages - 1)) })
|
ya.manager_emit("peek", { tostring(math.max(0, pages - 1)), only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,24 @@ function Video:cache() return ya.cache_file(self.file.url .. self.skip .. tostri
|
||||||
function Video:peek()
|
function Video:peek()
|
||||||
if self:preload() == 1 then
|
if self:preload() == 1 then
|
||||||
ya.image_show(self:cache(), self.area)
|
ya.image_show(self:cache(), self.area)
|
||||||
ya.preview_widgets(self.file, self.skip, {})
|
ya.preview_widgets(self, {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Video:seek(units)
|
function Video:seek(units)
|
||||||
local h = cx.active.current.hovered
|
local h = cx.active.current.hovered
|
||||||
if h and h.url == self.file.url then
|
if h and h.url == self.file.url then
|
||||||
ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + units)) })
|
ya.manager_emit("peek", {
|
||||||
|
tostring(math.max(0, cx.active.preview.skip + units)),
|
||||||
|
only_if = tostring(self.file.url),
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Video:preload()
|
function Video:preload()
|
||||||
local percentage = 5 + self.skip
|
local percentage = 5 + self.skip
|
||||||
if percentage > 95 then
|
if percentage > 95 then
|
||||||
ya.manager_emit("peek", { "90" })
|
ya.manager_emit("peek", { "90", only_if = tostring(self.file.url), upper_bound = "" })
|
||||||
return 2
|
return 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,14 @@ mod cha;
|
||||||
mod file;
|
mod file;
|
||||||
mod range;
|
mod range;
|
||||||
mod url;
|
mod url;
|
||||||
|
mod window;
|
||||||
|
|
||||||
pub use bindings::*;
|
pub use bindings::*;
|
||||||
pub use cha::*;
|
pub use cha::*;
|
||||||
pub use file::*;
|
pub use file::*;
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use url::*;
|
pub use url::*;
|
||||||
|
pub use window::*;
|
||||||
|
|
||||||
pub trait Cast<T> {
|
pub trait Cast<T> {
|
||||||
fn cast(lua: &mlua::Lua, data: T) -> mlua::Result<mlua::AnyUserData>;
|
fn cast(lua: &mlua::Lua, data: T) -> mlua::Result<mlua::AnyUserData>;
|
||||||
|
|
|
||||||
26
yazi-plugin/src/bindings/window.rs
Normal file
26
yazi-plugin/src/bindings/window.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
use mlua::{prelude::LuaUserDataFields, FromLua, UserData};
|
||||||
|
use yazi_shared::term::Term;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, FromLua)]
|
||||||
|
pub struct Window {
|
||||||
|
pub rows: u16,
|
||||||
|
pub cols: u16,
|
||||||
|
pub width: u16,
|
||||||
|
pub height: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Window {
|
||||||
|
fn default() -> Self {
|
||||||
|
let ws = Term::size();
|
||||||
|
Self { rows: ws.rows, cols: ws.columns, width: ws.width, height: ws.height }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for Window {
|
||||||
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("rows", |_, me| Ok(me.rows));
|
||||||
|
fields.add_field_method_get("cols", |_, me| Ok(me.cols));
|
||||||
|
fields.add_field_method_get("width", |_, me| Ok(me.width));
|
||||||
|
fields.add_field_method_get("height", |_, me| Ok(me.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ use yazi_config::LAYOUT;
|
||||||
use yazi_shared::{emit, event::Exec, Layer};
|
use yazi_shared::{emit, event::Exec, Layer};
|
||||||
|
|
||||||
use super::slim_lua;
|
use super::slim_lua;
|
||||||
use crate::{bindings::{Cast, File}, elements::Rect, OptData, LOADED, LUA};
|
use crate::{bindings::{Cast, File, Window}, elements::Rect, OptData, LOADED, LUA};
|
||||||
|
|
||||||
pub fn peek(exec: &Exec, file: yazi_shared::fs::File, skip: usize) -> CancellationToken {
|
pub fn peek(exec: &Exec, file: yazi_shared::fs::File, skip: usize) -> CancellationToken {
|
||||||
let ct = CancellationToken::new();
|
let ct = CancellationToken::new();
|
||||||
|
|
@ -33,6 +33,7 @@ pub fn peek(exec: &Exec, file: yazi_shared::fs::File, skip: usize) -> Cancellati
|
||||||
plugin.set("file", File::cast(&lua, file)?)?;
|
plugin.set("file", File::cast(&lua, file)?)?;
|
||||||
plugin.set("skip", skip)?;
|
plugin.set("skip", skip)?;
|
||||||
plugin.set("area", Rect::cast(&lua, LAYOUT.load().preview)?)?;
|
plugin.set("area", Rect::cast(&lua, LAYOUT.load().preview)?)?;
|
||||||
|
plugin.set("window", Window::default())?;
|
||||||
|
|
||||||
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", ()).await }
|
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", ()).await }
|
||||||
};
|
};
|
||||||
|
|
@ -59,6 +60,7 @@ pub fn peek_sync(exec: &Exec, file: yazi_shared::fs::File, skip: usize) {
|
||||||
plugin.set("file", File::cast(&LUA, file)?)?;
|
plugin.set("file", File::cast(&LUA, file)?)?;
|
||||||
plugin.set("skip", skip)?;
|
plugin.set("skip", skip)?;
|
||||||
plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
plugin.set("area", Rect::cast(&LUA, LAYOUT.load().preview)?)?;
|
||||||
|
plugin.set("window", Window::default())?;
|
||||||
plugin.call_method("peek", ())
|
plugin.call_method("peek", ())
|
||||||
})),
|
})),
|
||||||
tx: None,
|
tx: None,
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,13 @@ use yazi_config::LAYOUT;
|
||||||
use super::slim_lua;
|
use super::slim_lua;
|
||||||
use crate::{bindings::{Cast, File}, elements::Rect, LOADED};
|
use crate::{bindings::{Cast, File}, elements::Rect, LOADED};
|
||||||
|
|
||||||
pub async fn preload(name: String, files: Vec<yazi_shared::fs::File>) -> mlua::Result<u8> {
|
pub async fn preload(
|
||||||
|
name: String,
|
||||||
|
files: Vec<yazi_shared::fs::File>,
|
||||||
|
multi: bool,
|
||||||
|
) -> mlua::Result<u8> {
|
||||||
LOADED.ensure(&name).await.into_lua_err()?;
|
LOADED.ensure(&name).await.into_lua_err()?;
|
||||||
|
|
||||||
let multi = files.len() > 1;
|
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
let lua = slim_lua()?;
|
let lua = slim_lua()?;
|
||||||
let plugin: Table = if let Some(b) = LOADED.read().get(&name) {
|
let plugin: Table = if let Some(b) = LOADED.read().get(&name) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ impl Loader {
|
||||||
|
|
||||||
let b = fs::read(BOOT.plugin_dir.join(name)).await.map(|v| v.into()).unwrap_or(Cow::from(
|
let b = fs::read(BOOT.plugin_dir.join(name)).await.map(|v| v.into()).unwrap_or(Cow::from(
|
||||||
match name {
|
match name {
|
||||||
"noop.lua" => &[] as &[u8],
|
"noop.lua" => include_bytes!("../preset/plugins/noop.lua") as &[u8],
|
||||||
"archive.lua" => include_bytes!("../preset/plugins/archive.lua"),
|
"archive.lua" => include_bytes!("../preset/plugins/archive.lua"),
|
||||||
"code.lua" => include_bytes!("../preset/plugins/code.lua"),
|
"code.lua" => include_bytes!("../preset/plugins/code.lua"),
|
||||||
"folder.lua" => include_bytes!("../preset/plugins/folder.lua"),
|
"folder.lua" => include_bytes!("../preset/plugins/folder.lua"),
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ impl Utils {
|
||||||
args.push(v.to_str()?.to_owned());
|
args.push(v.to_str()?.to_owned());
|
||||||
}
|
}
|
||||||
Value::String(s) => {
|
Value::String(s) => {
|
||||||
named.insert(s.to_str()?.to_owned(), v.to_str()?.to_owned());
|
named.insert(s.to_str()?.replace('_', "-"), v.to_str()?.to_owned());
|
||||||
}
|
}
|
||||||
_ => return Err("invalid key in exec".into_lua_err()),
|
_ => return Err("invalid key in exec".into_lua_err()),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,87 +4,89 @@ use yazi_config::PREVIEW;
|
||||||
use yazi_shared::{emit, event::Exec, Layer, PeekError};
|
use yazi_shared::{emit, event::Exec, Layer, PeekError};
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
use crate::{bindings::FileRef, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::{self, Highlighter}};
|
use crate::{bindings::{FileRef, Window}, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::{self, Highlighter}};
|
||||||
|
|
||||||
pub struct PreviewLock {
|
pub struct PreviewLock {
|
||||||
pub url: yazi_shared::fs::Url,
|
pub url: yazi_shared::fs::Url,
|
||||||
pub cha: yazi_shared::fs::Cha,
|
pub cha: yazi_shared::fs::Cha,
|
||||||
|
|
||||||
pub skip: usize,
|
pub skip: usize,
|
||||||
pub data: Vec<Box<dyn Renderable + Send>>,
|
pub window: Window,
|
||||||
|
pub data: Vec<Box<dyn Renderable + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<Table<'a>> for PreviewLock {
|
||||||
|
type Error = mlua::Error;
|
||||||
|
|
||||||
|
fn try_from(t: Table) -> Result<Self, Self::Error> {
|
||||||
|
let file: FileRef = t.get("file")?;
|
||||||
|
Ok(Self {
|
||||||
|
url: file.url(),
|
||||||
|
cha: file.cha,
|
||||||
|
skip: t.get("skip")?,
|
||||||
|
window: t.get("window")?,
|
||||||
|
data: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Utils {
|
impl Utils {
|
||||||
pub(super) fn preview(lua: &Lua, ya: &Table) -> mlua::Result<()> {
|
pub(super) fn preview(lua: &Lua, ya: &Table) -> mlua::Result<()> {
|
||||||
ya.set(
|
ya.set(
|
||||||
"preview_code",
|
"preview_code",
|
||||||
lua.create_async_function(
|
lua.create_async_function(|lua, t: Table| async move {
|
||||||
|lua, (area, file, skip): (RectRef, FileRef, usize)| async move {
|
let area: RectRef = t.get("area")?;
|
||||||
let s = match Highlighter::new(&file.url).highlight(skip, area.height as usize).await {
|
let mut lock = PreviewLock::try_from(t)?;
|
||||||
Ok(s) => s.replace('\t', &" ".repeat(PREVIEW.tab_size as usize)),
|
|
||||||
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua),
|
|
||||||
Err(_) => return (false, Value::Nil).into_lua_multi(lua),
|
|
||||||
};
|
|
||||||
|
|
||||||
let lock = PreviewLock {
|
let s = match Highlighter::new(&lock.url).highlight(lock.skip, area.height as usize).await {
|
||||||
url: file.url(),
|
Ok(s) => s.replace('\t', &" ".repeat(PREVIEW.tab_size as usize)),
|
||||||
cha: file.cha,
|
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua),
|
||||||
skip,
|
Err(_) => return (false, Value::Nil).into_lua_multi(lua),
|
||||||
data: vec![Box::new(Paragraph {
|
};
|
||||||
area: *area,
|
|
||||||
text: s.into_text().into_lua_err()?,
|
|
||||||
..Default::default()
|
|
||||||
})],
|
|
||||||
};
|
|
||||||
|
|
||||||
emit!(Call(Exec::call("preview", vec![]).with_data(lock).vec(), Layer::Manager));
|
lock.data = vec![Box::new(Paragraph {
|
||||||
(true, Value::Nil).into_lua_multi(lua)
|
area: *area,
|
||||||
},
|
text: s.into_text().into_lua_err()?,
|
||||||
)?,
|
..Default::default()
|
||||||
|
})];
|
||||||
|
|
||||||
|
emit!(Call(Exec::call("preview", vec![]).with_data(lock).vec(), Layer::Manager));
|
||||||
|
(true, Value::Nil).into_lua_multi(lua)
|
||||||
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO: remove this once "archives as directories" feature is implemented
|
|
||||||
ya.set(
|
ya.set(
|
||||||
"preview_archive",
|
"preview_archive",
|
||||||
lua.create_async_function(
|
lua.create_async_function(|lua, t: Table| async move {
|
||||||
|lua, (area, file, skip): (RectRef, FileRef, usize)| async move {
|
let area: RectRef = t.get("area")?;
|
||||||
let lines: Vec<_> = match external::lsar(&file.url, skip, area.height as usize).await {
|
let mut lock = PreviewLock::try_from(t)?;
|
||||||
Ok(items) => items.into_iter().map(|f| ratatui::text::Line::from(f.name)).collect(),
|
|
||||||
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua),
|
|
||||||
Err(_) => return (false, Value::Nil).into_lua_multi(lua),
|
|
||||||
};
|
|
||||||
|
|
||||||
let lock = PreviewLock {
|
let lines: Vec<_> = match external::lsar(&lock.url, lock.skip, area.height as usize).await {
|
||||||
url: file.url(),
|
Ok(items) => items.into_iter().map(|f| ratatui::text::Line::from(f.name)).collect(),
|
||||||
cha: file.cha,
|
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua),
|
||||||
skip,
|
Err(_) => return (false, Value::Nil).into_lua_multi(lua),
|
||||||
data: vec![Box::new(Paragraph {
|
};
|
||||||
area: *area,
|
|
||||||
text: ratatui::text::Text::from(lines),
|
|
||||||
..Default::default()
|
|
||||||
})],
|
|
||||||
};
|
|
||||||
|
|
||||||
emit!(Call(Exec::call("preview", vec![]).with_data(lock).vec(), Layer::Manager));
|
lock.data = vec![Box::new(Paragraph {
|
||||||
(true, Value::Nil).into_lua_multi(lua)
|
area: *area,
|
||||||
},
|
text: ratatui::text::Text::from(lines),
|
||||||
)?,
|
..Default::default()
|
||||||
|
})];
|
||||||
|
|
||||||
|
emit!(Call(Exec::call("preview", vec![]).with_data(lock).vec(), Layer::Manager));
|
||||||
|
(true, Value::Nil).into_lua_multi(lua)
|
||||||
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
ya.set(
|
ya.set(
|
||||||
"preview_widgets",
|
"preview_widgets",
|
||||||
lua.create_async_function(
|
lua.create_async_function(|_, (t, widgets): (Table, Vec<AnyUserData>)| async move {
|
||||||
|_, (file, skip, widgets): (FileRef, usize, Vec<AnyUserData>)| async move {
|
let mut lock = PreviewLock::try_from(t)?;
|
||||||
let lock = PreviewLock {
|
lock.data = widgets.into_iter().filter_map(cast_to_renderable).collect();
|
||||||
url: file.url(),
|
|
||||||
cha: file.cha,
|
emit!(Call(Exec::call("preview", vec![]).with_data(lock).vec(), Layer::Manager));
|
||||||
skip,
|
Ok(())
|
||||||
data: widgets.into_iter().filter_map(cast_to_renderable).collect(),
|
})?,
|
||||||
};
|
|
||||||
emit!(Call(Exec::call("preview", vec![]).with_data(lock).vec(), Layer::Manager));
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)?,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -308,11 +308,11 @@ impl Scheduler {
|
||||||
_ = self.todo.send_blocking({
|
_ = self.todo.send_blocking({
|
||||||
let preload = self.preload.clone();
|
let preload = self.preload.clone();
|
||||||
|
|
||||||
let rule_id = rule.id;
|
let (rule_id, rule_multi) = (rule.id, rule.multi);
|
||||||
let cmd = rule.exec.cmd.clone();
|
let cmd = rule.exec.cmd.clone();
|
||||||
let targets = targets.into_iter().cloned().collect();
|
let targets = targets.into_iter().cloned().collect();
|
||||||
async move {
|
async move {
|
||||||
preload.rule(PreloadOpRule { id, rule_id, plugin: cmd, targets }).await.ok();
|
preload.rule(PreloadOpRule { id, rule_id, rule_multi, plugin: cmd, targets }).await.ok();
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,11 @@ pub struct PreloadOpSize {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PreloadOpRule {
|
pub struct PreloadOpRule {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub rule_id: u8,
|
pub rule_id: u8,
|
||||||
pub plugin: String,
|
pub rule_multi: bool,
|
||||||
pub targets: Vec<yazi_shared::fs::File>,
|
pub plugin: String,
|
||||||
|
pub targets: Vec<yazi_shared::fs::File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Preload {
|
impl Preload {
|
||||||
|
|
@ -39,7 +40,7 @@ impl Preload {
|
||||||
self.sch.send(TaskOp::New(task.id, 0))?;
|
self.sch.send(TaskOp::New(task.id, 0))?;
|
||||||
|
|
||||||
let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect();
|
let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect();
|
||||||
let result = yazi_plugin::isolate::preload(task.plugin, task.targets).await;
|
let result = yazi_plugin::isolate::preload(task.plugin, task.targets, task.rule_multi).await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
self.fail(task.id, format!("Preload task failed:\n{e}"))?;
|
self.fail(task.id, format!("Preload task failed:\n{e}"))?;
|
||||||
return Err(e.into());
|
return Err(e.into());
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue