From 49910fcfe75ae0ee3f2652956b06f717b0d8a70f Mon Sep 17 00:00:00 2001 From: Cattyman <88530324+cattyman919@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:55:28 +0700 Subject: [PATCH] feat: progress of each task (#3121) Co-authored-by: sxyazi --- yazi-config/preset/theme-dark.toml | 2 +- yazi-config/preset/theme-light.toml | 2 +- yazi-core/src/tasks/tasks.rs | 5 +- yazi-plugin/preset/components/tasks.lua | 114 +++++++++++++++++++++--- yazi-shared/src/url/buf.rs | 2 +- 5 files changed, 109 insertions(+), 16 deletions(-) diff --git a/yazi-config/preset/theme-dark.toml b/yazi-config/preset/theme-dark.toml index b9871abc..e33e8ddd 100644 --- a/yazi-config/preset/theme-dark.toml +++ b/yazi-config/preset/theme-dark.toml @@ -202,7 +202,7 @@ icon_command = "" [tasks] border = { fg = "blue" } title = {} -hovered = { fg = "magenta", underline = true } +hovered = { fg = "magenta", bold = true } # : }}} diff --git a/yazi-config/preset/theme-light.toml b/yazi-config/preset/theme-light.toml index 9efe0e07..d278acf3 100644 --- a/yazi-config/preset/theme-light.toml +++ b/yazi-config/preset/theme-light.toml @@ -202,7 +202,7 @@ icon_command = "" [tasks] border = { fg = "blue" } title = {} -hovered = { fg = "magenta", underline = true } +hovered = { fg = "magenta", bold = true } # : }}} diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 1258f51d..c3a2f973 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -54,8 +54,9 @@ impl Tasks { } pub fn limit() -> usize { - (Dimension::available().rows * TASKS_PERCENT / 100).saturating_sub(TASKS_BORDER + TASKS_PADDING) - as usize + ((Dimension::available().rows * TASKS_PERCENT / 100) + .saturating_sub(TASKS_BORDER + TASKS_PADDING) as usize) + / 3 } pub fn paginate(&self) -> Vec { diff --git a/yazi-plugin/preset/components/tasks.lua b/yazi-plugin/preset/components/tasks.lua index 120eca91..0ce40e98 100644 --- a/yazi-plugin/preset/components/tasks.lua +++ b/yazi-plugin/preset/components/tasks.lua @@ -2,21 +2,113 @@ Tasks = { _id = "tasks", } -function Tasks:new(area) return setmetatable({ _area = area }, { __index = self }) end +function Tasks:new(area) + local me = setmetatable({ _area = area }, { __index = self }) + me:layout() + return me +end + +function Tasks:layout() + self._area = self._area:pad(ui.Pad(1, 1, 1, 3)) + self._chunks = ui.Layout() + :direction(ui.Layout.HORIZONTAL) + :constraints({ + ui.Constraint.Percentage(60), + ui.Constraint.Percentage(40), + }) + :split(self._area) +end function Tasks:reflow() return { self } end function Tasks:redraw() - local rows = {} - for _, snap in ipairs(cx.tasks.snaps) do - rows[#rows + 1] = ui.Row { snap.name } + local elements = {} + for i, snap in ipairs(cx.tasks.snaps) do + local y = self._area.y + (i - 1) * 3 + if y >= self._area.bottom then + break + end + + elements[#elements + 1] = ui.Line({ self:icon(snap), snap.name }):area(ui.Rect { + x = self._area.x, + y = y, + w = self._area.w, + h = 1, + }) + + if i == cx.tasks.cursor + 1 then + elements[#elements] = elements[#elements]:style(th.tasks.hovered) + end + + for _, e in ipairs(self:progress_redraw(snap, y + 1)) do + elements[#elements + 1] = e + end + + elements[#elements + 1] = ui.Bar(ui.Edge.LEFT) + :area(ui.Rect { + x = math.max(0, self._area.x - 2), + y = y, + w = self._area.w, + h = 2, + }) + :symbol("┃") + + if i == cx.tasks.cursor + 1 then + elements[#elements] = elements[#elements]:style(th.tasks.hovered) + end end - local tbl = ui.Table(rows) - :area(self._area:pad(ui.Pad.x(1))) - :row(cx.tasks.cursor) - :row_style(th.tasks.hovered) - :widths { ui.Constraint.Fill(1) } - - return { tbl } + return elements +end + +function Tasks:icon(snap) + if snap.prog.kind == "FilePaste" then + return " " + elseif snap.prog.kind == "FileDelete" then + return " " + else + return " " + end +end + +function Tasks:progress_redraw(snap, y) + local kind = snap.prog.kind + if kind == "FilePaste" or kind == "FileDelete" then + local label = string.format( + "%3d%% - %s / %s", + math.floor(snap.percent), + ya.readable_size(snap.prog.processed_bytes), + ya.readable_size(snap.prog.total_bytes) + ) + + local style = th.status.progress_normal + if snap.prog.failed_files > 0 then + style = th.status.progress_error + end + + return { + ui.Gauge() + :area(ui.Rect { x = self._chunks[1].x, y = y, w = self._chunks[1].w, h = 1 }) + :percent(snap.percent) + :label(ui.Span(label):style(th.status.progress_label)) + :gauge_style(style), + + ui.Line(string.format("%d/%d", snap.prog.success_files, snap.prog.total_files)) + :fg("gray") + :area(ui.Rect { x = self._chunks[2].x, y = y, w = self._chunks[2].w, h = 1 }) + :align(ui.Align.RIGHT), + } + else + local text + if snap.running then + text = "Running…" + elseif snap.success then + text = "Completing…" + else + text = "Failed, press Enter to view log…" + end + return { + ui.Line(text):fg("gray"):area(ui.Rect { x = self._chunks[1].x, y = y, w = self._chunks[1].w, h = 1 }), + } + end end diff --git a/yazi-shared/src/url/buf.rs b/yazi-shared/src/url/buf.rs index 13e4a4c0..e72ee1d4 100644 --- a/yazi-shared/src/url/buf.rs +++ b/yazi-shared/src/url/buf.rs @@ -339,7 +339,7 @@ mod tests { for (path, expected) in cases { let path: UrlBuf = path.parse()?; - assert_eq!(path.parent().map(|u| format!("{:?}", u)).as_deref(), expected); + assert_eq!(path.parent().map(|u| format!("{u:?}")).as_deref(), expected); } Ok(())