diff --git a/yazi-actor/src/mgr/spot.rs b/yazi-actor/src/mgr/spot.rs index 31cc58ee..dc35f2dd 100644 --- a/yazi-actor/src/mgr/spot.rs +++ b/yazi-actor/src/mgr/spot.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use yazi_macro::succ; +use yazi_macro::{act, succ}; use yazi_parser::mgr::SpotOpt; use yazi_shared::{data::Data, pool::InternStr}; @@ -13,12 +13,13 @@ impl Actor for Spot { const NAME: &str = "spot"; fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + act!(mgr:escape_visual, cx)?; let Some(hovered) = cx.hovered().cloned() else { succ!() }; - let (mime, urls) = if cx.tab().selected.len() >= 2 { - ("multi/selected".intern(), Some(cx.tab().selected.values().cloned().collect())) + let mime = if cx.tab().selected.is_empty() { + cx.mgr.mimetype.owned(&hovered.url).unwrap_or_default() } else { - (cx.mgr.mimetype.owned(&hovered.url).unwrap_or_default(), None) + "multi/unknown".intern() }; // if !self.active().spot.same_file(&hovered, &mime) { @@ -31,7 +32,7 @@ impl Actor for Spot { cx.tab_mut().spot.skip = 0; } - cx.tab_mut().spot.go(hovered, mime, urls); + cx.tab_mut().spot.go(hovered, mime); succ!(); } } diff --git a/yazi-config/preset/yazi-default.toml b/yazi-config/preset/yazi-default.toml index fbb1b967..0e68e6c2 100644 --- a/yazi-config/preset/yazi-default.toml +++ b/yazi-config/preset/yazi-default.toml @@ -105,7 +105,9 @@ fetchers = [ { id = "mime", url = "remote://*", run = "mime.remote", prio = "high" }, ] spotters = [ + # Multi-file { mime = "multi/*", run = "multi" }, + # Folder { url = "*/", run = "folder" }, # Code { mime = "text/*", run = "code" }, diff --git a/yazi-core/src/spot/spot.rs b/yazi-core/src/spot/spot.rs index 715a3b64..34034168 100644 --- a/yazi-core/src/spot/spot.rs +++ b/yazi-core/src/spot/spot.rs @@ -15,10 +15,10 @@ pub struct Spot { } impl Spot { - pub fn go(&mut self, file: File, mime: Symbol, urls: Option>) { + pub fn go(&mut self, file: File, mime: Symbol) { if mime.is_empty() { return; // Wait till mimetype is resolved to avoid flickering - } else if urls.is_none() && self.same_lock(&file, &mime) { + } else if self.same_lock(&file, &mime) { return; } @@ -27,7 +27,7 @@ impl Spot { }; self.abort(); - self.ct = Some(isolate::spot(&spotter.run, file, mime, self.skip, urls)); + self.ct = Some(isolate::spot(&spotter.run, file, mime, self.skip)); } pub fn visible(&self) -> bool { self.lock.is_some() } diff --git a/yazi-plugin/preset/plugins/multi.lua b/yazi-plugin/preset/plugins/multi.lua index 670688f8..8f5648fe 100644 --- a/yazi-plugin/preset/plugins/multi.lua +++ b/yazi-plugin/preset/plugins/multi.lua @@ -1,61 +1,46 @@ +--- @sync peek + local M = {} +local selected = ya.sync(function() + local urls = {} + for _, u in pairs(cx.active.selected) do + urls[#urls + 1] = u + end + return urls +end) + function M:spot(job) - self.size = 0 - self.done = 0 - self.total = #job.files - self.last = 0 - self:spot_render(job, false) - local sizes = {} - for _, url in ipairs(job.files) do - local cha = fs.cha(url) - if cha and not cha.is_dir then - self.size = self.size + cha.len - self.done = self.done + 1 - self:spot_render(job, false) - else - local it = fs.calc_size(url) - local sub = 0 - while true do - local next = it:recv() - if next then - sub = sub + next - self.size = self.size + next - self:spot_render(job, false) - else - break - end - end - self.done = self.done + 1 - if cha and cha.is_dir then - sizes[url.urn] = sub + self.sum, self.sizes, self.last, self.selected = 0, {}, 0, selected() + self:spot_multi(job, false) + + for _, u in ipairs(self.selected) do + local it, size = fs.calc_size(u), 0 + while true do + local next = it:recv() + if next then + size, self.sum = size + next, self.sum + next + self:spot_multi(job, false) + else + self.sizes[u], size = size, 0 + break end end end - if next(sizes) then - local first = job.files[1] - local parent = first.parent - if parent then - local op = fs.op("size", { url = parent, sizes = sizes }) - ya.emit("update_files", { op = op }) - end - end - self:spot_render(job, true) + self:spot_multi(job, true) end -function M:spot_render(job, comp) +function M:spot_multi(job, comp) local now = ya.time() if not comp and now < self.last + 0.1 then return end - local progress = string.format("%d/%d", self.done, self.total) local rows = { - ui.Row({ "Selected" }):style(ui.Style():fg("green")), - ui.Row { " Count:", tostring(self.total) }, - ui.Row { " Size:", ya.readable_size(self.size) .. (comp and "" or " (?)") }, - ui.Row { " Progress:", comp and "Done" or progress }, + ui.Row({ "Multi" }):style(ui.Style():fg("green")), + ui.Row { " Count:", string.format("%d items", #self.selected) }, + ui.Row { " Size:", ya.readable_size(self.sum) .. (comp and "" or " (?)") }, } ya.spot_table( @@ -68,7 +53,21 @@ function M:spot_render(job, comp) :cell_style(th.spot.tbl_cell) :widths { ui.Constraint.Length(14), ui.Constraint.Fill(1) } ) + + self:update_sizes() self.last = now end +function M:update_sizes() + local parents = {} + for url, size in pairs(self.sizes) do + local p = url.parent + parents[p] = parents[p] or {} + parents[p][url.urn] = size + end + for parent, sizes in pairs(parents) do + ya.emit("update_files", { op = fs.op("size", { url = parent, sizes = sizes }) }) + end +end + return M diff --git a/yazi-plugin/src/isolate/spot.rs b/yazi-plugin/src/isolate/spot.rs index e5649e7c..183a2ae9 100644 --- a/yazi-plugin/src/isolate/spot.rs +++ b/yazi-plugin/src/isolate/spot.rs @@ -2,9 +2,9 @@ use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmS use tokio::{runtime::Handle, select}; use tokio_util::sync::CancellationToken; use tracing::error; -use yazi_binding::{File, Id, Url}; +use yazi_binding::{File, Id}; use yazi_dds::Sendable; -use yazi_shared::{Ids, event::Action, pool::Symbol, url::UrlBuf}; +use yazi_shared::{Ids, event::Action, pool::Symbol}; use super::slim_lua; use crate::loader::LOADER; @@ -16,7 +16,6 @@ pub fn spot( file: yazi_fs::File, mime: Symbol, skip: usize, - urls: Option>, ) -> CancellationToken { let ct = CancellationToken::new(); let (ct1, ct2) = (ct.clone(), ct.clone()); @@ -45,13 +44,6 @@ pub fn spot( ("mime", mime.into_lua(&lua)?), ("skip", skip.into_lua(&lua)?), ])?; - if let Some(urls) = urls { - let files = lua.create_table_with_capacity(urls.len(), 0)?; - for (i, url) in urls.into_iter().enumerate() { - files.raw_set(i + 1, Url::new(url))?; - } - job.raw_set("files", files)?; - } if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("spot", job).await } };