diff options
Diffstat (limited to 'fedora/.config/yazi/plugins/mount.yazi/main.lua')
| -rw-r--r-- | fedora/.config/yazi/plugins/mount.yazi/main.lua | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/fedora/.config/yazi/plugins/mount.yazi/main.lua b/fedora/.config/yazi/plugins/mount.yazi/main.lua new file mode 100644 index 0000000..31c2e28 --- /dev/null +++ b/fedora/.config/yazi/plugins/mount.yazi/main.lua @@ -0,0 +1,304 @@ +--- @since 25.5.31 + +local toggle_ui = ya.sync(function(self) + if self.children then + Modal:children_remove(self.children) + self.children = nil + else + self.children = Modal:children_add(self, 10) + end + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end +end) + +local subscribe = ya.sync(function(self) + ps.unsub("mount") + ps.sub("mount", function() ya.emit("plugin", { self._id, "refresh" }) end) +end) + +local update_partitions = ya.sync(function(self, partitions) + self.partitions = partitions + self.cursor = math.max(0, math.min(self.cursor or 0, #self.partitions - 1)) + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end +end) + +local active_partition = ya.sync(function(self) return self.partitions[self.cursor + 1] end) + +local update_cursor = ya.sync(function(self, cursor) + if #self.partitions == 0 then + self.cursor = 0 + else + self.cursor = ya.clamp(0, self.cursor + cursor, #self.partitions - 1) + end + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end +end) + +local M = { + keys = { + { on = "q", run = "quit" }, + + { on = "k", run = "up" }, + { on = "j", run = "down" }, + { on = "l", run = { "enter", "quit" } }, + + { on = "<Up>", run = "up" }, + { on = "<Down>", run = "down" }, + { on = "<Right>", run = { "enter", "quit" } }, + + { on = "m", run = "mount" }, + { on = "u", run = "unmount" }, + { on = "e", run = "eject" }, + }, +} + +function M:new(area) + self:layout(area) + return self +end + +function M:layout(area) + local chunks = ui.Layout() + :constraints({ + ui.Constraint.Percentage(10), + ui.Constraint.Percentage(80), + ui.Constraint.Percentage(10), + }) + :split(area) + + local chunks = ui.Layout() + :direction(ui.Layout.HORIZONTAL) + :constraints({ + ui.Constraint.Percentage(10), + ui.Constraint.Percentage(80), + ui.Constraint.Percentage(10), + }) + :split(chunks[2]) + + self._area = chunks[2] +end + +function M:entry(job) + if job.args[1] == "refresh" then + return update_partitions(self.obtain()) + end + + toggle_ui() + update_partitions(self.obtain()) + subscribe() + + local tx1, rx1 = ya.chan("mpsc") + local tx2, rx2 = ya.chan("mpsc") + function producer() + while true do + local cand = self.keys[ya.which { cands = self.keys, silent = true }] or { run = {} } + for _, r in ipairs(type(cand.run) == "table" and cand.run or { cand.run }) do + tx1:send(r) + if r == "quit" then + toggle_ui() + return + end + end + end + end + + function consumer1() + repeat + local run = rx1:recv() + if run == "quit" then + tx2:send(run) + break + elseif run == "up" then + update_cursor(-1) + elseif run == "down" then + update_cursor(1) + elseif run == "enter" then + local active = active_partition() + if active and active.dist then + ya.emit("cd", { active.dist }) + end + else + tx2:send(run) + end + until not run + end + + function consumer2() + repeat + local run = rx2:recv() + if run == "quit" then + break + elseif run == "mount" then + self.operate("mount") + elseif run == "unmount" then + self.operate("unmount") + elseif run == "eject" then + self.operate("eject") + end + until not run + end + + ya.join(producer, consumer1, consumer2) +end + +function M:reflow() return { self } end + +function M:redraw() + local rows = {} + for _, p in ipairs(self.partitions or {}) do + if not p.sub then + rows[#rows + 1] = ui.Row { p.main } + elseif p.sub == "" then + rows[#rows + 1] = ui.Row { p.main, p.label or "", p.dist or "", p.fstype or "" } + else + rows[#rows + 1] = ui.Row { " " .. p.sub, p.label or "", p.dist or "", p.fstype or "" } + end + end + + return { + ui.Clear(self._area), + ui.Border(ui.Edge.ALL) + :area(self._area) + :type(ui.Border.ROUNDED) + :style(ui.Style():fg("blue")) + :title(ui.Line("Mount"):align(ui.Align.CENTER)), + ui.Table(rows) + :area(self._area:pad(ui.Pad(1, 2, 1, 2))) + :header(ui.Row({ "Src", "Label", "Dist", "FSType" }):style(ui.Style():bold())) + :row(self.cursor) + :row_style(ui.Style():fg("blue"):underline()) + :widths { + ui.Constraint.Length(20), + ui.Constraint.Length(20), + ui.Constraint.Percentage(70), + ui.Constraint.Length(10), + }, + } +end + +function M.obtain() + local tbl = {} + local last + for _, p in ipairs(fs.partitions()) do + local main, sub = M.split(p.src) + if main and last ~= main then + if p.src == main then + last, p.main, p.sub, tbl[#tbl + 1] = p.src, p.src, "", p + else + last, tbl[#tbl + 1] = main, { src = main, main = main, sub = "" } + end + end + if sub then + if tbl[#tbl].sub == "" and tbl[#tbl].main == main then + tbl[#tbl].sub = nil + end + p.main, p.sub, tbl[#tbl + 1] = main, sub, p + end + end + table.sort(M.fillin(tbl), function(a, b) + if a.main == b.main then + return (a.sub or "") < (b.sub or "") + else + return a.main > b.main + end + end) + return tbl +end + +function M.split(src) + local pats = { + { "^/dev/sd[a-z]", "%d+$" }, -- /dev/sda1 + { "^/dev/nvme%d+n%d+", "p%d+$" }, -- /dev/nvme0n1p1 + { "^/dev/mmcblk%d+", "p%d+$" }, -- /dev/mmcblk0p1 + { "^/dev/disk%d+", ".+$" }, -- /dev/disk1s1 + { "^/dev/sr%d+", ".+$" }, -- /dev/sr0 + } + for _, p in ipairs(pats) do + local main = src:match(p[1]) + if main then + return main, src:sub(#main + 1):match(p[2]) + end + end +end + +function M.fillin(tbl) + if ya.target_os() ~= "linux" then + return tbl + end + + local sources, indices = {}, {} + for i, p in ipairs(tbl) do + if p.sub and not p.fstype then + sources[#sources + 1], indices[p.src] = p.src, i + end + end + if #sources == 0 then + return tbl + end + + local output, err = Command("lsblk"):arg({ "-p", "-o", "name,fstype", "-J" }):arg(sources):output() + if err then + ya.dbg("Failed to fetch filesystem types for unmounted partitions: " .. err) + return tbl + end + + local t = ya.json_decode(output and output.stdout or "") + for _, p in ipairs(t and t.blockdevices or {}) do + tbl[indices[p.name]].fstype = p.fstype + end + return tbl +end + +function M.operate(type) + local active = active_partition() + if not active then + return + elseif not active.sub then + return -- TODO: mount/unmount main disk + end + + local output, err + if ya.target_os() == "macos" then + output, err = Command("diskutil"):arg({ type, active.src }):output() + end + if ya.target_os() == "linux" then + if type == "eject" and active.src:match("^/dev/sr%d+") then + Command("udisksctl"):arg({ "unmount", "-b", active.src }):status() + output, err = Command("eject"):arg({ "--traytoggle", active.src }):output() + elseif type == "eject" then + Command("udisksctl"):arg({ "unmount", "-b", active.src }):status() + output, err = Command("udisksctl"):arg({ "power-off", "-b", active.src }):output() + else + output, err = Command("udisksctl"):arg({ type, "-b", active.src }):output() + end + end + + if not output then + M.fail("Failed to %s `%s`: %s", type, active.src, err) + elseif not output.status.success then + M.fail("Failed to %s `%s`: %s", type, active.src, output.stderr) + end +end + +function M.fail(...) ya.notify { title = "Mount", content = string.format(...), timeout = 10, level = "error" } end + +function M:click() end + +function M:scroll() end + +function M:touch() end + +return M |
