mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: support customizable alignment for image display
This commit is contained in:
parent
62ac224cff
commit
cf43e11ada
15 changed files with 262 additions and 192 deletions
|
|
@ -5,7 +5,7 @@ use ratatui::layout::Rect;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use yazi_shared::env_exists;
|
use yazi_shared::env_exists;
|
||||||
|
|
||||||
use crate::{Brand, Emulator, SHOWN, TMUX, WSL, drivers};
|
use crate::{Brand, Emulator, Offset, SHOWN, TMUX, WSL, drivers};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum Adapter {
|
pub enum Adapter {
|
||||||
|
|
@ -35,18 +35,18 @@ impl Display for Adapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Adapter {
|
impl Adapter {
|
||||||
pub async fn image_show(self, path: &Path, max: Rect) -> Result<Rect> {
|
pub async fn image_show(self, path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
|
||||||
if max.is_empty() {
|
if max.is_empty() {
|
||||||
return Ok(Rect::default());
|
return Ok(Rect::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Kgp => drivers::Kgp::image_show(path, max).await,
|
Self::Kgp => drivers::Kgp::image_show(path, max, offset).await,
|
||||||
Self::KgpOld => drivers::KgpOld::image_show(path, max).await,
|
Self::KgpOld => drivers::KgpOld::image_show(path, max, offset).await,
|
||||||
Self::Iip => drivers::Iip::image_show(path, max).await,
|
Self::Iip => drivers::Iip::image_show(path, max, offset).await,
|
||||||
Self::Sixel => drivers::Sixel::image_show(path, max).await,
|
Self::Sixel => drivers::Sixel::image_show(path, max, offset).await,
|
||||||
Self::X11 | Self::Wayland => drivers::Ueberzug::image_show(path, max).await,
|
Self::X11 | Self::Wayland => drivers::Ueberzug::image_show(path, max, offset).await,
|
||||||
Self::Chafa => drivers::Chafa::image_show(path, max).await,
|
Self::Chafa => drivers::Chafa::image_show(path, max, offset).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@ use std::{io::Write, path::Path, process::Stdio};
|
||||||
use ansi_to_tui::IntoText;
|
use ansi_to_tui::IntoText;
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use crossterm::{cursor::MoveTo, queue};
|
use crossterm::{cursor::MoveTo, queue};
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::{Rect, Size};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::{Adapter, Emulator};
|
use crate::{Adapter, Emulator, Offset};
|
||||||
|
|
||||||
pub(crate) struct Chafa;
|
pub(crate) struct Chafa;
|
||||||
|
|
||||||
impl Chafa {
|
impl Chafa {
|
||||||
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
pub(crate) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
|
||||||
let child = Command::new("chafa")
|
let child = Command::new("chafa")
|
||||||
.args([
|
.args([
|
||||||
"-f",
|
"-f",
|
||||||
|
|
@ -46,11 +46,13 @@ impl Chafa {
|
||||||
bail!("failed to parse chafa output");
|
bail!("failed to parse chafa output");
|
||||||
};
|
};
|
||||||
|
|
||||||
let area = Rect {
|
let area = {
|
||||||
x: max.x,
|
let width = first.width() as u16;
|
||||||
y: max.y,
|
let height = lines.len() as u16;
|
||||||
width: first.width() as u16,
|
let offset = offset.unwrap_or_else(|| {
|
||||||
height: lines.len() as u16,
|
Offset::from((Size { width, height }, Size { width: max.width, height: max.height }))
|
||||||
|
});
|
||||||
|
Rect { x: max.x + offset.x, y: max.y + offset.y, width, height }
|
||||||
};
|
};
|
||||||
|
|
||||||
Adapter::Chafa.image_hide()?;
|
Adapter::Chafa.image_hide()?;
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@ use image::{DynamicImage, ExtendedColorType, ImageEncoder, codecs::{jpeg::JpegEn
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use yazi_config::PREVIEW;
|
use yazi_config::PREVIEW;
|
||||||
|
|
||||||
use crate::{CLOSE, Emulator, Image, START, adapter::Adapter};
|
use crate::{CLOSE, Emulator, Image, Offset, START, adapter::Adapter};
|
||||||
|
|
||||||
pub(crate) struct Iip;
|
pub(crate) struct Iip;
|
||||||
|
|
||||||
impl Iip {
|
impl Iip {
|
||||||
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
pub(crate) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
|
||||||
let img = Image::downscale(path, max).await?;
|
let img = Image::downscale(path, max).await?;
|
||||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
let area = Image::pixel_area((img.width(), img.height()), max, offset);
|
||||||
let b = Self::encode(img).await?;
|
let b = Self::encode(img).await?;
|
||||||
|
|
||||||
Adapter::Iip.image_hide()?;
|
Adapter::Iip.image_hide()?;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use crossterm::{cursor::MoveTo, queue};
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
|
|
||||||
use crate::{CLOSE, ESCAPE, Emulator, START, adapter::Adapter, image::Image};
|
use crate::{CLOSE, ESCAPE, Emulator, Offset, START, adapter::Adapter, image::Image};
|
||||||
|
|
||||||
static DIACRITICS: [char; 297] = [
|
static DIACRITICS: [char; 297] = [
|
||||||
'\u{0305}',
|
'\u{0305}',
|
||||||
|
|
@ -312,9 +312,9 @@ static DIACRITICS: [char; 297] = [
|
||||||
pub(crate) struct Kgp;
|
pub(crate) struct Kgp;
|
||||||
|
|
||||||
impl Kgp {
|
impl Kgp {
|
||||||
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
pub(crate) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
|
||||||
let img = Image::downscale(path, max).await?;
|
let img = Image::downscale(path, max).await?;
|
||||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
let area = Image::pixel_area((img.width(), img.height()), max, offset);
|
||||||
|
|
||||||
let b1 = Self::encode(img).await?;
|
let b1 = Self::encode(img).await?;
|
||||||
let b2 = Self::place(&area)?;
|
let b2 = Self::place(&area)?;
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@ use base64::{Engine, engine::general_purpose};
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
|
|
||||||
use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};
|
use crate::{CLOSE, ESCAPE, Emulator, Image, Offset, START, adapter::Adapter};
|
||||||
|
|
||||||
pub(crate) struct KgpOld;
|
pub(crate) struct KgpOld;
|
||||||
|
|
||||||
impl KgpOld {
|
impl KgpOld {
|
||||||
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
pub(crate) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
|
||||||
let img = Image::downscale(path, max).await?;
|
let img = Image::downscale(path, max).await?;
|
||||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
let area = Image::pixel_area((img.width(), img.height()), max, offset);
|
||||||
let b = Self::encode(img).await?;
|
let b = Self::encode(img).await?;
|
||||||
|
|
||||||
Adapter::KgpOld.image_hide()?;
|
Adapter::KgpOld.image_hide()?;
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@ use image::DynamicImage;
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use yazi_config::PREVIEW;
|
use yazi_config::PREVIEW;
|
||||||
|
|
||||||
use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};
|
use crate::{CLOSE, ESCAPE, Emulator, Image, Offset, START, adapter::Adapter};
|
||||||
|
|
||||||
pub(crate) struct Sixel;
|
pub(crate) struct Sixel;
|
||||||
|
|
||||||
impl Sixel {
|
impl Sixel {
|
||||||
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
pub(crate) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
|
||||||
let img = Image::downscale(path, max).await?;
|
let img = Image::downscale(path, max).await?;
|
||||||
let area = Image::pixel_area((img.width(), img.height()), max);
|
let area = Image::pixel_area((img.width(), img.height()), max, offset);
|
||||||
let b = Self::encode(img).await?;
|
let b = Self::encode(img).await?;
|
||||||
|
|
||||||
Adapter::Sixel.image_hide()?;
|
Adapter::Sixel.image_hide()?;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use std::{path::{Path, PathBuf}, process::Stdio};
|
use std::{path::{Path, PathBuf}, process::Stdio};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::{Rect, Size};
|
||||||
use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}};
|
use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use yazi_config::PREVIEW;
|
use yazi_config::PREVIEW;
|
||||||
use yazi_shared::{RoCell, env_exists};
|
use yazi_shared::{RoCell, env_exists};
|
||||||
|
|
||||||
use crate::{Adapter, Dimension};
|
use crate::{Adapter, Dimension, Offset};
|
||||||
|
|
||||||
type Cmd = Option<(PathBuf, Rect)>;
|
type Cmd = Option<(PathBuf, Rect)>;
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ impl Ueberzug {
|
||||||
DEMON.init(Some(tx))
|
DEMON.init(Some(tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn image_show(path: &Path, max: Rect) -> Result<Rect> {
|
pub(crate) async fn image_show(path: &Path, max: Rect, offset: Option<Offset>) -> Result<Rect> {
|
||||||
let Some(tx) = &*DEMON else {
|
let Some(tx) = &*DEMON else {
|
||||||
bail!("uninitialized ueberzugpp");
|
bail!("uninitialized ueberzugpp");
|
||||||
};
|
};
|
||||||
|
|
@ -50,11 +50,13 @@ impl Ueberzug {
|
||||||
let (w, h) = tokio::task::spawn_blocking(move || image::image_dimensions(p)).await??;
|
let (w, h) = tokio::task::spawn_blocking(move || image::image_dimensions(p)).await??;
|
||||||
|
|
||||||
let area = Dimension::ratio()
|
let area = Dimension::ratio()
|
||||||
.map(|(r1, r2)| Rect {
|
.map(|(r1, r2)| {
|
||||||
x: max.x,
|
let width = max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _);
|
||||||
y: max.y,
|
let height = max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _);
|
||||||
width: max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _),
|
let offset = offset.unwrap_or_else(|| {
|
||||||
height: max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _),
|
Offset::from((Size { width, height }, Size { width: max.width, height: max.height }))
|
||||||
|
});
|
||||||
|
Rect { x: max.x + offset.x, y: max.y + offset.y, width, height }
|
||||||
})
|
})
|
||||||
.unwrap_or(max);
|
.unwrap_or(max);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation};
|
use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation};
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::{Rect, Size};
|
||||||
use yazi_config::{PREVIEW, TASKS};
|
use yazi_config::{PREVIEW, TASKS};
|
||||||
|
|
||||||
use crate::Dimension;
|
use crate::{Dimension, Offset};
|
||||||
|
|
||||||
pub struct Image;
|
pub struct Image;
|
||||||
|
|
||||||
|
|
@ -73,13 +73,15 @@ impl Image {
|
||||||
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height))
|
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn pixel_area(size: (u32, u32), rect: Rect) -> Rect {
|
pub(super) fn pixel_area(size: (u32, u32), rect: Rect, offset: Option<Offset>) -> Rect {
|
||||||
Dimension::ratio()
|
Dimension::ratio()
|
||||||
.map(|(r1, r2)| Rect {
|
.map(|(r1, r2)| {
|
||||||
x: rect.x,
|
let width = (size.0 as f64 / r1).ceil() as u16;
|
||||||
y: rect.y,
|
let height = (size.1 as f64 / r2).ceil() as u16;
|
||||||
width: (size.0 as f64 / r1).ceil() as u16,
|
let offset = offset.unwrap_or_else(|| {
|
||||||
height: (size.1 as f64 / r2).ceil() as u16,
|
Offset::from((Size { width, height }, Size { width: rect.width, height: rect.height }))
|
||||||
|
});
|
||||||
|
Rect { x: rect.x + offset.x, y: rect.y + offset.y, width, height }
|
||||||
})
|
})
|
||||||
.unwrap_or(rect)
|
.unwrap_or(rect)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
yazi_macro::mod_pub!(drivers);
|
yazi_macro::mod_pub!(drivers);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
|
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux offset unknown);
|
||||||
|
|
||||||
use yazi_shared::{RoCell, SyncCell, env_exists, in_wsl};
|
use yazi_shared::{RoCell, SyncCell, env_exists, in_wsl};
|
||||||
|
|
||||||
|
|
|
||||||
26
yazi-adapter/src/offset.rs
Normal file
26
yazi-adapter/src/offset.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
use ratatui::layout::Size;
|
||||||
|
use yazi_config::{PREVIEW, preview::{HorizontalAlignment, VerticalAlignment}};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub struct Offset {
|
||||||
|
pub x: u16,
|
||||||
|
pub y: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(Size, Size)> for Offset {
|
||||||
|
fn from(value: (Size, Size)) -> Self {
|
||||||
|
let inner = value.0;
|
||||||
|
let outer = value.1;
|
||||||
|
let offset_x = match PREVIEW.alignment.horizontal {
|
||||||
|
HorizontalAlignment::Left => 0,
|
||||||
|
HorizontalAlignment::Center => (outer.width - inner.width) / 2,
|
||||||
|
HorizontalAlignment::Right => outer.width - inner.width,
|
||||||
|
};
|
||||||
|
let offset_y = match PREVIEW.alignment.vertical {
|
||||||
|
VerticalAlignment::Top => 0,
|
||||||
|
VerticalAlignment::Center => (outer.height - inner.height) / 2,
|
||||||
|
VerticalAlignment::Bottom => outer.height - inner.height,
|
||||||
|
};
|
||||||
|
Self { x: offset_x, y: offset_y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,214 +3,215 @@
|
||||||
"$schema" = "https://yazi-rs.github.io/schemas/yazi.json"
|
"$schema" = "https://yazi-rs.github.io/schemas/yazi.json"
|
||||||
|
|
||||||
[manager]
|
[manager]
|
||||||
ratio = [ 1, 4, 3 ]
|
ratio = [1, 4, 3]
|
||||||
sort_by = "alphabetical"
|
sort_by = "alphabetical"
|
||||||
sort_sensitive = false
|
sort_sensitive = false
|
||||||
sort_reverse = false
|
sort_reverse = false
|
||||||
sort_dir_first = true
|
sort_dir_first = true
|
||||||
sort_translit = false
|
sort_translit = false
|
||||||
linemode = "none"
|
linemode = "none"
|
||||||
show_hidden = false
|
show_hidden = false
|
||||||
show_symlink = true
|
show_symlink = true
|
||||||
scrolloff = 5
|
scrolloff = 5
|
||||||
mouse_events = [ "click", "scroll" ]
|
mouse_events = ["click", "scroll"]
|
||||||
title_format = "Yazi: {cwd}"
|
title_format = "Yazi: {cwd}"
|
||||||
|
|
||||||
[preview]
|
[preview]
|
||||||
wrap = "no"
|
wrap = "no"
|
||||||
tab_size = 2
|
tab_size = 2
|
||||||
max_width = 600
|
max_width = 600
|
||||||
max_height = 900
|
max_height = 900
|
||||||
cache_dir = ""
|
alignment = { horizontal = "center", vertical = "top" }
|
||||||
image_delay = 30
|
cache_dir = ""
|
||||||
image_filter = "triangle"
|
image_delay = 30
|
||||||
image_quality = 75
|
image_filter = "triangle"
|
||||||
sixel_fraction = 15
|
image_quality = 75
|
||||||
ueberzug_scale = 1
|
sixel_fraction = 15
|
||||||
ueberzug_offset = [ 0, 0, 0, 0 ]
|
ueberzug_scale = 1
|
||||||
|
ueberzug_offset = [0, 0, 0, 0]
|
||||||
|
|
||||||
[opener]
|
[opener]
|
||||||
edit = [
|
edit = [
|
||||||
{ run = '${EDITOR:-vi} "$@"', desc = "$EDITOR", block = true, for = "unix" },
|
{ run = '${EDITOR:-vi} "$@"', desc = "$EDITOR", block = true, for = "unix" },
|
||||||
{ run = 'code %*', orphan = true, desc = "code", for = "windows" },
|
{ run = 'code %*', orphan = true, desc = "code", for = "windows" },
|
||||||
{ run = 'code -w %*', block = true, desc = "code (block)", for = "windows" },
|
{ run = 'code -w %*', block = true, desc = "code (block)", for = "windows" },
|
||||||
]
|
]
|
||||||
open = [
|
open = [
|
||||||
{ run = 'xdg-open "$1"', desc = "Open", for = "linux" },
|
{ run = 'xdg-open "$1"', desc = "Open", for = "linux" },
|
||||||
{ run = 'open "$@"', desc = "Open", for = "macos" },
|
{ run = 'open "$@"', desc = "Open", for = "macos" },
|
||||||
{ run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" },
|
{ run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" },
|
||||||
]
|
]
|
||||||
reveal = [
|
reveal = [
|
||||||
{ run = 'xdg-open "$(dirname "$1")"', desc = "Reveal", for = "linux" },
|
{ run = 'xdg-open "$(dirname "$1")"', desc = "Reveal", for = "linux" },
|
||||||
{ run = 'open -R "$1"', desc = "Reveal", for = "macos" },
|
{ run = 'open -R "$1"', desc = "Reveal", for = "macos" },
|
||||||
{ run = 'explorer /select,"%1"', orphan = true, desc = "Reveal", for = "windows" },
|
{ run = 'explorer /select,"%1"', orphan = true, desc = "Reveal", for = "windows" },
|
||||||
{ run = '''exiftool "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show EXIF", for = "unix" },
|
{ run = '''exiftool "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show EXIF", for = "unix" },
|
||||||
]
|
]
|
||||||
extract = [
|
extract = [
|
||||||
{ run = 'ya pub extract --list "$@"', desc = "Extract here", for = "unix" },
|
{ run = 'ya pub extract --list "$@"', desc = "Extract here", for = "unix" },
|
||||||
{ run = 'ya pub extract --list %*', desc = "Extract here", for = "windows" },
|
{ run = 'ya pub extract --list %*', desc = "Extract here", for = "windows" },
|
||||||
]
|
]
|
||||||
play = [
|
play = [
|
||||||
{ run = 'mpv --force-window "$@"', orphan = true, for = "unix" },
|
{ run = 'mpv --force-window "$@"', orphan = true, for = "unix" },
|
||||||
{ run = 'mpv --force-window %*', orphan = true, for = "windows" },
|
{ run = 'mpv --force-window %*', orphan = true, for = "windows" },
|
||||||
{ run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
|
{ run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[open]
|
[open]
|
||||||
rules = [
|
rules = [
|
||||||
# Folder
|
# Folder
|
||||||
{ name = "*/", use = [ "edit", "open", "reveal" ] },
|
{ name = "*/", use = ["edit", "open", "reveal"] },
|
||||||
# Text
|
# Text
|
||||||
{ mime = "text/*", use = [ "edit", "reveal" ] },
|
{ mime = "text/*", use = ["edit", "reveal"] },
|
||||||
# Image
|
# Image
|
||||||
{ mime = "image/*", use = [ "open", "reveal" ] },
|
{ mime = "image/*", use = ["open", "reveal"] },
|
||||||
# Media
|
# Media
|
||||||
{ mime = "{audio,video}/*", use = [ "play", "reveal" ] },
|
{ mime = "{audio,video}/*", use = ["play", "reveal"] },
|
||||||
# Archive
|
# Archive
|
||||||
{ mime = "application/{,g}zip", use = [ "extract", "reveal" ] },
|
{ mime = "application/{,g}zip", use = ["extract", "reveal"] },
|
||||||
{ mime = "application/{tar,bzip*,7z*,xz,rar}", use = [ "extract", "reveal" ] },
|
{ mime = "application/{tar,bzip*,7z*,xz,rar}", use = ["extract", "reveal"] },
|
||||||
# JSON
|
# JSON
|
||||||
{ mime = "application/{json,ndjson}", use = [ "edit", "reveal" ] },
|
{ mime = "application/{json,ndjson}", use = ["edit", "reveal"] },
|
||||||
{ mime = "*/javascript", use = [ "edit", "reveal" ] },
|
{ mime = "*/javascript", use = ["edit", "reveal"] },
|
||||||
# Empty file
|
# Empty file
|
||||||
{ mime = "inode/empty", use = [ "edit", "reveal" ] },
|
{ mime = "inode/empty", use = ["edit", "reveal"] },
|
||||||
# Fallback
|
# Fallback
|
||||||
{ name = "*", use = [ "open", "reveal" ] },
|
{ name = "*", use = ["open", "reveal"] },
|
||||||
]
|
]
|
||||||
|
|
||||||
[tasks]
|
[tasks]
|
||||||
micro_workers = 10
|
micro_workers = 10
|
||||||
macro_workers = 25
|
macro_workers = 25
|
||||||
bizarre_retry = 5
|
bizarre_retry = 5
|
||||||
image_alloc = 536870912 # 512MB
|
image_alloc = 536870912 # 512MB
|
||||||
image_bound = [ 0, 0 ]
|
image_bound = [0, 0]
|
||||||
suppress_preload = false
|
suppress_preload = false
|
||||||
|
|
||||||
[plugin]
|
[plugin]
|
||||||
|
|
||||||
fetchers = [
|
fetchers = [
|
||||||
# Mimetype
|
# Mimetype
|
||||||
{ id = "mime", name = "*", run = "mime", if = "!mime", prio = "high" },
|
{ id = "mime", name = "*", run = "mime", if = "!mime", prio = "high" },
|
||||||
]
|
]
|
||||||
spotters = [
|
spotters = [
|
||||||
{ name = "*/", run = "folder" },
|
{ name = "*/", run = "folder" },
|
||||||
# Code
|
# Code
|
||||||
{ mime = "text/*", run = "code" },
|
{ mime = "text/*", run = "code" },
|
||||||
{ mime = "*/{xml,javascript,wine-extension-ini}", run = "code" },
|
{ mime = "*/{xml,javascript,wine-extension-ini}", run = "code" },
|
||||||
# Image
|
# Image
|
||||||
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
|
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
|
||||||
{ mime = "image/*", run = "image" },
|
{ mime = "image/*", run = "image" },
|
||||||
# Video
|
# Video
|
||||||
{ mime = "video/*", run = "video" },
|
{ mime = "video/*", run = "video" },
|
||||||
# Fallback
|
# Fallback
|
||||||
{ name = "*", run = "file" },
|
{ name = "*", run = "file" },
|
||||||
]
|
]
|
||||||
preloaders = [
|
preloaders = [
|
||||||
# Image
|
# Image
|
||||||
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
|
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
|
||||||
{ mime = "image/*", run = "image" },
|
{ mime = "image/*", run = "image" },
|
||||||
# Video
|
# Video
|
||||||
{ mime = "video/*", run = "video" },
|
{ mime = "video/*", run = "video" },
|
||||||
# PDF
|
# PDF
|
||||||
{ mime = "application/pdf", run = "pdf" },
|
{ mime = "application/pdf", run = "pdf" },
|
||||||
# Font
|
# Font
|
||||||
{ mime = "font/*", run = "font" },
|
{ mime = "font/*", run = "font" },
|
||||||
{ mime = "application/ms-opentype", run = "font" },
|
{ mime = "application/ms-opentype", run = "font" },
|
||||||
]
|
]
|
||||||
previewers = [
|
previewers = [
|
||||||
{ name = "*/", run = "folder", sync = true },
|
{ name = "*/", run = "folder", sync = true },
|
||||||
# Code
|
# Code
|
||||||
{ mime = "text/*", run = "code" },
|
{ mime = "text/*", run = "code" },
|
||||||
{ mime = "*/{xml,javascript,wine-extension-ini}", run = "code" },
|
{ mime = "*/{xml,javascript,wine-extension-ini}", run = "code" },
|
||||||
# JSON
|
# JSON
|
||||||
{ mime = "application/{json,ndjson}", run = "json" },
|
{ mime = "application/{json,ndjson}", run = "json" },
|
||||||
# Image
|
# Image
|
||||||
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
|
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
|
||||||
{ mime = "image/*", run = "image" },
|
{ mime = "image/*", run = "image" },
|
||||||
# Video
|
# Video
|
||||||
{ mime = "video/*", run = "video" },
|
{ mime = "video/*", run = "video" },
|
||||||
# PDF
|
# PDF
|
||||||
{ mime = "application/pdf", run = "pdf" },
|
{ mime = "application/pdf", run = "pdf" },
|
||||||
# Archive
|
# Archive
|
||||||
{ mime = "application/{,g}zip", run = "archive" },
|
{ mime = "application/{,g}zip", run = "archive" },
|
||||||
{ mime = "application/{tar,bzip*,7z*,xz,rar,iso9660-image}", run = "archive" },
|
{ mime = "application/{tar,bzip*,7z*,xz,rar,iso9660-image}", run = "archive" },
|
||||||
# Font
|
# Font
|
||||||
{ mime = "font/*", run = "font" },
|
{ mime = "font/*", run = "font" },
|
||||||
{ mime = "application/ms-opentype", run = "font" },
|
{ mime = "application/ms-opentype", run = "font" },
|
||||||
# Empty file
|
# Empty file
|
||||||
{ mime = "inode/empty", run = "empty" },
|
{ mime = "inode/empty", run = "empty" },
|
||||||
# Fallback
|
# Fallback
|
||||||
{ name = "*", run = "file" },
|
{ name = "*", run = "file" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[input]
|
[input]
|
||||||
cursor_blink = false
|
cursor_blink = false
|
||||||
|
|
||||||
# cd
|
# cd
|
||||||
cd_title = "Change directory:"
|
cd_title = "Change directory:"
|
||||||
cd_origin = "top-center"
|
cd_origin = "top-center"
|
||||||
cd_offset = [ 0, 2, 50, 3 ]
|
cd_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
# create
|
# create
|
||||||
create_title = [ "Create:", "Create (dir):" ]
|
create_title = ["Create:", "Create (dir):"]
|
||||||
create_origin = "top-center"
|
create_origin = "top-center"
|
||||||
create_offset = [ 0, 2, 50, 3 ]
|
create_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
# rename
|
# rename
|
||||||
rename_title = "Rename:"
|
rename_title = "Rename:"
|
||||||
rename_origin = "hovered"
|
rename_origin = "hovered"
|
||||||
rename_offset = [ 0, 1, 50, 3 ]
|
rename_offset = [0, 1, 50, 3]
|
||||||
|
|
||||||
# filter
|
# filter
|
||||||
filter_title = "Filter:"
|
filter_title = "Filter:"
|
||||||
filter_origin = "top-center"
|
filter_origin = "top-center"
|
||||||
filter_offset = [ 0, 2, 50, 3 ]
|
filter_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
# find
|
# find
|
||||||
find_title = [ "Find next:", "Find previous:" ]
|
find_title = ["Find next:", "Find previous:"]
|
||||||
find_origin = "top-center"
|
find_origin = "top-center"
|
||||||
find_offset = [ 0, 2, 50, 3 ]
|
find_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
# search
|
# search
|
||||||
search_title = "Search via {n}:"
|
search_title = "Search via {n}:"
|
||||||
search_origin = "top-center"
|
search_origin = "top-center"
|
||||||
search_offset = [ 0, 2, 50, 3 ]
|
search_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
# shell
|
# shell
|
||||||
shell_title = [ "Shell:", "Shell (block):" ]
|
shell_title = ["Shell:", "Shell (block):"]
|
||||||
shell_origin = "top-center"
|
shell_origin = "top-center"
|
||||||
shell_offset = [ 0, 2, 50, 3 ]
|
shell_offset = [0, 2, 50, 3]
|
||||||
|
|
||||||
[confirm]
|
[confirm]
|
||||||
# trash
|
# trash
|
||||||
trash_title = "Trash {n} selected file{s}?"
|
trash_title = "Trash {n} selected file{s}?"
|
||||||
trash_origin = "center"
|
trash_origin = "center"
|
||||||
trash_offset = [ 0, 0, 70, 20 ]
|
trash_offset = [0, 0, 70, 20]
|
||||||
|
|
||||||
# delete
|
# delete
|
||||||
delete_title = "Permanently delete {n} selected file{s}?"
|
delete_title = "Permanently delete {n} selected file{s}?"
|
||||||
delete_origin = "center"
|
delete_origin = "center"
|
||||||
delete_offset = [ 0, 0, 70, 20 ]
|
delete_offset = [0, 0, 70, 20]
|
||||||
|
|
||||||
# overwrite
|
# overwrite
|
||||||
overwrite_title = "Overwrite file?"
|
overwrite_title = "Overwrite file?"
|
||||||
overwrite_content = "Will overwrite the following file:"
|
overwrite_content = "Will overwrite the following file:"
|
||||||
overwrite_origin = "center"
|
overwrite_origin = "center"
|
||||||
overwrite_offset = [ 0, 0, 50, 15 ]
|
overwrite_offset = [0, 0, 50, 15]
|
||||||
|
|
||||||
# quit
|
# quit
|
||||||
quit_title = "Quit?"
|
quit_title = "Quit?"
|
||||||
quit_content = "The following task is still running, are you sure you want to quit?"
|
quit_content = "The following task is still running, are you sure you want to quit?"
|
||||||
quit_origin = "center"
|
quit_origin = "center"
|
||||||
quit_offset = [ 0, 0, 50, 15 ]
|
quit_offset = [0, 0, 50, 15]
|
||||||
|
|
||||||
[pick]
|
[pick]
|
||||||
open_title = "Open with:"
|
open_title = "Open with:"
|
||||||
open_origin = "hovered"
|
open_origin = "hovered"
|
||||||
open_offset = [ 0, 1, 50, 7 ]
|
open_offset = [0, 1, 50, 7]
|
||||||
|
|
||||||
[which]
|
[which]
|
||||||
sort_by = "none"
|
sort_by = "none"
|
||||||
sort_sensitive = false
|
sort_sensitive = false
|
||||||
sort_reverse = false
|
sort_reverse = false
|
||||||
sort_translit = false
|
sort_translit = false
|
||||||
|
|
|
||||||
27
yazi-config/src/preview/alignment.rs
Normal file
27
yazi-config/src/preview/alignment.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum HorizontalAlignment {
|
||||||
|
Left,
|
||||||
|
#[default]
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum VerticalAlignment {
|
||||||
|
#[default]
|
||||||
|
Top,
|
||||||
|
Center,
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||||
|
pub struct Alignment {
|
||||||
|
#[serde(default)]
|
||||||
|
pub horizontal: HorizontalAlignment,
|
||||||
|
#[serde(default)]
|
||||||
|
pub vertical: VerticalAlignment,
|
||||||
|
}
|
||||||
|
|
@ -1 +1 @@
|
||||||
yazi_macro::mod_flat!(preview wrap);
|
yazi_macro::mod_flat!(alignment preview wrap);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use validator::Validate;
|
||||||
use yazi_fs::{Xdg, expand_path};
|
use yazi_fs::{Xdg, expand_path};
|
||||||
use yazi_shared::timestamp_us;
|
use yazi_shared::timestamp_us;
|
||||||
|
|
||||||
use super::PreviewWrap;
|
use super::{Alignment, PreviewWrap};
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
const TABS: &[&str] = &["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "];
|
const TABS: &[&str] = &["", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "];
|
||||||
|
|
@ -17,6 +17,7 @@ pub struct Preview {
|
||||||
pub tab_size: u8,
|
pub tab_size: u8,
|
||||||
pub max_width: u32,
|
pub max_width: u32,
|
||||||
pub max_height: u32,
|
pub max_height: u32,
|
||||||
|
pub alignment: Alignment,
|
||||||
|
|
||||||
pub cache_dir: PathBuf,
|
pub cache_dir: PathBuf,
|
||||||
|
|
||||||
|
|
@ -72,6 +73,8 @@ impl<'de> Deserialize<'de> for Preview {
|
||||||
tab_size: u8,
|
tab_size: u8,
|
||||||
max_width: u32,
|
max_width: u32,
|
||||||
max_height: u32,
|
max_height: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
alignment: Alignment,
|
||||||
|
|
||||||
cache_dir: Option<String>,
|
cache_dir: Option<String>,
|
||||||
|
|
||||||
|
|
@ -95,6 +98,7 @@ impl<'de> Deserialize<'de> for Preview {
|
||||||
tab_size: preview.tab_size,
|
tab_size: preview.tab_size,
|
||||||
max_width: preview.max_width,
|
max_width: preview.max_width,
|
||||||
max_height: preview.max_height,
|
max_height: preview.max_height,
|
||||||
|
alignment: preview.alignment,
|
||||||
|
|
||||||
cache_dir: preview
|
cache_dir: preview
|
||||||
.cache_dir
|
.cache_dir
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use mlua::{Function, IntoLua, Lua, Value};
|
use mlua::{Function, IntoLua, Lua, Table, Value};
|
||||||
use yazi_adapter::{ADAPTOR, Image};
|
use yazi_adapter::{ADAPTOR, Image, Offset};
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
use crate::{bindings::ImageInfo, elements::Rect, url::UrlRef};
|
use crate::{bindings::ImageInfo, elements::Rect, url::UrlRef};
|
||||||
|
|
@ -16,13 +16,19 @@ impl Utils {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn image_show(lua: &Lua) -> mlua::Result<Function> {
|
pub(super) fn image_show(lua: &Lua) -> mlua::Result<Function> {
|
||||||
lua.create_async_function(|lua, (url, rect): (UrlRef, Rect)| async move {
|
lua.create_async_function(
|
||||||
if let Ok(area) = ADAPTOR.image_show(&url, *rect).await {
|
|lua, (url, rect, offset_table): (UrlRef, Rect, Option<Table>)| async move {
|
||||||
Rect::from(area).into_lua(&lua)
|
let offset = offset_table.map(|lua_offset| Offset {
|
||||||
} else {
|
x: lua_offset.get("x").unwrap_or(0),
|
||||||
Value::Nil.into_lua(&lua)
|
y: lua_offset.get("y").unwrap_or(0),
|
||||||
}
|
});
|
||||||
})
|
if let Ok(area) = ADAPTOR.image_show(&url, *rect, offset).await {
|
||||||
|
Rect::from(area).into_lua(&lua)
|
||||||
|
} else {
|
||||||
|
Value::Nil.into_lua(&lua)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn image_precache(lua: &Lua) -> mlua::Result<Function> {
|
pub(super) fn image_precache(lua: &Lua) -> mlua::Result<Function> {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue