diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index 5d47b605..62b0c2f2 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -13,6 +13,8 @@ body:
- macOS
- Windows
- Windows WSL
+ - FreeBSD X11
+ - FreeBSD Wayland
validations:
required: true
- type: input
@@ -22,31 +24,32 @@ body:
placeholder: "ex: kitty v0.32.2"
validations:
required: true
- - type: dropdown
- id: tried_main
- attributes:
- label: Did you try the latest code to see if this problem got fixed?
- options:
- - Tried, but the problem still
- - Not tried, and I'll explain why below
- validations:
- required: true
- type: textarea
id: debug
attributes:
label: "`yazi --debug` output"
- description: Please do a `yazi --debug` and paste the output here.
+ description: Please run `yazi --debug` and paste the debug information here.
value: |
-
```sh
+
+
```
validations:
required: true
+ - type: dropdown
+ id: tried_main
+ attributes:
+ label: Did you try the latest nightly build to see if the problem got fixed?
+ options:
+ - Yes, and I updated the debug information above (`yazi --debug`) to the nightly that I tried
+ - No, and I'll explain why below
+ validations:
+ required: true
- type: textarea
id: description
attributes:
diff --git a/Cargo.lock b/Cargo.lock
index e1233811..f3f9e688 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -206,9 +206,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.5.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block-buffer"
@@ -303,9 +303,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.7"
+version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
+checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
dependencies = [
"clap_builder",
"clap_derive",
@@ -313,9 +313,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.7"
+version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
+checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
dependencies = [
"anstream",
"anstyle",
@@ -325,9 +325,9 @@ dependencies = [
[[package]]
name = "clap_complete"
-version = "4.5.6"
+version = "4.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbca90c87c2a04da41e95d1856e8bcd22f159bdbfa147314d2ce5218057b0e58"
+checksum = "1d598e88f6874d4b888ed40c71efbcbf4076f1dfbae128a08a8c9e45f710605d"
dependencies = [
"clap",
]
@@ -354,9 +354,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.5"
+version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
+checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
dependencies = [
"heck",
"proc-macro2",
@@ -480,7 +480,7 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"crossterm_winapi",
"futures-core",
"libc",
@@ -1110,7 +1110,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"libc",
]
@@ -1282,7 +1282,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"filetime",
"fsevent-sys",
"inotify",
@@ -1366,7 +1366,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"block2",
"libc",
"objc2",
@@ -1588,7 +1588,7 @@ version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"cassowary",
"compact_str",
"crossterm",
@@ -1638,7 +1638,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
]
[[package]]
@@ -1699,7 +1699,7 @@ version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1765,9 +1765,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.117"
+version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [
"itoa",
"ryu",
@@ -2796,7 +2796,7 @@ version = "0.2.5"
dependencies = [
"anyhow",
"arc-swap",
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"crossterm",
"globset",
"indexmap",
@@ -2812,7 +2812,7 @@ name = "yazi-core"
version = "0.2.5"
dependencies = [
"anyhow",
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"crossterm",
"dirs",
"futures",
@@ -2962,7 +2962,7 @@ name = "yazi-shared"
version = "0.2.5"
dependencies = [
"anyhow",
- "bitflags 2.5.0",
+ "bitflags 2.6.0",
"crossterm",
"dirs",
"futures",
diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml
index ebb7864f..e48b693d 100644
--- a/yazi-boot/Cargo.toml
+++ b/yazi-boot/Cargo.toml
@@ -15,12 +15,12 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" }
yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
# External dependencies
-clap = { version = "4.5.7", features = [ "derive" ] }
+clap = { version = "4.5.8", features = [ "derive" ] }
serde = { version = "1.0.203", features = [ "derive" ] }
[build-dependencies]
-clap = { version = "4.5.7", features = [ "derive" ] }
-clap_complete = "4.5.6"
+clap = { version = "4.5.8", features = [ "derive" ] }
+clap_complete = "4.5.7"
clap_complete_nushell = "4.5.2"
clap_complete_fig = "4.5.1"
vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] }
diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml
index 1b2d04c5..c6873961 100644
--- a/yazi-cli/Cargo.toml
+++ b/yazi-cli/Cargo.toml
@@ -14,20 +14,20 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
# External dependencies
anyhow = "1.0.86"
-clap = { version = "4.5.7", features = [ "derive" ] }
+clap = { version = "4.5.8", features = [ "derive" ] }
crossterm = "0.27.0"
md-5 = "0.10.6"
-serde_json = "1.0.117"
+serde_json = "1.0.120"
tokio = { version = "1.38.0", features = [ "full" ] }
toml_edit = "0.22.14"
[build-dependencies]
anyhow = "1.0.86"
-clap = { version = "4.5.7", features = [ "derive" ] }
-clap_complete = "4.5.6"
+clap = { version = "4.5.8", features = [ "derive" ] }
+clap_complete = "4.5.7"
clap_complete_fig = "4.5.1"
clap_complete_nushell = "4.5.2"
-serde_json = "1.0.117"
+serde_json = "1.0.120"
vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] }
[[bin]]
diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml
index 8df9f74d..9979eb0d 100644
--- a/yazi-config/Cargo.toml
+++ b/yazi-config/Cargo.toml
@@ -14,7 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
# External dependencies
anyhow = "1.0.86"
arc-swap = "1.7.1"
-bitflags = "2.5.0"
+bitflags = "2.6.0"
crossterm = "0.27.0"
globset = "0.4.14"
indexmap = "2.2.6"
diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml
index f35bdc6a..fddd5516 100644
--- a/yazi-core/Cargo.toml
+++ b/yazi-core/Cargo.toml
@@ -20,7 +20,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" }
# External dependencies
anyhow = "1.0.86"
-bitflags = "2.5.0"
+bitflags = "2.6.0"
crossterm = "0.27.0"
dirs = "5.0.1"
futures = "0.3.30"
diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml
index d364f54d..4d5ab4dc 100644
--- a/yazi-dds/Cargo.toml
+++ b/yazi-dds/Cargo.toml
@@ -21,7 +21,7 @@ anyhow = "1.0.86"
mlua = { version = "0.9.9", features = [ "lua54" ] }
parking_lot = "0.12.3"
serde = { version = "1.0.203", features = [ "derive" ] }
-serde_json = "1.0.117"
+serde_json = "1.0.120"
tokio = { version = "1.38.0", features = [ "full" ] }
tokio-stream = "0.1.15"
tokio-util = "0.7.11"
diff --git a/yazi-fm/src/app/commands/mouse.rs b/yazi-fm/src/app/commands/mouse.rs
index 5e786efc..8c42255d 100644
--- a/yazi-fm/src/app/commands/mouse.rs
+++ b/yazi-fm/src/app/commands/mouse.rs
@@ -49,7 +49,7 @@ impl App {
if matches!(event.kind, MouseEventKind::Down(_) if MANAGER.mouse_events.draggable()) {
let evt = yazi_plugin::bindings::MouseEvent::cast(&LUA, event);
if let (Ok(evt), Ok(root)) = (evt, LUA.globals().raw_get::<_, Table>("Root")) {
- root.raw_set("drag_start", evt).ok();
+ root.raw_set("_drag_start", evt).ok();
}
}
@@ -59,7 +59,7 @@ impl App {
}
if let Err(e) = Lives::scope(&self.cx, move |_| f(event)) {
- error!("{:?}", e);
+ error!("{e}");
}
}
}
diff --git a/yazi-fm/src/components/header.rs b/yazi-fm/src/components/header.rs
index 67ab8216..73050576 100644
--- a/yazi-fm/src/components/header.rs
+++ b/yazi-fm/src/components/header.rs
@@ -1,25 +1,9 @@
use crossterm::event::MouseEventKind;
use mlua::{Table, TableExt};
-use ratatui::{buffer::Buffer, widgets::Widget};
-use tracing::error;
-use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA};
+use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};
pub(crate) struct Header;
-impl Widget for Header {
- fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) {
- let mut f = || {
- let area = Rect::cast(&LUA, area)?;
- let comp: Table = LUA.globals().raw_get("Header")?;
- render_widgets(comp.call_method("render", area)?, buf);
- Ok::<_, anyhow::Error>(())
- };
- if let Err(e) = f() {
- error!("{:?}", e);
- }
- }
-}
-
impl Header {
pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
let evt = MouseEvent::cast(&LUA, event)?;
diff --git a/yazi-fm/src/components/manager.rs b/yazi-fm/src/components/manager.rs
deleted file mode 100644
index bf4b266b..00000000
--- a/yazi-fm/src/components/manager.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use mlua::{Table, TableExt};
-use ratatui::{buffer::Buffer, widgets::Widget};
-use tracing::error;
-use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA};
-
-pub(crate) struct Manager;
-
-impl Widget for Manager {
- fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) {
- let mut f = || {
- let area = Rect::cast(&LUA, area)?;
- let comp: Table = LUA.globals().raw_get("Manager")?;
- render_widgets(comp.call_method("render", area)?, buf);
- Ok::<_, anyhow::Error>(())
- };
- if let Err(e) = f() {
- error!("{:?}", e);
- }
- }
-}
diff --git a/yazi-fm/src/components/mod.rs b/yazi-fm/src/components/mod.rs
index 1898af85..a62097a0 100644
--- a/yazi-fm/src/components/mod.rs
+++ b/yazi-fm/src/components/mod.rs
@@ -2,7 +2,6 @@
mod current;
mod header;
-mod manager;
mod parent;
mod preview;
mod progress;
@@ -10,7 +9,6 @@ mod status;
pub(super) use current::*;
pub(super) use header::*;
-pub(super) use manager::*;
pub(super) use parent::*;
pub(super) use preview::*;
pub(super) use progress::*;
diff --git a/yazi-fm/src/components/progress.rs b/yazi-fm/src/components/progress.rs
index 39de994e..70aab946 100644
--- a/yazi-fm/src/components/progress.rs
+++ b/yazi-fm/src/components/progress.rs
@@ -34,7 +34,7 @@ impl Progress {
};
if let Err(e) = f() {
- error!("{:?}", e);
+ error!("{e}");
}
patches
}
diff --git a/yazi-fm/src/components/status.rs b/yazi-fm/src/components/status.rs
index b9a1fe5e..3be1bdeb 100644
--- a/yazi-fm/src/components/status.rs
+++ b/yazi-fm/src/components/status.rs
@@ -1,25 +1,9 @@
use crossterm::event::MouseEventKind;
use mlua::{Table, TableExt};
-use ratatui::widgets::Widget;
-use tracing::error;
-use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA};
+use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};
pub(crate) struct Status;
-impl Widget for Status {
- fn render(self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) {
- let mut f = || {
- let area = Rect::cast(&LUA, area)?;
- let comp: Table = LUA.globals().raw_get("Status")?;
- render_widgets(comp.call_method("render", area)?, buf);
- Ok::<_, anyhow::Error>(())
- };
- if let Err(e) = f() {
- error!("{:?}", e);
- }
- }
-}
-
impl Status {
pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> {
let evt = MouseEvent::cast(&LUA, event)?;
diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs
index 7380fc47..9965ae4d 100644
--- a/yazi-fm/src/lives/lives.rs
+++ b/yazi-fm/src/lives/lives.rs
@@ -55,11 +55,11 @@ impl Lives {
let ret = f(scope)?;
LAYOUT.store(Arc::new(yazi_config::Layout {
- header: *globals.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("area")?,
- parent: *globals.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("area")?,
- current: *globals.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("area")?,
- preview: *globals.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("area")?,
- status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?,
+ header: *globals.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("_area")?,
+ parent: *globals.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("_area")?,
+ current: *globals.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("_area")?,
+ preview: *globals.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("_area")?,
+ status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("_area")?,
}));
Ok(ret)
diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs
index 93d67543..86363ab6 100644
--- a/yazi-fm/src/main.rs
+++ b/yazi-fm/src/main.rs
@@ -53,7 +53,7 @@ async fn main() -> anyhow::Result<()> {
yazi_dds::init();
- yazi_plugin::init();
+ yazi_plugin::init()?;
yazi_core::init();
diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs
index 3ffeaaf8..1de47d73 100644
--- a/yazi-fm/src/root.rs
+++ b/yazi-fm/src/root.rs
@@ -1,7 +1,8 @@
use crossterm::event::MouseEventKind;
use mlua::{Table, TableExt};
-use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Rect}, widgets::Widget};
-use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA};
+use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
+use tracing::error;
+use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::render_widgets, LUA};
use super::{completion, input, select, tasks, which};
use crate::{components, help, Ctx};
@@ -16,13 +17,16 @@ impl<'a> Root<'a> {
impl<'a> Widget for Root<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
- let chunks =
- Layout::vertical([Constraint::Length(1), Constraint::Fill(1), Constraint::Length(1)])
- .split(area);
+ let mut f = || {
+ let area = yazi_plugin::elements::Rect::cast(&LUA, area)?;
+ let root: Table = LUA.globals().raw_get("Root")?;
+ render_widgets(root.call_method("render", area)?, buf);
+ Ok::<_, mlua::Error>(())
+ };
+ if let Err(e) = f() {
+ error!("Lua method `Root:render()` call failed:\n{e}");
+ }
- components::Header.render(chunks[0], buf);
- components::Manager.render(chunks[1], buf);
- components::Status.render(chunks[2], buf);
components::Preview::new(self.cx).render(area, buf);
if self.cx.tasks.visible {
diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua
index c79bcb26..f5d16d25 100644
--- a/yazi-plugin/preset/components/current.lua
+++ b/yazi-plugin/preset/components/current.lua
@@ -1,8 +1,13 @@
-Current = {
- area = ui.Rect.default,
-}
+Current = {}
-function Current:empty(area)
+function Current:new(area, tab)
+ return setmetatable({
+ _area = area,
+ _tab = tab,
+ }, { __index = self })
+end
+
+function Current:empty()
local folder = Folder:by_kind(Folder.CURRENT)
local line
@@ -13,21 +18,19 @@ function Current:empty(area)
end
return {
- ui.Paragraph(area, { line }):align(ui.Paragraph.CENTER),
+ ui.Paragraph(self._area, { line }):align(ui.Paragraph.CENTER),
}
end
-function Current:render(area)
- self.area = area
-
+function Current:render()
local files = Folder:by_kind(Folder.CURRENT).window
if #files == 0 then
- return self:empty(area)
+ return self:empty()
end
local items, markers = {}, {}
for i, f in ipairs(files) do
- items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f))
+ items[#items + 1] = ui.ListItem(File:children_render(f)):style(File:style(f))
-- Yanked/marked/selected files
local marker = File:marker(f)
@@ -37,9 +40,9 @@ function Current:render(area)
end
return ya.flat {
- ui.List(area, items),
- Folder:linemode(area, files),
- Folder:markers(area, markers),
+ ui.List(self._area, items),
+ Folder:linemode(self._area, files),
+ Folder:markers(self._area, markers),
}
end
diff --git a/yazi-plugin/preset/components/file.lua b/yazi-plugin/preset/components/file.lua
index 1e7cabd2..5f20cc80 100644
--- a/yazi-plugin/preset/components/file.lua
+++ b/yazi-plugin/preset/components/file.lua
@@ -1,76 +1,6 @@
-File = {}
-
-function File:icon(file)
- local icon = file:icon()
- if not icon then
- return {}
- elseif file:is_hovered() then
- return { ui.Span(" " .. icon.text .. " ") }
- else
- return { ui.Span(" " .. icon.text .. " "):style(icon.style) }
- end
-end
-
-function File:prefix(file)
- local prefix = file:prefix() or ""
- return prefix == "" and {} or { ui.Span(prefix .. "/") }
-end
-
-function File:highlights(file)
- local name = file.name:gsub("\r", "?", 1)
- local highlights = file:highlights()
- if not highlights or #highlights == 0 then
- return { ui.Span(name) }
- end
-
- local spans, last = {}, 0
- for _, h in ipairs(highlights) do
- if h[1] > last then
- spans[#spans + 1] = ui.Span(name:sub(last + 1, h[1]))
- end
- spans[#spans + 1] = ui.Span(name:sub(h[1] + 1, h[2])):style(THEME.manager.find_keyword)
- last = h[2]
- end
- if last < #name then
- spans[#spans + 1] = ui.Span(name:sub(last + 1))
- end
- return spans
-end
-
-function File:found(file)
- if not file:is_hovered() then
- return {}
- end
-
- local found = file:found()
- if not found then
- return {}
- end
-
- return {
- ui.Span(" "),
- ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position),
- }
-end
-
-function File:symlink(file)
- if not MANAGER.show_symlink then
- return {}
- end
-
- local to = file.link_to
- return to and { ui.Span(" -> " .. tostring(to)):italic() } or {}
-end
-
-function File:full(file)
- return ya.flat {
- self:icon(file),
- self:prefix(file),
- self:highlights(file),
- self:found(file),
- self:symlink(file),
- }
-end
+File = {
+ _inc = 1000,
+}
function File:style(file)
local style = file:style()
@@ -97,3 +27,98 @@ function File:marker(file)
end
return 0
end
+
+function File:icon(file)
+ local icon = file:icon()
+ if not icon then
+ return ui.Line("")
+ elseif file:is_hovered() then
+ return ui.Line(" " .. icon.text .. " ")
+ else
+ return ui.Line(" " .. icon.text .. " "):style(icon.style)
+ end
+end
+
+function File:prefix(file)
+ local prefix = file:prefix() or ""
+ return ui.Line(prefix ~= "" and prefix .. "/" or "")
+end
+
+function File:highlights(file)
+ local name = file.name:gsub("\r", "?", 1)
+ local highlights = file:highlights()
+ if not highlights or #highlights == 0 then
+ return ui.Line(name)
+ end
+
+ local spans, last = {}, 0
+ for _, h in ipairs(highlights) do
+ if h[1] > last then
+ spans[#spans + 1] = ui.Span(name:sub(last + 1, h[1]))
+ end
+ spans[#spans + 1] = ui.Span(name:sub(h[1] + 1, h[2])):style(THEME.manager.find_keyword)
+ last = h[2]
+ end
+ if last < #name then
+ spans[#spans + 1] = ui.Span(name:sub(last + 1))
+ end
+ return ui.Line(spans)
+end
+
+function File:found(file)
+ if not file:is_hovered() then
+ return ui.Line {}
+ end
+
+ local found = file:found()
+ if not found then
+ return ui.Line {}
+ end
+
+ return ui.Line {
+ ui.Span(" "),
+ ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position),
+ }
+end
+
+function File:symlink(file)
+ if not MANAGER.show_symlink then
+ return ui.Line {}
+ end
+
+ local to = file.link_to
+ return ui.Line(to and { ui.Span(" -> " .. tostring(to)):italic() } or {})
+end
+
+-- Initialize children
+File._children = {
+ { File.icon, id = 1, order = 1000 },
+ { File.prefix, id = 2, order = 2000 },
+ { File.highlights, id = 3, order = 3000 },
+ { File.found, id = 4, order = 4000 },
+ { File.symlink, id = 5, order = 5000 },
+}
+
+function File:children_add(fn, order)
+ self._inc = self._inc + 1
+ self._children[#self._children + 1] = { fn, id = self._inc, order = order }
+ table.sort(self._children, function(a, b) return a.order < b.order end)
+ return self._inc
+end
+
+function File:children_remove(id)
+ for i, child in ipairs(self._children) do
+ if child.id == id then
+ table.remove(self._children, i)
+ break
+ end
+ end
+end
+
+function File:children_render(file)
+ local lines = {}
+ for _, child in ipairs(self._children) do
+ lines[#lines + 1] = child[1](self, file)
+ end
+ return ui.Line(lines)
+end
diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua
index d973266d..9f3b4340 100644
--- a/yazi-plugin/preset/components/header.lua
+++ b/yazi-plugin/preset/components/header.lua
@@ -1,6 +1,10 @@
-Header = {
- area = ui.Rect.default,
-}
+Header = {}
+
+function Header:new(area)
+ return setmetatable({
+ _area = area,
+ }, { __index = self })
+end
function Header:cwd(max)
local s = ya.readable_path(tostring(cx.active.current.cwd)) .. self:flags()
@@ -67,14 +71,12 @@ function Header:tabs()
return ui.Line(spans)
end
-function Header:render(area)
- self.area = area
-
+function Header:render()
local right = ui.Line { self:count(), self:tabs() }
- local left = ui.Line { self:cwd(math.max(0, area.w - right:width())) }
+ local left = ui.Line { self:cwd(math.max(0, self._area.w - right:width())) }
return {
- ui.Paragraph(area, { left }),
- ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT),
+ ui.Paragraph(self._area, { left }),
+ ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT),
}
end
diff --git a/yazi-plugin/preset/components/parent.lua b/yazi-plugin/preset/components/parent.lua
index a60ef3e8..14c7385a 100644
--- a/yazi-plugin/preset/components/parent.lua
+++ b/yazi-plugin/preset/components/parent.lua
@@ -1,10 +1,13 @@
-Parent = {
- area = ui.Rect.default,
-}
+Parent = {}
-function Parent:render(area)
- self.area = area
+function Parent:new(area, tab)
+ return setmetatable({
+ _area = area,
+ _tab = tab,
+ }, { __index = self })
+end
+function Parent:render()
local folder = Folder:by_kind(Folder.PARENT)
if not folder then
return {}
@@ -12,7 +15,7 @@ function Parent:render(area)
local items, markers = {}, {}
for i, f in ipairs(folder.window) do
- items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f))
+ items[#items + 1] = ui.ListItem(File:children_render(f)):style(File:style(f))
-- Yanked/marked/selected files
local marker = File:marker(f)
@@ -22,8 +25,8 @@ function Parent:render(area)
end
return ya.flat {
- ui.List(area, items),
- Folder:markers(area, markers),
+ ui.List(self._area, items),
+ Folder:markers(self._area, markers),
}
end
diff --git a/yazi-plugin/preset/components/preview.lua b/yazi-plugin/preset/components/preview.lua
index 4bf5866d..3b882ab6 100644
--- a/yazi-plugin/preset/components/preview.lua
+++ b/yazi-plugin/preset/components/preview.lua
@@ -1,12 +1,14 @@
-Preview = {
- area = ui.Rect.default,
-}
+Preview = {}
-function Preview:render(area)
- self.area = area
- return {}
+function Preview:new(area, tab)
+ return setmetatable({
+ _area = area,
+ _tab = tab,
+ }, { __index = self })
end
+function Preview:render() return {} end
+
function Preview:click(event, up)
if up or not event.is_left then
return
diff --git a/yazi-plugin/preset/components/progress.lua b/yazi-plugin/preset/components/progress.lua
index 079a445e..4ed8da98 100644
--- a/yazi-plugin/preset/components/progress.lua
+++ b/yazi-plugin/preset/components/progress.lua
@@ -1,9 +1,9 @@
Progress = {
- area = ui.Rect.default,
+ _area = ui.Rect.default, -- TODO: remove this
}
function Progress:render(area, offset)
- self.area = ui.Rect {
+ self._area = ui.Rect {
x = math.max(0, area.w - offset - 21),
y = area.y,
w = ya.clamp(0, area.w - offset - 1, 20),
@@ -15,16 +15,13 @@ end
-- Progress bars usually need frequent updates to report the latest task progress.
-- We use `partial_render()` to partially render it when there is progress change,
-- which has almost no cost compared to a full render by `render()`.
---
--- However, at this time, we can only access `cx.tasks`. If you need certain data from the complete `cx`,
--- just cache it to `self` during `render()`, and read it in `partial_render()` - this process is referred to as "composition".
function Progress:partial_render()
local progress = cx.tasks.progress
if progress.total == 0 then
- return { ui.Paragraph(self.area, {}) }
+ return { ui.Paragraph(self._area, {}) }
end
- local gauge = ui.Gauge(self.area)
+ local gauge = ui.Gauge(self._area)
if progress.fail == 0 then
gauge = gauge:gauge_style(THEME.status.progress_normal)
else
diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua
index cccc315e..d47e7963 100644
--- a/yazi-plugin/preset/components/root.lua
+++ b/yazi-plugin/preset/components/root.lua
@@ -1,7 +1,28 @@
Root = {
- drag_start = ui.Rect.default,
+ _drag_start = ui.Rect.default,
}
+function Root:layout(area)
+ return ui.Layout()
+ :direction(ui.Layout.VERTICAL)
+ :constraints({
+ ui.Constraint.Length(1),
+ ui.Constraint.Fill(1),
+ ui.Constraint.Length(1),
+ })
+ :split(area)
+end
+
+function Root:render(area)
+ local chunks = self:layout(area)
+
+ return ya.flat {
+ ya.eval("Header:render", Header.render, Header:new(chunks[1])) or {},
+ ya.eval("Tab:render", Tab.render, Tab:new(chunks[2], cx.active)) or {},
+ ya.eval("Status:render", Status.render, Status:new(chunks[3])) or {},
+ }
+end
+
function Root:move(event) end
function Root:drag(event) end
diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua
index 571b9469..86974255 100644
--- a/yazi-plugin/preset/components/status.lua
+++ b/yazi-plugin/preset/components/status.lua
@@ -1,8 +1,17 @@
Status = {
- area = ui.Rect.default,
+ LEFT = 0,
+ RIGHT = 1,
+
+ _inc = 1000,
}
-function Status.style()
+function Status:new(area)
+ return setmetatable({
+ _area = area,
+ }, { __index = self })
+end
+
+function Status:style()
if cx.active.mode.is_select then
return THEME.status.mode_select
elseif cx.active.mode.is_unset then
@@ -18,7 +27,7 @@ function Status:mode()
mode = "UN-SET"
end
- local style = self.style()
+ local style = self:style()
return ui.Line {
ui.Span(THEME.status.separator_open):fg(style.bg),
ui.Span(" " .. mode .. " "):style(style),
@@ -31,7 +40,7 @@ function Status:size()
return ui.Line {}
end
- local style = self.style()
+ local style = self:style()
return ui.Line {
ui.Span(" " .. ya.readable_size(h:size() or h.cha.length) .. " "):fg(style.bg):bg(THEME.status.separator_style.bg),
ui.Span(THEME.status.separator_close):fg(THEME.status.separator_style.fg),
@@ -41,10 +50,10 @@ end
function Status:name()
local h = cx.active.current.hovered
if not h then
- return ui.Span("")
+ return ui.Line {}
end
- return ui.Span(" " .. h.name)
+ return ui.Line(" " .. h.name)
end
function Status:permissions()
@@ -92,7 +101,7 @@ function Status:percentage()
percent = string.format(" %3d%% ", percent)
end
- local style = self.style()
+ local style = self:style()
return ui.Line {
ui.Span(" " .. THEME.status.separator_open):fg(THEME.status.separator_style.fg),
ui.Span(percent):fg(style.bg):bg(THEME.status.separator_style.bg),
@@ -103,27 +112,66 @@ function Status:position()
local cursor = cx.active.current.cursor
local length = #cx.active.current.files
- local style = self.style()
+ local style = self:style()
return ui.Line {
ui.Span(string.format(" %2d/%-2d ", cursor + 1, length)):style(style),
ui.Span(THEME.status.separator_close):fg(style.bg),
}
end
-function Status:render(area)
- self.area = area
-
- local left = ui.Line { self:mode(), self:size(), self:name() }
- local right = ui.Line { self:permissions(), self:percentage(), self:position() }
+function Status:render()
+ local left = self:children_render(self.LEFT)
+ local right = self:children_render(self.RIGHT)
return {
- ui.Paragraph(area, { left }),
- ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT),
- table.unpack(Progress:render(area, right:width())),
+ ui.Paragraph(self._area, { left }),
+ ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT),
+ table.unpack(Progress:render(self._area, right:width())),
}
end
+-- Mouse events
function Status:click(event, up) end
function Status:scroll(event, step) end
function Status:touch(event, step) end
+
+-- Initialize children
+Status._left = {
+ { Status.mode, id = 1, order = 1000 },
+ { Status.size, id = 2, order = 2000 },
+ { Status.name, id = 3, order = 3000 },
+}
+Status._right = {
+ { Status.permissions, id = 4, order = 1000 },
+ { Status.percentage, id = 5, order = 2000 },
+ { Status.position, id = 6, order = 3000 },
+}
+
+function Status:children_add(fn, order, side)
+ self._inc = self._inc + 1
+ local children = side == self.RIGHT and self._right or self._left
+
+ children[#children + 1] = { fn, id = self._inc, order = order }
+ table.sort(children, function(a, b) return a.order < b.order end)
+
+ return self._inc
+end
+
+function Status:children_remove(id, side)
+ local children = side == self.RIGHT and self._right or self._left
+ for i, child in ipairs(children) do
+ if child.id == id then
+ table.remove(children, i)
+ break
+ end
+ end
+end
+
+function Status:children_render(side)
+ local lines = {}
+ for _, child in ipairs(side == self.RIGHT and self._right or self._left) do
+ lines[#lines + 1] = child[1](self)
+ end
+ return ui.Line(lines)
+end
diff --git a/yazi-plugin/preset/components/manager.lua b/yazi-plugin/preset/components/tab.lua
similarity index 55%
rename from yazi-plugin/preset/components/manager.lua
rename to yazi-plugin/preset/components/tab.lua
index 72633ada..01976de4 100644
--- a/yazi-plugin/preset/components/manager.lua
+++ b/yazi-plugin/preset/components/tab.lua
@@ -1,10 +1,14 @@
-Manager = {
- area = ui.Rect.default,
-}
+Manager = {} -- TODO: remove this after 0.3.0 release
+Tab = {}
-function Manager:layout(area)
- self.area = area
+function Tab:new(area, tab)
+ return setmetatable({
+ _area = area,
+ _tab = tab,
+ }, { __index = self })
+end
+function Tab:layout()
return ui.Layout()
:direction(ui.Layout.HORIZONTAL)
:constraints({
@@ -12,11 +16,11 @@ function Manager:layout(area)
ui.Constraint.Ratio(MANAGER.ratio.current, MANAGER.ratio.all),
ui.Constraint.Ratio(MANAGER.ratio.preview, MANAGER.ratio.all),
})
- :split(area)
+ :split(self._area)
end
-function Manager:render(area)
- local chunks = self:layout(area)
+function Tab:render()
+ local chunks = self:layout()
return ya.flat {
-- Borders
@@ -24,10 +28,10 @@ function Manager:render(area)
ui.Bar(chunks[3], ui.Bar.LEFT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style),
-- Parent
- Parent:render(chunks[1]:padding(ui.Padding.x(1))),
+ Parent:new(chunks[1]:padding(ui.Padding.x(1)), self._tab):render(),
-- Current
- Current:render(chunks[2]),
+ Current:new(chunks[2], self._tab):render(),
-- Preview
- Preview:render(chunks[3]:padding(ui.Padding.x(1))),
+ Preview:new(chunks[3]:padding(ui.Padding.x(1)), self._tab):render(),
}
end
diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua
index bac2a4f2..a0c63fca 100644
--- a/yazi-plugin/preset/plugins/folder.lua
+++ b/yazi-plugin/preset/plugins/folder.lua
@@ -20,7 +20,7 @@ function M:peek()
local items, markers = {}, {}
for i, f in ipairs(folder.window) do
- items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f))
+ items[#items + 1] = ui.ListItem(File:children_render(f)):style(File:style(f))
-- Yanked/marked/selected files
local marker = File:marker(f)
diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua
index 8713c0a1..399f38a6 100644
--- a/yazi-plugin/preset/ya.lua
+++ b/yazi-plugin/preset/ya.lua
@@ -15,19 +15,29 @@ end
function ya.round(x) return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) end
function ya.flat(t)
- local r = {}
- for _, v in ipairs(t) do
- if type(v) == "table" then
- for _, v2 in ipairs(ya.flat(v)) do
- r[#r + 1] = v2
+ local r, todo = {}, { t }
+ while #todo > 0 do
+ for _, v in ipairs(table.remove(todo)) do
+ if type(v) == "table" then
+ todo[#todo + 1] = v
+ else
+ r[#r + 1] = v
end
- else
- r[#r + 1] = v
end
end
return r
end
+function ya.eval(ctx, f, ...)
+ local ok, res = xpcall(f, debug.traceback, ...)
+ if ok then
+ return res
+ end
+
+ ya.raw_err(string.format("Failed to call `%s()`:\n%s", ctx, res))
+ return nil
+end
+
function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end
function ya.readable_size(size)
diff --git a/yazi-plugin/src/elements/constraint.rs b/yazi-plugin/src/elements/constraint.rs
index 1f05df28..69b25ff1 100644
--- a/yazi-plugin/src/elements/constraint.rs
+++ b/yazi-plugin/src/elements/constraint.rs
@@ -7,6 +7,18 @@ impl Constraint {
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
let constraint = lua.create_table()?;
+ constraint.raw_set(
+ "Min",
+ lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Min(n))))?,
+ )?;
+ constraint.raw_set(
+ "Max",
+ lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Max(n))))?,
+ )?;
+ constraint.raw_set(
+ "Length",
+ lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Length(n))))?,
+ )?;
constraint.raw_set(
"Percentage",
lua
@@ -19,16 +31,8 @@ impl Constraint {
})?,
)?;
constraint.raw_set(
- "Length",
- lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Length(n))))?,
- )?;
- constraint.raw_set(
- "Max",
- lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Max(n))))?,
- )?;
- constraint.raw_set(
- "Min",
- lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Min(n))))?,
+ "Fill",
+ lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Fill(n))))?,
)?;
ui.raw_set("Constraint", constraint)
diff --git a/yazi-plugin/src/elements/line.rs b/yazi-plugin/src/elements/line.rs
index ca926111..fcf74b24 100644
--- a/yazi-plugin/src/elements/line.rs
+++ b/yazi-plugin/src/elements/line.rs
@@ -23,7 +23,11 @@ impl TryFrom
> for Line {
if let Ok(span) = ud.take::() {
spans.push(span.0);
} else if let Ok(line) = ud.take::() {
- spans.extend(line.0.spans.into_iter().collect::>());
+ let style = line.0.style;
+ spans.extend(line.0.spans.into_iter().map(|mut span| {
+ span.style = style.patch(span.style);
+ span
+ }));
} else {
return Err("expected a table of Spans or Lines".into_lua_err());
}
@@ -83,9 +87,9 @@ impl UserData for Line {
{
let mut me = ud.borrow_mut::()?;
me.0.style = match value {
- Value::Nil => me.0.style.patch(ratatui::style::Style::reset()),
- Value::Table(tb) => me.0.style.patch(Style::try_from(tb)?.0),
- Value::UserData(ud) => me.0.style.patch(ud.borrow::