yazi plugin refactor
This commit is contained in:
parent
38f162562e
commit
a00af3e849
39 changed files with 92 additions and 4880 deletions
|
@ -1,44 +1,113 @@
|
|||
{ inputs, pkgs, ... }:
|
||||
{ inputs, pkgs, lib, ... }:
|
||||
{
|
||||
programs.yazi = {
|
||||
enable = true;
|
||||
enableFishIntegration = true;
|
||||
package = inputs.yazi.packages.x86_64-linux.default;
|
||||
initLua = ./yazi/init.lua;
|
||||
|
||||
plugins = let
|
||||
officialPluginsNames = [
|
||||
"chmod"
|
||||
"diff"
|
||||
"smart-filter"
|
||||
"git"
|
||||
"mount"
|
||||
];
|
||||
|
||||
officialPluginsSrc = pkgs.fetchgit {
|
||||
url = "https://github.com/yazi-rs/plugins.git";
|
||||
sparseCheckout = map (p: "${p}.yazi") officialPluginsNames;
|
||||
rev = "HEAD";
|
||||
hash = "sha256-3F44uFeFBX7PNXo2/maiAzkA/OfweyN4nbDhftna+CI=";
|
||||
};
|
||||
|
||||
officialPlugins =
|
||||
lib.lists.fold (
|
||||
a: b:
|
||||
{
|
||||
${a} = "${officialPluginsSrc}/${a}.yazi";
|
||||
}
|
||||
// b
|
||||
)
|
||||
{}
|
||||
officialPluginsNames;
|
||||
in
|
||||
officialPlugins // {
|
||||
yatline = pkgs.fetchFromGitHub {
|
||||
owner = "imsi32";
|
||||
repo = "yatline.yazi";
|
||||
rev = "HEAD";
|
||||
sha256 = "sha256-JiUik4umi+ujsJmHliUbLsIj7ZTKayrWAAM8HmRdjwk=";
|
||||
};
|
||||
starship = pkgs.fetchFromGitHub {
|
||||
owner = "Rolv-Apneseth";
|
||||
repo = "starship.yazi";
|
||||
rev = "HEAD";
|
||||
sha256 = "sha256-bhLUziCDnF4QDCyysRn7Az35RAy8ibZIVUzoPgyEO1A=";
|
||||
};
|
||||
restore = pkgs.fetchFromGitHub {
|
||||
owner = "boydaihungst";
|
||||
repo = "restore.yazi";
|
||||
rev = "HEAD";
|
||||
sha256 = "sha256-OJJPgpSaUHYz8a9opVLCds+VZsK1B6T+pSRJyVgYNy8=";
|
||||
};
|
||||
lazygit = pkgs.fetchFromGitHub {
|
||||
owner = "Lil-Dank";
|
||||
repo = "lazygit.yazi";
|
||||
rev = "HEAD";
|
||||
sha256 = "sha256-OJJPgpSaUHYz8a9opVLCds+VZsK1B6T+pSRJyVgYNy8=";
|
||||
};
|
||||
ouch = pkgs.fetchFromGitHub {
|
||||
owner = "ndtoan96";
|
||||
repo = "ouch.yazi";
|
||||
rev = "HEAD";
|
||||
sha256 = "sha256-7X8uAiJ8vBXYBXOgyKhVVikOnTBGrdCcXOJemjQNolI=";
|
||||
};
|
||||
dir-rules = ./yazi/plugins/dir-rules.yazi;
|
||||
smart-tab = ./yazi/plugins/smart-tab.yazi;
|
||||
};
|
||||
|
||||
keymap = {
|
||||
manager.prepend_keymap = [
|
||||
{ on = "!"; run = "shell '$SHELL' --block"; desc = "Open shell here"; }
|
||||
|
||||
{ on = ["c" "d"]; run = "shell 'ripdrag \"$@\" -x 2>/dev/null &' --confirm"; desc = "Drag selection";}
|
||||
{ on = ["c" "c"]; run = "yank"; desc = "Copy file"; }
|
||||
{ on = "y"; run = ["shell 'for path in \"$@\"; do echo \"file://$path\"; done | wl-copy -t text/uri-list'\n" "yank"]; }
|
||||
|
||||
{ on = ["g" "r"]; run = "shell 'ya emit cd \"$(git rev-parse --show-toplevel)\"'\n"; desc = "Go to top of git repo"; }
|
||||
{ on = ["g" "p"]; run = "cd ~/Projects"; desc = "Go to ~/Projects"; }
|
||||
|
||||
{ on = ["g" "l"]; run = "plugin lazygit"; desc = "lazygit"; }
|
||||
|
||||
{ on = ["t" "p"]; run = "plugin toggle-view parent"; desc = "Toggle parent"; }
|
||||
{ on = ["t" "c"]; run = "plugin toggle-view current"; desc = "Toggle current"; }
|
||||
{ on = ["t" "r"]; run = "plugin toggle-view preview"; desc = "Toggle preview"; }
|
||||
|
||||
{ on = "<C-t>"; run ="tab_create"; }
|
||||
{ on = "<C-w>"; run ="tab_close"; }
|
||||
{ on = "<C-A-right>"; run ="tab_switch 1 --relative"; }
|
||||
{ on = "<C-A-left>"; run ="tab_switch -1 --relative"; }
|
||||
|
||||
{ on = "<Tab>"; run = "spot"; desc = "Spot hovered file"; }
|
||||
|
||||
{ on = "M"; run = "plugin mount"; }
|
||||
|
||||
{ on = "p"; run = "plugin smart-paste"; desc = "Paste into the hovered directory or CWD"; }
|
||||
{ on = ["g" "l"]; run = "plugin lazygit"; desc = "lazygit"; }
|
||||
# { on = "k"; run = "arrow -1"; desc = "up"; }
|
||||
# { on = "j"; run = "arrow 1"; desc = "down"; }
|
||||
# { on = "<Up>"; run = "arrow -1"; desc = "up"; }
|
||||
# { on = "<Down>"; run = "arrow 1"; desc = "down"; }
|
||||
|
||||
{ on = "C"; run = "plugin ouch zip"; }
|
||||
|
||||
{ on = "T"; run = "plugin smart-tab"; desc = "Create a tab and enter the hovered directory"; }
|
||||
|
||||
{ on = "<Backspace>"; run = "remove"; }
|
||||
|
||||
{ on = [ "t" "s" ]; run = "plugin what-size"; desc = "Calc size of selection or cwd"; }
|
||||
|
||||
{ on = ["d" "u"]; run = "plugin restore"; desc = "Restore last deleted files/folders"; }
|
||||
{ on = ["d" "d"]; run = "remove"; desc = "Delete files/folders"; }
|
||||
{ on = ["d" "u"]; run = "plugin restore"; desc = "Restore last deleted files/folders"; }
|
||||
|
||||
{ on = "f"; run = "plugin smart-filter"; desc = "Find file"; }
|
||||
{ on = "F"; run = "find --smart"; desc = "Find file"; }
|
||||
|
||||
|
||||
{ on = "f"; run = "find --smart"; desc = "Find file"; }
|
||||
{ on = "F"; run = "filter --smart"; desc = "Find file"; }
|
||||
];
|
||||
};
|
||||
settings = {
|
||||
|
@ -90,12 +159,12 @@
|
|||
{ name = "*"; run = "file-extra-metadata"; }
|
||||
];
|
||||
prepend_fetchers = [
|
||||
{
|
||||
id = "mime";
|
||||
name = "*";
|
||||
run = "mime-ext";
|
||||
prio = "high";
|
||||
}
|
||||
# {
|
||||
# id = "mime";
|
||||
# name = "*";
|
||||
# run = "mime-ext";
|
||||
# prio = "high";
|
||||
# }
|
||||
# {
|
||||
# id = "git";
|
||||
# name = "*";
|
||||
|
@ -113,5 +182,5 @@
|
|||
|
||||
xdg.configFile."yazi/theme.toml".source = ./yazi/theme.toml;
|
||||
xdg.configFile."yazi/flavors".source = ./yazi/flavors;
|
||||
xdg.configFile."yazi/plugins".source = ./yazi/plugins;
|
||||
# xdg.configFile."yazi/plugins".source = ./yazi/plugins;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
require("git"):setup()
|
||||
require("yatline-symlink"):setup()
|
||||
-- require("git"):setup()
|
||||
require("dir-rules"):setup()
|
||||
|
||||
require("starship"):setup {
|
||||
config_file = "~/.config/starship.toml",
|
||||
}
|
||||
|
||||
require("fg"):setup({
|
||||
default_action = "menu",
|
||||
})
|
||||
|
||||
require("yatline"):setup({
|
||||
--theme = my_theme,
|
||||
section_separator = { open = "", close = "" },
|
||||
|
|
|
@ -2,9 +2,9 @@ local function setup()
|
|||
ps.sub("cd", function()
|
||||
local cwd = cx.active.current.cwd
|
||||
if cwd:ends_with("Downloads") then
|
||||
ya.manager_emit("sort", { "mtime", reverse = true, dir_first = false })
|
||||
ya.mgr_emit("sort", { "mtime", reverse = true, dir_first = false })
|
||||
else
|
||||
ya.manager_emit("sort", { "alphabetical", reverse = false })
|
||||
ya.mgr_emit("sort", { "alphabetical", reverse = false, dir_first = true })
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -1,274 +0,0 @@
|
|||
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
|
||||
ya.render()
|
||||
end)
|
||||
|
||||
local init_ui_data = ya.sync(function(self,file_url)
|
||||
self.opt = {"nvim", "jump"}
|
||||
self.title = "fg"
|
||||
self.title_color = "#82ab3a"
|
||||
self.cursor = 0
|
||||
self.file_url = file_url and file_url or ""
|
||||
ya.render()
|
||||
end)
|
||||
|
||||
local set_option = ya.sync(function(self,enable)
|
||||
if enable then
|
||||
self.active_opt = self.opt[self.cursor+1]
|
||||
else
|
||||
self.active_opt = nil
|
||||
end
|
||||
end)
|
||||
|
||||
local get_option = ya.sync(function(self)
|
||||
return self.active_opt
|
||||
end)
|
||||
|
||||
local get_default_action = ya.sync(function(self)
|
||||
return self.default_action
|
||||
end)
|
||||
|
||||
local update_cursor = ya.sync(function(self, cursor)
|
||||
self.cursor = ya.clamp(0, self.cursor + cursor, 1)
|
||||
ya.render()
|
||||
end)
|
||||
|
||||
local M = {
|
||||
keys = {
|
||||
{ on = "q", run = "quit" },
|
||||
{ on = "<Esc>", run = "quit" },
|
||||
{ on = "<Enter>", run = "select" },
|
||||
|
||||
|
||||
{ on = "k", run = "up" },
|
||||
{ on = "j", run = "down" },
|
||||
|
||||
|
||||
{ on = "<Up>", run = "up" },
|
||||
{ on = "<Down>", run = "down" },
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
local function splitAndGetNth(inputstr, sep, index)
|
||||
if sep == nil then
|
||||
sep = "%s"
|
||||
end
|
||||
local count = 0
|
||||
local start = 1
|
||||
while true do
|
||||
local sepStart, sepEnd = string.find(inputstr, sep, start)
|
||||
if not sepStart then
|
||||
break
|
||||
end
|
||||
count = count + 1
|
||||
if count == index then
|
||||
return string.sub(inputstr, start, sepStart - 1)
|
||||
end
|
||||
start = sepEnd + 1
|
||||
end
|
||||
if index == 1 then
|
||||
return inputstr
|
||||
end
|
||||
return nil -- 如果没有足够的分割部分,返回nil
|
||||
end
|
||||
|
||||
local state = ya.sync(function() return tostring(cx.active.current.cwd) end)
|
||||
|
||||
local function fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end
|
||||
|
||||
function M:entry(job)
|
||||
local args = job.args
|
||||
local _permit = ya.hide()
|
||||
local cwd = state()
|
||||
local shell_value = ya.target_family() == "windows" and "nu" or os.getenv("SHELL"):match(".*/(.*)")
|
||||
local cmd_args = ""
|
||||
|
||||
local preview_cmd = [===[line={2} && begin=$( if [[ $line -lt 7 ]]; then echo $((line-1)); else echo 6; fi ) && bat --highlight-line={2} --color=always --line-range $((line-begin)):$((line+10)) {1}]===]
|
||||
if ya.target_family() == "windows" then
|
||||
preview_cmd = [[bat --highlight-line={2} --color=always --line-range {2}: {1}]]
|
||||
elseif shell_value == "fish" then
|
||||
preview_cmd = [[set line {2} && set begin ( test $line -lt 7 && echo (math "$line-1") || echo 6 ) && bat --highlight-line={2} --color=always --line-range (math "$line-$begin"):(math "$line+10") {1}]]
|
||||
elseif shell_value == "nu" then
|
||||
preview_cmd = [[let line = ({2} | into int); let begin = if $line < 7 { $line - 1 } else { 6 }; bat --highlight-line={2} --color=always --line-range $'($line - $begin):($line + 10)' {1}]]
|
||||
end
|
||||
if ya.target_family() == "windows" and args[1] == "fzf" then
|
||||
cmd_args = [[fzf --preview="bat --color=always {}"]]
|
||||
elseif ya.target_family() == "windows" and args[1] == "rg" then
|
||||
local rg_prefix = [[rg --colors "path:fg:blue" --colors "line:fg:red" --colors "column:fg:yellow" --column --line-number --no-heading --color=always --smart-case ]]
|
||||
cmd_args = [[fzf --ansi --disabled --bind "start:reload:]]
|
||||
.. rg_prefix
|
||||
.. [[{q}" --bind "change:reload:]]
|
||||
.. rg_prefix
|
||||
.. [[{q}" --delimiter ":" --preview "]]
|
||||
.. preview_cmd
|
||||
.. [[" --preview-window "up,60%" --nth "3.."]]
|
||||
elseif ya.target_family() == "windows" then
|
||||
cmd_args = [[rg --color=always --line-number --no-heading --smart-case "" | fzf --ansi --preview="]] .. preview_cmd .. [[" --delimiter=":" --preview-window="up:60%" --nth="3.."]]
|
||||
elseif args[1] == "fzf" then
|
||||
cmd_args = [[fzf --preview="bat --color=always {}"]]
|
||||
elseif args[1] == "rg" and shell_value == "fish" then
|
||||
cmd_args = [[
|
||||
RG_PREFIX="rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case " \
|
||||
fzf --ansi --disabled \
|
||||
--bind "start:reload:$RG_PREFIX {q}" \
|
||||
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
|
||||
--delimiter : \
|
||||
--preview ']] .. preview_cmd .. [[' \
|
||||
--preview-window 'up,60%' \
|
||||
--nth '3..'
|
||||
]]
|
||||
elseif args[1] == "rg" and (shell_value == "bash" or shell_value == "zsh") then
|
||||
cmd_args = [[
|
||||
RG_PREFIX="rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case "
|
||||
fzf --ansi --disabled \
|
||||
--bind "start:reload:$RG_PREFIX {q}" \
|
||||
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
|
||||
--delimiter : \
|
||||
--preview ']] .. preview_cmd .. [[' \
|
||||
--preview-window 'up,60%' \
|
||||
--nth '3..'
|
||||
]]
|
||||
elseif args[1] == "rg" and shell_value == "nu" then
|
||||
local rg_prefix = "rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case "
|
||||
cmd_args = [[fzf --ansi --disabled --bind "start:reload:]]
|
||||
.. rg_prefix
|
||||
.. [[{q}" --bind "change:reload:sleep 100ms; try { ]]
|
||||
.. rg_prefix
|
||||
.. [[{q} }" --delimiter : --preview ']]
|
||||
.. preview_cmd
|
||||
.. [[' --preview-window 'up,60%' --nth '3..']]
|
||||
else
|
||||
cmd_args = [[rg --color=always --line-number --no-heading --smart-case '' | fzf --ansi --preview=']] .. preview_cmd .. [[' --delimiter=':' --preview-window='up:60%' --nth='3..']]
|
||||
end
|
||||
|
||||
local child, err =
|
||||
Command(shell_value):args({"-c", cmd_args}):cwd(cwd):stdin(Command.INHERIT):stdout(Command.PIPED):stderr(Command.INHERIT):spawn()
|
||||
|
||||
if not child then
|
||||
return fail("Spawn `rfzf` failed with error code %s. Do you have it installed?", err)
|
||||
end
|
||||
|
||||
local output, err = child:wait_with_output()
|
||||
if not output then
|
||||
return fail("Cannot read `fzf` output, error code %s", err)
|
||||
elseif not output.status.success and output.status.code ~= 130 then
|
||||
return fail("`fzf` exited with error code %s", output.status.code)
|
||||
end
|
||||
|
||||
if output.stdout == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local target = output.stdout:gsub("\n$", "")
|
||||
|
||||
local file_url = splitAndGetNth(target,":",1)
|
||||
local line_number = splitAndGetNth(target,":",2)
|
||||
line_number = line_number and line_number or 1
|
||||
init_ui_data(cwd.."/"..file_url)
|
||||
local default_action = get_default_action()
|
||||
|
||||
if (default_action == "menu" or default_action == nil) and args[1] ~= "fzf" then
|
||||
_permit:drop()
|
||||
toggle_ui()
|
||||
while true do
|
||||
local cand = self.keys[ya.which { cands = self.keys, silent = true }]
|
||||
if cand then
|
||||
if cand.run == "quit" then
|
||||
set_option(false)
|
||||
toggle_ui()
|
||||
break
|
||||
elseif cand.run == "select" then
|
||||
set_option(true)
|
||||
toggle_ui()
|
||||
break
|
||||
elseif cand.run == "down" then
|
||||
update_cursor(1)
|
||||
elseif cand.run == "up" then
|
||||
update_cursor(-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
_permit = ya.hide()
|
||||
end
|
||||
|
||||
if (default_action == "nvim" or get_option() == "nvim" ) and args[1] ~= "fzf" then
|
||||
os.execute("nvim +"..line_number.." -n "..file_url)
|
||||
elseif (default_action == "jump" or get_option() == "jump" or args[1] == "fzf") and file_url ~= "" then
|
||||
ya.manager_emit(file_url:match("[/\\]$") and "cd" or "reveal", { file_url })
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function M:reflow() return { self } end
|
||||
|
||||
function M:redraw()
|
||||
local rows = {}
|
||||
|
||||
rows[1] = ui.Row { "open with nvim" }
|
||||
rows[2] = ui.Row { "reach at yazi" }
|
||||
return {
|
||||
ui.Clear(self._area),
|
||||
ui.Border(ui.Border.ALL)
|
||||
:area(self._area)
|
||||
:type(ui.Border.ROUNDED)
|
||||
:style(ui.Style():fg("#82ab3a"))
|
||||
:title(ui.Line(self.title):align(ui.Line.CENTER):fg(self.title_color)),
|
||||
ui.Table(rows)
|
||||
:area(self._area:pad(ui.Pad(1, 2, 1, 2)))
|
||||
:header(ui.Row({ "Action for:"..self.file_url }):style(ui.Style():bold():fg("#e73c80")))
|
||||
:row(self.cursor)
|
||||
:row_style(ui.Style():fg("#82ab3a"):underline()),
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function M.fail(s, ...)
|
||||
ya.manager_emit("plugin", {"mount", args = "refresh" })
|
||||
ya.notify { title = "fg", content = string.format(s, ...), timeout = 10, level = "error" }
|
||||
end
|
||||
|
||||
function M:click() end
|
||||
|
||||
function M:scroll() end
|
||||
|
||||
function M:touch() end
|
||||
|
||||
function M:setup(config)
|
||||
self.default_action = (config and config.default_action) and config.default_action or "menu"
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2024 boydaihungst
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,102 +0,0 @@
|
|||
# file-extra-metadata
|
||||
|
||||
<!--toc:start-->
|
||||
|
||||
- [file-extra-metadata](#file-extra-metadata)
|
||||
- [Preview](#preview)
|
||||
- [Before:](#before)
|
||||
- [After:](#after)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [For developer](#for-developer)
|
||||
<!--toc:end-->
|
||||
|
||||
This is a Yazi plugin that replaces the default file previewer and spotter with extra information.
|
||||
|
||||
## Preview
|
||||
|
||||
### Before:
|
||||
|
||||
- Previewer
|
||||
|
||||

|
||||
|
||||
- Spotter (yazi >= v0.4 after 21/11/2024)
|
||||
|
||||

|
||||
|
||||
### After:
|
||||
|
||||
- Previewer
|
||||
|
||||

|
||||
|
||||
- Spotter (yazi >= v0.4 after 21/11/2024)
|
||||
|
||||

|
||||
|
||||
## Requirements
|
||||
|
||||
- [yazi >=0.4](https://github.com/sxyazi/yazi)
|
||||
- Tested on Linux. For MacOS, Windows: some fields will shows empty values.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin:
|
||||
|
||||
```sh
|
||||
ya pack -a boydaihungst/file-extra-metadata
|
||||
```
|
||||
|
||||
Add spotter keybind, makes sure not conflict with other `<Tab>` keybind in
|
||||
`manager` section:
|
||||
|
||||
```toml
|
||||
[manager]
|
||||
keymap = [
|
||||
# ...
|
||||
# Spotting
|
||||
{ on = "<Tab>", run = "spot", desc = "Spot hovered file" },
|
||||
]
|
||||
```
|
||||
|
||||
Create `~/.config/yazi/yazi.toml` and add:
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
append_previewers = [
|
||||
{ name = "*", run = "file-extra-metadata" },
|
||||
]
|
||||
# yazi v0.4 after 21/11/2024
|
||||
# Setup keybind for spotter: https://github.com/sxyazi/yazi/pull/1802
|
||||
append_spotters = [
|
||||
{ name = "*", run = "file-extra-metadata" },
|
||||
]
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
previewers = [
|
||||
# ... the rest
|
||||
# disable default file plugin { name = "*", run = "file" },
|
||||
{ name = "*", run = "file-extra-metadata" },
|
||||
]
|
||||
# yazi v0.4 after 21/11/2024
|
||||
# Setup keybind for spotter: https://github.com/sxyazi/yazi/pull/1802
|
||||
spotters = [
|
||||
# ... the rest
|
||||
# Fallback
|
||||
# { name = "*", run = "file" },
|
||||
{ name = "*", run = "file-extra-metadata" },
|
||||
]
|
||||
```
|
||||
|
||||
## For developer
|
||||
|
||||
If you want to compile this with other spotter/previewer:
|
||||
|
||||
```lua
|
||||
require("file-extra-metadata"):render_table(job, { show_plugins_section = true })
|
||||
```
|
|
@ -1,489 +0,0 @@
|
|||
local M = {}
|
||||
|
||||
local function permission(file)
|
||||
local h = file
|
||||
if not h then
|
||||
return ""
|
||||
end
|
||||
|
||||
local perm = h.cha:perm()
|
||||
if not perm then
|
||||
return ""
|
||||
end
|
||||
|
||||
local spans = ""
|
||||
for i = 1, #perm do
|
||||
local c = perm:sub(i, i)
|
||||
spans = spans .. c
|
||||
end
|
||||
return spans
|
||||
end
|
||||
|
||||
local function link_count(file)
|
||||
local h = file
|
||||
if h == nil or ya.target_family() ~= "unix" then
|
||||
return ""
|
||||
end
|
||||
|
||||
return h.cha.nlink
|
||||
end
|
||||
|
||||
local function owner_group(file)
|
||||
local h = file
|
||||
if h == nil or ya.target_family() ~= "unix" then
|
||||
return ""
|
||||
end
|
||||
return (ya.user_name(h.cha.uid) or tostring(h.cha.uid)) .. "/" .. (ya.group_name(h.cha.gid) or tostring(h.cha.gid))
|
||||
end
|
||||
|
||||
local file_size_and_folder_childs = function(file)
|
||||
local h = file
|
||||
if not h or h.cha.is_link then
|
||||
return ""
|
||||
end
|
||||
|
||||
return h.cha.len and ya.readable_size(h.cha.len) or ""
|
||||
end
|
||||
|
||||
--- get file timestamp
|
||||
---@param file any
|
||||
---@param type "mtime" | "atime" | "btime"
|
||||
---@return any
|
||||
local function fileTimestamp(file, type)
|
||||
local h = file
|
||||
if not h or h.cha.is_link then
|
||||
return ""
|
||||
end
|
||||
local time = math.floor(h.cha[type] or 0)
|
||||
if time == 0 then
|
||||
return ""
|
||||
else
|
||||
return os.date("%Y-%m-%d %H:%M", time)
|
||||
end
|
||||
end
|
||||
|
||||
-- Function to split a string by spaces (considering multiple spaces as one delimiter)
|
||||
local function split_by_whitespace(input)
|
||||
local result = {}
|
||||
for word in string.gmatch(input, "%S+") do
|
||||
table.insert(result, word)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function get_filesystem_extra(file)
|
||||
local result = {
|
||||
filesystem = "",
|
||||
device = "",
|
||||
type = "",
|
||||
used_space = "",
|
||||
avail_space = "",
|
||||
total_space = "",
|
||||
used_space_percent = "",
|
||||
avail_space_percent = "",
|
||||
error = nil,
|
||||
}
|
||||
local h = file
|
||||
local file_url = tostring(h.url)
|
||||
if not h or ya.target_family() ~= "unix" then
|
||||
return result
|
||||
end
|
||||
|
||||
local output, _ = Command("tail")
|
||||
:args({ "-n", "-1" })
|
||||
:stdin(Command("df"):args({ "-P", "-T", "-h", file_url }):stdout(Command.PIPED):spawn():take_stdout())
|
||||
:stdout(Command.PIPED)
|
||||
:output()
|
||||
|
||||
if output then
|
||||
-- Splitting the data
|
||||
local parts = split_by_whitespace(output.stdout)
|
||||
|
||||
-- Display the result
|
||||
for i, part in ipairs(parts) do
|
||||
if i == 1 then
|
||||
result.filesystem = part
|
||||
elseif i == 2 then
|
||||
result.device = part
|
||||
elseif i == 3 then
|
||||
result.total_space = part
|
||||
elseif i == 4 then
|
||||
result.used_space = part
|
||||
elseif i == 5 then
|
||||
result.avail_space = part
|
||||
elseif i == 6 then
|
||||
result.used_space_percent = part
|
||||
result.avail_space_percent = 100 - tonumber((string.match(part, "%d+") or "0"))
|
||||
elseif i == 7 then
|
||||
result.type = part
|
||||
end
|
||||
end
|
||||
else
|
||||
result.error = "tail, df are installed?"
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function attributes(file)
|
||||
local h = file
|
||||
local file_url = tostring(h.url)
|
||||
if not h or ya.target_family() ~= "unix" then
|
||||
return ""
|
||||
end
|
||||
|
||||
local output, _ = Command("lsattr"):args({ "-d", file_url }):stdout(Command.PIPED):output()
|
||||
|
||||
if output then
|
||||
-- Splitting the data
|
||||
local parts = split_by_whitespace(output.stdout)
|
||||
|
||||
-- Display the result
|
||||
for i, part in ipairs(parts) do
|
||||
if i == 1 then
|
||||
return part
|
||||
end
|
||||
end
|
||||
return ""
|
||||
else
|
||||
return "lsattr is installed?"
|
||||
end
|
||||
end
|
||||
|
||||
---shorten string
|
||||
---@param _s string string
|
||||
---@param _t string tail
|
||||
---@param _w number max characters
|
||||
---@return string
|
||||
local shorten = function(_s, _t, _w)
|
||||
local s = _s or utf8.len(_s)
|
||||
local t = _t or ""
|
||||
local ellipsis = "…" .. t
|
||||
local w = _w < utf8.len(ellipsis) and utf8.len(ellipsis) or _w
|
||||
local n_ellipsis = utf8.len(ellipsis) or 0
|
||||
if utf8.len(s) > w then
|
||||
return s:sub(1, (utf8.offset(s, w - n_ellipsis + 1) or 2) - 1) .. ellipsis
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local is_supported_table = type(ui.Table) ~= "nil" and type(ui.Row) ~= "nil"
|
||||
|
||||
local styles = {
|
||||
header = ui.Style():fg("green"),
|
||||
row_label = ui.Style():fg("reset"),
|
||||
row_value = ui.Style():fg("blue"),
|
||||
row_value_spot_hovered = ui.Style():fg("blue"):reverse(),
|
||||
}
|
||||
|
||||
function M:render_table(job, opts)
|
||||
local filesystem_extra = get_filesystem_extra(job.file)
|
||||
local prefix = " "
|
||||
local label_lines, value_lines, rows = {}, {}, {}
|
||||
local label_max_length = 15
|
||||
local file_name_extension = job.file.cha.is_dir and "…" or ("." .. (job.file.url.ext(job.file.url) or ""))
|
||||
|
||||
local row = function(key, value)
|
||||
local h = type(value) == "table" and #value or 1
|
||||
rows[#rows + 1] = ui.Row({ ui.Line(key):style(styles.row_label), ui.Line(value):style(styles.row_value) })
|
||||
:height(h)
|
||||
end
|
||||
|
||||
local file_name = shorten(
|
||||
job.file.name,
|
||||
file_name_extension,
|
||||
math.floor(job.area.w - label_max_length - utf8.len(file_name_extension))
|
||||
)
|
||||
local location =
|
||||
shorten(tostring(job.file.url:parent()), "", math.floor(job.area.w - label_max_length - utf8.len(prefix)))
|
||||
local filesystem_error = filesystem_extra.error
|
||||
and shorten(filesystem_extra.error, "", math.floor(job.area.w - label_max_length - utf8.len(prefix)))
|
||||
or nil
|
||||
local filesystem =
|
||||
shorten(filesystem_extra.filesystem, "", math.floor(job.area.w - label_max_length - utf8.len(prefix)))
|
||||
|
||||
if not is_supported_table then
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span("Metadata:"),
|
||||
}):style(styles.header)
|
||||
)
|
||||
table.insert(
|
||||
value_lines,
|
||||
ui.Line({
|
||||
ui.Span(""),
|
||||
})
|
||||
)
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("File:"),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(
|
||||
value_lines,
|
||||
ui.Line({
|
||||
ui.Span(file_name),
|
||||
}):style(styles.row_value)
|
||||
)
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Mimetype: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(job._mime or job.mime)):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Location: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(
|
||||
value_lines,
|
||||
ui.Line({
|
||||
ui.Span(location),
|
||||
}):style(styles.row_value)
|
||||
)
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Mode: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(permission(job.file)):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Attributes: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(attributes(job.file))):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Links: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(
|
||||
value_lines,
|
||||
ui.Line({
|
||||
ui.Span(tostring(link_count(job.file))),
|
||||
}):style(styles.row_value)
|
||||
)
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Owner: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(owner_group(job.file))):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Size: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(file_size_and_folder_childs(job.file))):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Created: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(fileTimestamp(job.file, "btime"))):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Modified: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(fileTimestamp(job.file, "mtime"))):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Accessed: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(fileTimestamp(job.file, "atime"))):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Filesystem: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(filesystem_error or filesystem)):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Device: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(filesystem_error or filesystem_extra.device)):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Type: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(value_lines, ui.Line(ui.Span(filesystem_error or filesystem_extra.type)):style(styles.row_value))
|
||||
|
||||
table.insert(
|
||||
label_lines,
|
||||
ui.Line({
|
||||
ui.Span(prefix),
|
||||
ui.Span("Free space: "),
|
||||
}):style(styles.row_label)
|
||||
)
|
||||
table.insert(
|
||||
value_lines,
|
||||
ui.Line(
|
||||
ui.Span(
|
||||
filesystem_extra.error
|
||||
or (
|
||||
filesystem_extra.avail_space
|
||||
.. " / "
|
||||
.. filesystem_extra.total_space
|
||||
.. " ("
|
||||
.. filesystem_extra.avail_space_percent
|
||||
.. "%)"
|
||||
)
|
||||
)
|
||||
):style(styles.row_value)
|
||||
)
|
||||
else
|
||||
rows[#rows + 1] = ui.Row({ "Metadata", "" }):style(styles.header)
|
||||
row(prefix .. "File:", file_name)
|
||||
row(prefix .. "Mimetype:", job.mime)
|
||||
row(prefix .. "Location:", location)
|
||||
row(prefix .. "Mode:", permission(job.file))
|
||||
row(prefix .. "Attributes:", attributes(job.file))
|
||||
row(prefix .. "Links:", tostring(link_count(job.file)))
|
||||
row(prefix .. "Owner:", owner_group(job.file))
|
||||
row(prefix .. "Size:", file_size_and_folder_childs(job.file))
|
||||
row(prefix .. "Created:", fileTimestamp(job.file, "btime"))
|
||||
row(prefix .. "Modified:", fileTimestamp(job.file, "mtime"))
|
||||
row(prefix .. "Accessed:", fileTimestamp(job.file, "atime"))
|
||||
row(prefix .. "Filesystem:", filesystem_error or filesystem)
|
||||
row(prefix .. "Device:", filesystem_error or filesystem_extra.device)
|
||||
row(prefix .. "Type:", filesystem_error or filesystem_extra.type)
|
||||
row(
|
||||
prefix .. "Free space:",
|
||||
filesystem_error
|
||||
or (
|
||||
(
|
||||
filesystem_extra.avail_space
|
||||
and filesystem_extra.total_space
|
||||
and filesystem_extra.avail_space_percent
|
||||
)
|
||||
and (filesystem_extra.avail_space .. " / " .. filesystem_extra.total_space .. " (" .. filesystem_extra.avail_space_percent .. "%)")
|
||||
or ""
|
||||
)
|
||||
)
|
||||
if opts and opts.show_plugins_section and PLUGIN then
|
||||
local spotter = PLUGIN.spotter(job.file.url, job.mime)
|
||||
local previewer = PLUGIN.previewer(job.file.url, job.mime)
|
||||
local fetchers = PLUGIN.fetchers(job.file, job.mime)
|
||||
local preloaders = PLUGIN.preloaders(job.file.url, job.mime)
|
||||
|
||||
for i, v in ipairs(fetchers) do
|
||||
fetchers[i] = v.cmd
|
||||
end
|
||||
for i, v in ipairs(preloaders) do
|
||||
preloaders[i] = v.cmd
|
||||
end
|
||||
|
||||
rows[#rows + 1] = ui.Row({ { "", "Plugins" }, "" }):height(2):style(styles.header)
|
||||
row(prefix .. "Spotter:", spotter and spotter.cmd or "")
|
||||
row(prefix .. "Previewer:", previewer and previewer.cmd or "")
|
||||
row(prefix .. "Fetchers:", #fetchers ~= 0 and fetchers or "")
|
||||
row(prefix .. "Preloaders:", #preloaders ~= 0 and preloaders or "")
|
||||
end
|
||||
end
|
||||
|
||||
if not is_supported_table then
|
||||
local areas = ui.Layout()
|
||||
:direction(ui.Layout.HORIZONTAL)
|
||||
:constraints({ ui.Constraint.Length(label_max_length), ui.Constraint.Fill(1) })
|
||||
:split(job.area)
|
||||
local label_area = areas[1]
|
||||
local value_area = areas[2]
|
||||
return {
|
||||
ui.Text(label_lines):area(label_area):align(ui.Text.LEFT):wrap(ui.Text.WRAP_NO),
|
||||
ui.Text(value_lines):area(value_area):align(ui.Text.LEFT):wrap(ui.Text.WRAP_NO),
|
||||
}
|
||||
else
|
||||
return {
|
||||
ui.Table(rows):area(job.area):row(1):col(1):col_style(styles.row_value):widths({
|
||||
ui.Constraint.Length(label_max_length),
|
||||
ui.Constraint.Fill(1),
|
||||
}),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function M:peek(job)
|
||||
local start, cache = os.clock(), ya.file_cache(job)
|
||||
if not cache or self:preload(job) ~= 1 then
|
||||
return 1
|
||||
end
|
||||
ya.sleep(math.max(0, PREVIEW.image_delay / 1000 + start - os.clock()))
|
||||
ya.preview_widgets(job, self:render_table(job))
|
||||
end
|
||||
|
||||
function M:seek(job)
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.url == job.file.url then
|
||||
local step = math.floor(job.units * job.area.h / 10)
|
||||
ya.manager_emit("peek", {
|
||||
tostring(math.max(0, cx.active.preview.skip + step)),
|
||||
only_if = tostring(job.file.url),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M:preload(job)
|
||||
local cache = ya.file_cache(job)
|
||||
if not cache or fs.cha(cache) then
|
||||
return 1
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
function M:spot(job)
|
||||
job.area = ui.Pos({ "center", w = 80, h = 25 })
|
||||
ya.spot_table(
|
||||
job,
|
||||
self:render_table(job, { show_plugins_section = true })[1]:cell_style(styles.row_value_spot_hovered)
|
||||
)
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Darius
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,29 +0,0 @@
|
|||
# lazygit.yazi
|
||||
Plugin for [Yazi](https://github.com/sxyazi/yazi) to manage git repos with [lazygit](https://github.com/jesseduffield/lazygit)
|
||||
## Dependencies
|
||||
Make sure [lazygit](https://github.com/jesseduffield/lazygit) is installed and in your `PATH`.
|
||||
## Installation
|
||||
|
||||
### Using `ya pack`
|
||||
```
|
||||
ya pack -a Lil-Dank/lazygit
|
||||
```
|
||||
|
||||
### Manual
|
||||
**Linux/macOS**
|
||||
```
|
||||
git clone https://github.com/Lil-Dank/lazygit.yazi.git ~/.config/yazi/plugins/lazygit.yazi
|
||||
```
|
||||
**Windows**
|
||||
```
|
||||
git clone https://github.com/Lil-Dank/lazygit.yazi.git %AppData%\yazi\config\plugins\lazygit.yazi
|
||||
```
|
||||
## Configuration
|
||||
add this to your **keymap.toml** file
|
||||
```toml
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "g", "i" ]
|
||||
run = "plugin lazygit"
|
||||
desc = "run lazygit"
|
||||
```
|
||||
you can customize the keybinding however you like. Please refer to the [keymap.toml](https://yazi-rs.github.io/docs/configuration/keymap) documentation
|
|
@ -1,31 +0,0 @@
|
|||
return {
|
||||
entry = function()
|
||||
local output = Command("git"):arg("status"):stderr(Command.PIPED):output()
|
||||
if output.stderr ~= "" then
|
||||
ya.notify({
|
||||
title = "lazygit",
|
||||
content = "Not in a git directory",
|
||||
level = "warn",
|
||||
timeout = 5,
|
||||
})
|
||||
else
|
||||
permit = ya.hide()
|
||||
local output, err_code = Command("lazygit"):stderr(Command.PIPED):output()
|
||||
if err_code ~= nil then
|
||||
ya.notify({
|
||||
title = "Failed to run lazygit command",
|
||||
content = "Status: " .. err_code,
|
||||
level = "error",
|
||||
timeout = 5,
|
||||
})
|
||||
elseif not output.status.success then
|
||||
ya.notify({
|
||||
title = "lazygit in" .. cwd .. "failed, exit code " .. output.status.code,
|
||||
content = output.stderr,
|
||||
level = "error",
|
||||
timeout = 5,
|
||||
})
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2024 Lauri Niskanen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,26 +0,0 @@
|
|||
> [!NOTE]
|
||||
> Please use **boydaihungst**'s fork for the latest version of this plugin:
|
||||
> https://github.com/boydaihungst/mediainfo.yazi
|
||||
|
||||
# mediainfo.yazi
|
||||
|
||||
This is a Yazi plugin for previewing media files. The preview shows thumbnail
|
||||
using `ffmpegthumbnailer` if available and media metadata using `mediainfo`.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin:
|
||||
|
||||
```
|
||||
ya pack -a Ape/mediainfo
|
||||
```
|
||||
|
||||
Create `~/.config/yazi/yazi.toml` and add:
|
||||
|
||||
```
|
||||
[plugin]
|
||||
prepend_previewers = [
|
||||
{ mime = "{image,audio,video}/*", run = "mediainfo"},
|
||||
{ mime = "application/x-subrip", run = "mediainfo"},
|
||||
]
|
||||
```
|
|
@ -1,111 +0,0 @@
|
|||
local skip_labels = {
|
||||
["Complete name"] = true,
|
||||
["CompleteName_Last"] = true,
|
||||
["Unique ID"] = true,
|
||||
["File size"] = true,
|
||||
["Format/Info"] = true,
|
||||
["Codec ID/Info"] = true,
|
||||
["MD5 of the unencoded content"] = true,
|
||||
}
|
||||
|
||||
local M = {}
|
||||
|
||||
function M:peek()
|
||||
local image_height = 0
|
||||
|
||||
if self:preload() == 1 then
|
||||
local cache = ya.file_cache(self)
|
||||
if cache and fs.cha(cache).length > 0 then
|
||||
image_height = ya.image_show(cache, self.area).h
|
||||
end
|
||||
end
|
||||
|
||||
local cmd = "mediainfo"
|
||||
local output, code = Command(cmd)
|
||||
:args({ tostring(self.file.url) })
|
||||
:stdout(Command.PIPED)
|
||||
:output()
|
||||
|
||||
local lines = {}
|
||||
|
||||
if output then
|
||||
local i = 0
|
||||
for str in output.stdout:gmatch("[^\n]*") do
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
|
||||
if label then
|
||||
if not skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):bold(),
|
||||
ui.Span(value),
|
||||
})
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):underline() })
|
||||
end
|
||||
|
||||
if line then
|
||||
if i >= self.skip then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
local max_width = math.max(1, self.area.w - 3)
|
||||
i = i + math.max(1, math.ceil(line:width() / max_width))
|
||||
end
|
||||
end
|
||||
else
|
||||
local error = string.format("Spawn `%s` command returns %s", cmd, code)
|
||||
table.insert(lines, ui.Line(error))
|
||||
end
|
||||
|
||||
ya.preview_widgets(self, {
|
||||
ui.Paragraph(
|
||||
ui.Rect({
|
||||
x = self.area.x,
|
||||
y = self.area.y + image_height,
|
||||
w = self.area.w,
|
||||
h = self.area.h - image_height,
|
||||
}),
|
||||
lines
|
||||
):wrap(ui.Paragraph.WRAP),
|
||||
})
|
||||
end
|
||||
|
||||
function M:seek(units)
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.url == self.file.url then
|
||||
local step = math.floor(units * self.area.h / 10)
|
||||
ya.manager_emit("peek", {
|
||||
math.max(0, cx.active.preview.skip + step),
|
||||
only_if = self.file.url,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M:preload()
|
||||
local cache = ya.file_cache(self)
|
||||
if not cache or fs.cha(cache) then
|
||||
return 1
|
||||
end
|
||||
|
||||
local cmd = "ffmpegthumbnailer"
|
||||
local child, code = Command(cmd):args({
|
||||
"-q", "6",
|
||||
"-c", "jpeg",
|
||||
"-i", tostring(self.file.url),
|
||||
"-o", tostring(cache),
|
||||
"-t", "5",
|
||||
"-s", tostring(PREVIEW.max_width),
|
||||
}):spawn()
|
||||
|
||||
if not child then
|
||||
ya.err(string.format("spawn `%s` command returns %s", cmd, code))
|
||||
return 0
|
||||
end
|
||||
|
||||
local status = child:wait()
|
||||
return status and status.success and 1 or 2
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 yazi-rs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,56 +0,0 @@
|
|||
# mime-ext.yazi
|
||||
|
||||
A mime-type provider based on a file extension database, replacing the [builtin `file(1)`](https://github.com/sxyazi/yazi/blob/main/yazi-plugin/preset/plugins/mime.lua) to speed up mime-type retrieval at the expense of accuracy.
|
||||
|
||||
See https://yazi-rs.github.io/docs/tips#make-yazi-even-faster for more information.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
ya pack -a yazi-rs/plugins:mime-ext
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `~/.config/yazi/yazi.toml`:
|
||||
|
||||
```toml
|
||||
[[plugin.prepend_fetchers]]
|
||||
id = "mime"
|
||||
name = "*"
|
||||
run = "mime-ext"
|
||||
prio = "high"
|
||||
```
|
||||
|
||||
## Advanced
|
||||
|
||||
You can also customize it in your `~/.config/yazi/init.lua` with:
|
||||
|
||||
```lua
|
||||
require("mime-ext"):setup {
|
||||
-- Expand the existing filename database (lowercase), for example:
|
||||
with_files = {
|
||||
makefile = "text/makefile",
|
||||
-- ...
|
||||
},
|
||||
|
||||
-- Expand the existing extension database (lowercase), for example:
|
||||
with_exts = {
|
||||
mk = "text/makefile",
|
||||
-- ...
|
||||
},
|
||||
|
||||
-- If the mime-type is not in both filename and extension databases,
|
||||
-- then fallback to Yazi's preset `mime` plugin, which uses `file(1)`
|
||||
fallback_file1 = false,
|
||||
}
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- Add more file types (PRs welcome!).
|
||||
- Compress mime-type tables.
|
||||
|
||||
## License
|
||||
|
||||
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file.
|
File diff suppressed because it is too large
Load diff
|
@ -1,20 +0,0 @@
|
|||
return {
|
||||
entry = function(_, job)
|
||||
local block = job.args[1] and job.args[1] == "block"
|
||||
|
||||
local value, event = ya.input({
|
||||
title = block and "Open with (block):" or "Open with:",
|
||||
position = { "hovered", y = 1, w = 50 },
|
||||
})
|
||||
|
||||
if event == 1 then
|
||||
local s = ya.target_family() == "windows" and " %*" or ' "$@"'
|
||||
ya.manager_emit("shell", {
|
||||
value .. s,
|
||||
block = block,
|
||||
orphan = true,
|
||||
confirm = true,
|
||||
})
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 ndtoan96
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,83 +0,0 @@
|
|||
# ouch.yazi
|
||||
|
||||
[ouch](https://github.com/ouch-org/ouch) plugin for [Yazi](https://github.com/sxyazi/yazi).
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
- Archive preview
|
||||
- Compression
|
||||
|
||||
## Installation
|
||||
|
||||
If you use Yazi from latest main branch
|
||||
```bash
|
||||
# Linux/macOS
|
||||
git clone https://github.com/ndtoan96/ouch.yazi.git ~/.config/yazi/plugins/ouch.yazi
|
||||
|
||||
# Windows with cmd
|
||||
git clone https://github.com/ndtoan96/ouch.yazi.git %AppData%\yazi\config\plugins\ouch.yazi
|
||||
|
||||
# Windows with powershell
|
||||
git clone https://github.com/ndtoan96/ouch.yazi.git "$($env:APPDATA)\yazi\config\plugins\ouch.yazi"
|
||||
```
|
||||
|
||||
If you use Yazi < 0.4.3
|
||||
```bash
|
||||
# Linux/macOS
|
||||
git clone --branch v0.4.0 --single-branch https://github.com/ndtoan96/ouch.yazi.git ~/.config/yazi/plugins/ouch.yazi
|
||||
|
||||
# Windows with cmd
|
||||
git clone --branch v0.4.0 --single-branch https://github.com/ndtoan96/ouch.yazi.git %AppData%\yazi\config\plugins\ouch.yazi
|
||||
|
||||
# Windows with powershell
|
||||
git clone --branch v0.4.0 --single-branch https://github.com/ndtoan96/ouch.yazi.git "$($env:APPDATA)\yazi\config\plugins\ouch.yazi"
|
||||
```
|
||||
|
||||
Make sure you have [ouch](https://github.com/ouch-org/ouch) installed and in your `PATH`.
|
||||
|
||||
## Usage
|
||||
|
||||
### Preview
|
||||
For archive preview, add this to your `yazi.toml`:
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
prepend_previewers = [
|
||||
# Archive previewer
|
||||
{ mime = "application/*zip", run = "ouch" },
|
||||
{ mime = "application/x-tar", run = "ouch" },
|
||||
{ mime = "application/x-bzip2", run = "ouch" },
|
||||
{ mime = "application/x-7z-compressed", run = "ouch" },
|
||||
{ mime = "application/x-rar", run = "ouch" },
|
||||
{ mime = "application/x-xz", run = "ouch" },
|
||||
]
|
||||
```
|
||||
|
||||
Now go to an archive on Yazi, you should see the archive's content in the preview pane. You can use `J` and `K` to roll up and down the preview.
|
||||
|
||||
If you want to change the icon or the style of text, you can modify the `peek` function in `init.lua` file (all of them are stored in the `lines` variable).
|
||||
|
||||
### Compression
|
||||
For compession, add this to your `keymap.toml`:
|
||||
|
||||
```toml
|
||||
[[manager.prepend_keymap]]
|
||||
on = ["C"]
|
||||
run = "plugin ouch --args=zip"
|
||||
desc = "Compress with ouch"
|
||||
```
|
||||
|
||||
The `--args=zip` part tells the plugin that default format is `zip`. You can change that to whatever format you want.
|
||||
|
||||
### Decompression
|
||||
This plugin does not provide a decompression feature because it already is supported by Yazi.
|
||||
To decompress with `ouch`, configure the opener in `yazi.toml`.
|
||||
|
||||
```toml
|
||||
[opener]
|
||||
extract = [
|
||||
{ run = 'ouch d -y "%*"', desc = "Extract here with ouch", for = "windows" },
|
||||
{ run = 'ouch d -y "$@"', desc = "Extract here with ouch", for = "unix" },
|
||||
]
|
||||
```
|
|
@ -1,145 +0,0 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek(job)
|
||||
local child = Command("ouch")
|
||||
:args({ "l", "-t", "-y", tostring(job.file.url) })
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
:spawn()
|
||||
local limit = job.area.h
|
||||
local file_name = string.match(tostring(job.file.url), ".*[/\\](.*)")
|
||||
local lines = string.format("📁 \x1b[2m%s\x1b[0m\n", file_name)
|
||||
local num_lines = 1
|
||||
local num_skip = 0
|
||||
repeat
|
||||
local line, event = child:read_line()
|
||||
if event == 1 then
|
||||
ya.err(tostring(event))
|
||||
elseif event ~= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
if line:find('Archive', 1, true) ~= 1 and line:find('[INFO]', 1, true) ~= 1 then
|
||||
if num_skip >= job.skip then
|
||||
lines = lines .. line
|
||||
num_lines = num_lines + 1
|
||||
else
|
||||
num_skip = num_skip + 1
|
||||
end
|
||||
end
|
||||
until num_lines >= limit
|
||||
|
||||
child:start_kill()
|
||||
if job.skip > 0 and num_lines < limit then
|
||||
ya.manager_emit(
|
||||
"peek",
|
||||
{ tostring(math.max(0, job.skip - (limit - num_lines))), only_if = tostring(job.file.url), upper_bound = "" }
|
||||
)
|
||||
else
|
||||
ya.preview_widgets(job, { ui.Text(lines):area(job.area) })
|
||||
end
|
||||
end
|
||||
|
||||
function M:seek(job)
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.url == job.file.url then
|
||||
local step = math.floor(job.units * job.area.h / 10)
|
||||
ya.manager_emit("peek", {
|
||||
math.max(0, cx.active.preview.skip + step),
|
||||
only_if = tostring(job.file.url),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if file exists
|
||||
local function file_exists(name)
|
||||
local f = io.open(name, "r")
|
||||
if f ~= nil then
|
||||
io.close(f)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Get the files that need to be compressed and infer a default archive name
|
||||
local get_compression_target = ya.sync(function()
|
||||
local tab = cx.active
|
||||
local default_name
|
||||
local paths = {}
|
||||
if #tab.selected == 0 then
|
||||
if tab.current.hovered then
|
||||
local name = tab.current.hovered.name
|
||||
default_name = name
|
||||
table.insert(paths, name)
|
||||
else
|
||||
return
|
||||
end
|
||||
else
|
||||
default_name = tab.current.cwd:name()
|
||||
for _, url in pairs(tab.selected) do
|
||||
table.insert(paths, tostring(url))
|
||||
end
|
||||
-- The compression targets are aquired, now unselect them
|
||||
ya.manager_emit("escape", {})
|
||||
end
|
||||
return paths, default_name
|
||||
end)
|
||||
|
||||
local function invoke_compress_command(paths, name)
|
||||
local cmd_output, err_code = Command("ouch")
|
||||
:args({ "c", "-y" })
|
||||
:args(paths)
|
||||
:arg(name)
|
||||
:stderr(Command.PIPED)
|
||||
:output()
|
||||
if err_code ~= nil then
|
||||
ya.notify({
|
||||
title = "Failed to run ouch command",
|
||||
content = "Status: " .. err_code,
|
||||
timeout = 5.0,
|
||||
level = "error",
|
||||
})
|
||||
elseif not cmd_output.status.success then
|
||||
ya.notify({
|
||||
title = "Compression failed: status code " .. cmd_output.status.code,
|
||||
content = cmd_output.stderr,
|
||||
timeout = 5.0,
|
||||
level = "error",
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function M:entry(job)
|
||||
local default_fmt = job.args[1]
|
||||
|
||||
ya.manager_emit("escape", { visual = true })
|
||||
|
||||
-- Get the files that need to be compressed and infer a default archive name
|
||||
local paths, default_name = get_compression_target()
|
||||
|
||||
-- Get archive name from user
|
||||
local output_name, name_event = ya.input({
|
||||
title = "Create archive:",
|
||||
value = default_name .. "." .. default_fmt,
|
||||
position = { "top-center", y = 3, w = 40 },
|
||||
})
|
||||
if name_event ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get confirmation if file exists
|
||||
if file_exists(output_name) then
|
||||
local confirm, confirm_event = ya.input({
|
||||
title = "Overwrite " .. output_name .. "? (y/N)",
|
||||
position = { "top-center", y = 3, w = 40 },
|
||||
})
|
||||
if not (confirm_event == 1 and confirm:lower() == "y") then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
invoke_compress_command(paths, output_name)
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,280 +0,0 @@
|
|||
--- @since 25.2.7
|
||||
|
||||
local M = {}
|
||||
local shell = os.getenv("SHELL") or ""
|
||||
local PackageName = "Restore"
|
||||
local function success(s, ...)
|
||||
ya.notify({ title = PackageName, content = string.format(s, ...), timeout = 5, level = "info" })
|
||||
end
|
||||
|
||||
local function fail(s, ...)
|
||||
ya.notify({ title = PackageName, content = string.format(s, ...), timeout = 5, level = "error" })
|
||||
end
|
||||
|
||||
---@enum STATE
|
||||
local STATE = {
|
||||
POSITION = "position",
|
||||
SHOW_CONFIRM = "show_confirm",
|
||||
THEME = "theme",
|
||||
}
|
||||
|
||||
local set_state = ya.sync(function(state, key, value)
|
||||
if state then
|
||||
state[key] = value
|
||||
else
|
||||
state = {}
|
||||
state[key] = value
|
||||
end
|
||||
end)
|
||||
|
||||
local get_state = ya.sync(function(state, key)
|
||||
if state then
|
||||
return state[key]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end)
|
||||
|
||||
---@enum File_Type
|
||||
local File_Type = {
|
||||
File = "file",
|
||||
Dir = "dir_all",
|
||||
None_Exist = "unknown",
|
||||
}
|
||||
|
||||
---@alias TRASHED_ITEM {trash_index: number, trashed_date_time: string, trashed_path: string, type: File_Type} Item in trash list
|
||||
|
||||
function get_basename(filepath)
|
||||
return filepath:match("^.+/(.+)$") or filepath
|
||||
end
|
||||
|
||||
local get_cwd = ya.sync(function()
|
||||
return tostring(cx.active.current.cwd)
|
||||
end)
|
||||
|
||||
local function path_quote(path)
|
||||
local result = "'" .. string.gsub(path, "'", "'\\''") .. "'"
|
||||
return result
|
||||
end
|
||||
|
||||
local function get_file_type(path)
|
||||
local cha, _ = fs.cha(Url(path))
|
||||
if cha then
|
||||
return cha.is_dir and File_Type.Dir or File_Type.File
|
||||
else
|
||||
return File_Type.None_Exist
|
||||
end
|
||||
end
|
||||
|
||||
local function get_trash_volume()
|
||||
local cwd = get_cwd()
|
||||
local trash_volumes_stream, cmr_err =
|
||||
Command("trash-list"):args({ "--volumes" }):stdout(Command.PIPED):stderr(Command.PIPED):output()
|
||||
|
||||
local matched_vol_path = nil
|
||||
if trash_volumes_stream then
|
||||
local matched_vol_length = 0
|
||||
for vol in trash_volumes_stream.stdout:gmatch("[^\r\n]+") do
|
||||
local vol_length = utf8.len(vol) or 0
|
||||
if cwd:sub(1, vol_length) == vol and vol_length > matched_vol_length then
|
||||
matched_vol_path = vol
|
||||
matched_vol_length = vol_length
|
||||
end
|
||||
end
|
||||
if not matched_vol_path then
|
||||
fail("Can't get trash directory")
|
||||
end
|
||||
else
|
||||
fail("Failed to start `trash-list` with error: `%s`. Do you have `trash-cli` installed?", cmr_err)
|
||||
end
|
||||
return matched_vol_path
|
||||
end
|
||||
|
||||
---get list of latest files/folders trashed
|
||||
---@param curr_working_volume currently working volume
|
||||
---@return TRASHED_ITEM[]|nil
|
||||
local function get_latest_trashed_items(curr_working_volume)
|
||||
---@type TRASHED_ITEM[]
|
||||
local restorable_items = {}
|
||||
local fake_enter = Command("printf"):stderr(Command.PIPED):stdout(Command.PIPED):spawn():take_stdout()
|
||||
local trash_list_stream, err_cmd = Command(shell)
|
||||
:args({ "-c", "trash-restore " .. path_quote(curr_working_volume) })
|
||||
:stdin(fake_enter)
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
:output()
|
||||
|
||||
if trash_list_stream then
|
||||
---@type TRASHED_ITEM[]
|
||||
local trash_list = {}
|
||||
for line in trash_list_stream.stdout:gmatch("[^\r\n]+") do
|
||||
-- remove leading spaces
|
||||
line = line:match("^%s*(.+)$")
|
||||
local trash_index, item_date, item_path = line:match("^(%d+) (%S+ %S+) (.+)$")
|
||||
if item_date and item_path and trash_index ~= nil then
|
||||
table.insert(trash_list, {
|
||||
trash_index = tonumber(trash_index),
|
||||
trashed_date_time = item_date,
|
||||
trashed_path = item_path,
|
||||
type = File_Type.None_Exist,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if #trash_list == 0 then
|
||||
success("Nothing left to restore")
|
||||
return
|
||||
end
|
||||
|
||||
local last_item_datetime = trash_list[#trash_list].trashed_date_time
|
||||
|
||||
for _, trash_item in ipairs(trash_list) do
|
||||
if trash_item then
|
||||
if trash_item.trashed_date_time == last_item_datetime then
|
||||
trash_item.type = get_file_type(trash_item.trashed_path)
|
||||
table.insert(restorable_items, trash_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
fail("Failed to start `trash-restore` with error: `%s`. Do you have `trash-cli` installed?", err_cmd)
|
||||
return
|
||||
end
|
||||
return restorable_items
|
||||
-- return newest_trashed_items
|
||||
end
|
||||
|
||||
---@param trash_list TRASHED_ITEM[]
|
||||
local function filter_none_exised_paths(trash_list)
|
||||
---@type TRASHED_ITEM[]
|
||||
local existed_trash_items = {}
|
||||
for _, v in ipairs(trash_list) do
|
||||
if v.type ~= File_Type.None_Exist then
|
||||
table.insert(existed_trash_items, v)
|
||||
end
|
||||
end
|
||||
return existed_trash_items
|
||||
end
|
||||
|
||||
local function restore_files(curr_working_volume, start_index, end_index)
|
||||
if type(start_index) ~= "number" or type(end_index) ~= "number" or start_index < 0 or end_index < 0 then
|
||||
fail("Failed to restore file(s): out of range")
|
||||
return
|
||||
end
|
||||
|
||||
ya.manager_emit("shell", {
|
||||
"echo " .. ya.quote(start_index .. "-" .. end_index) .. " | trash-restore --overwrite " .. path_quote(
|
||||
curr_working_volume
|
||||
),
|
||||
confirm = true,
|
||||
})
|
||||
local file_to_restore_count = end_index - start_index + 1
|
||||
success("Restored " .. tostring(file_to_restore_count) .. " file" .. (file_to_restore_count > 1 and "s" or ""))
|
||||
end
|
||||
|
||||
function M:setup(opts)
|
||||
if opts and opts.position and type(opts.position) == "table" then
|
||||
set_state(STATE.POSITION, opts.position)
|
||||
else
|
||||
set_state(STATE.POSITION, { "center", w = 70, h = 40 })
|
||||
end
|
||||
if opts and opts.show_confirm then
|
||||
set_state(STATE.SHOW_CONFIRM, opts.show_confirm)
|
||||
else
|
||||
set_state(STATE.SHOW_CONFIRM, false)
|
||||
end
|
||||
if opts and opts.theme and type(opts.theme) == "table" then
|
||||
set_state(STATE.THEME, opts.theme)
|
||||
else
|
||||
set_state(STATE.THEME, {})
|
||||
end
|
||||
end
|
||||
|
||||
---@param trash_list TRASHED_ITEM[]
|
||||
local function get_components(trash_list)
|
||||
local theme = get_state(STATE.THEME) or {}
|
||||
theme.list_item = theme.list_item or {
|
||||
odd = "blue",
|
||||
even = "blue",
|
||||
}
|
||||
local trashed_items_components = {}
|
||||
for idx, item in pairs(trash_list) do
|
||||
local fg_color = theme.list_item.odd or "blue"
|
||||
if idx % 2 == 0 then
|
||||
fg_color = theme.list_item.even or "blue"
|
||||
end
|
||||
table.insert(
|
||||
trashed_items_components,
|
||||
ui.Line({
|
||||
ui.Span(" "),
|
||||
ui.Span(item.trashed_path):fg(fg_color),
|
||||
}):align(ui.Line.LEFT)
|
||||
)
|
||||
end
|
||||
return trashed_items_components
|
||||
end
|
||||
|
||||
function M:entry()
|
||||
local curr_working_volume = get_trash_volume()
|
||||
if not curr_working_volume then
|
||||
return
|
||||
end
|
||||
local trashed_items = get_latest_trashed_items(curr_working_volume)
|
||||
if trashed_items == nil then
|
||||
return
|
||||
end
|
||||
local collided_items = filter_none_exised_paths(trashed_items)
|
||||
local overwrite_confirmed = true
|
||||
local show_confirm = get_state(STATE.SHOW_CONFIRM)
|
||||
local pos = get_state(STATE.POSITION)
|
||||
pos = pos or { "center", w = 70, h = 40 }
|
||||
|
||||
local theme = get_state(STATE.THEME) or {}
|
||||
theme.title = theme.title or "blue"
|
||||
theme.header = theme.header or "green"
|
||||
theme.header_warning = theme.header_warning or "yellow"
|
||||
if ya.confirm and show_confirm then
|
||||
local continue_restore = ya.confirm({
|
||||
title = ui.Line("Restore files/folders"):fg(theme.title):bold(),
|
||||
content = ui.Text({
|
||||
ui.Line(""),
|
||||
ui.Line("The following files and folders are going to be restored:"):fg(theme.header),
|
||||
ui.Line(""),
|
||||
table.unpack(get_components(trashed_items)),
|
||||
})
|
||||
:align(ui.Text.LEFT)
|
||||
:wrap(ui.Text.WRAP),
|
||||
--TODO: still wating for API :/
|
||||
-- list = ui.List({
|
||||
-- table.unpack(get_components(trashed_items)),
|
||||
-- }),
|
||||
|
||||
pos = pos,
|
||||
})
|
||||
-- stopping
|
||||
if not continue_restore then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- show Confirm dialog with list of collided items
|
||||
if #collided_items > 0 then
|
||||
overwrite_confirmed = ya.confirm({
|
||||
title = ui.Line("Restore files/folders"):fg(theme.title):bold(),
|
||||
content = ui.Text({
|
||||
ui.Line(""),
|
||||
ui.Line("The following files and folders are existed, overwrite?"):fg(theme.header_warning),
|
||||
ui.Line(""),
|
||||
table.unpack(get_components(collided_items)),
|
||||
})
|
||||
:align(ui.Text.LEFT)
|
||||
:wrap(ui.Text.WRAP),
|
||||
pos = pos,
|
||||
})
|
||||
end
|
||||
if overwrite_confirmed then
|
||||
restore_files(curr_working_volume, trashed_items[1].trash_index, trashed_items[#trashed_items].trash_index)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,53 +0,0 @@
|
|||
local M = {}
|
||||
|
||||
function M:peek(job)
|
||||
local child = Command("rich")
|
||||
:args({
|
||||
"-j",
|
||||
"--left",
|
||||
"--line-numbers",
|
||||
"--force-terminal",
|
||||
"--panel=rounded",
|
||||
"--guides",
|
||||
"--max-width",
|
||||
tostring(job.area.w),
|
||||
tostring(job.file.url),
|
||||
})
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
:spawn()
|
||||
|
||||
if not child then
|
||||
return require("code"):peek(job)
|
||||
end
|
||||
|
||||
local limit = job.area.h
|
||||
local i, lines = 0, ""
|
||||
repeat
|
||||
local next, event = child:read_line()
|
||||
if event == 1 then
|
||||
return require("code"):peek(job)
|
||||
elseif event ~= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
if i > job.skip then
|
||||
lines = lines .. next
|
||||
end
|
||||
until i >= job.skip + limit
|
||||
|
||||
child:start_kill()
|
||||
if job.skip > 0 and i < job.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true })
|
||||
else
|
||||
lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size))
|
||||
ya.preview_widgets(job, { ui.Text.parse(lines):area(job.area) })
|
||||
end
|
||||
end
|
||||
|
||||
function M:seek(job)
|
||||
require("code"):seek(job)
|
||||
end
|
||||
|
||||
return M
|
|
@ -1,13 +0,0 @@
|
|||
--- @sync entry
|
||||
return {
|
||||
entry = function()
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.cha.is_dir then
|
||||
ya.manager_emit("enter", {})
|
||||
ya.manager_emit("paste", {})
|
||||
ya.manager_emit("leave", {})
|
||||
else
|
||||
ya.manager_emit("paste", {})
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -2,6 +2,6 @@
|
|||
return {
|
||||
entry = function()
|
||||
local h = cx.active.current.hovered
|
||||
ya.manager_emit("tab_create", h and h.cha.is_dir and { h.url } or { current = true })
|
||||
ya.mgr_emit("tab_create", h and h.cha.is_dir and { h.url } or { current = true })
|
||||
end,
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Rolv Apneseth
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,109 +0,0 @@
|
|||
# starship.yazi
|
||||
|
||||
Starship prompt plugin for [Yazi](https://github.com/sxyazi/yazi)
|
||||
|
||||
<https://github.com/Rolv-Apneseth/starship.yazi/assets/69486699/f7314687-5cb1-4d66-8d9d-cca960ba6716>
|
||||
|
||||
## Requirements
|
||||
|
||||
- [Yazi](https://github.com/sxyazi/yazi)
|
||||
- [starship](https://github.com/starship/starship)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
ya pack -a Rolv-Apneseth/starship
|
||||
```
|
||||
|
||||
### Manual
|
||||
|
||||
```sh
|
||||
# Linux / MacOS
|
||||
git clone https://github.com/Rolv-Apneseth/starship.yazi.git ~/.config/yazi/plugins/starship.yazi
|
||||
# Windows
|
||||
git clone https://github.com/Rolv-Apneseth/starship.yazi.git %AppData%\yazi\config\plugins\starship.yazi
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to `~/.config/yazi/init.lua`:
|
||||
|
||||
```lua
|
||||
require("starship"):setup()
|
||||
```
|
||||
|
||||
Make sure you have [starship](https://github.com/starship/starship) installed and in your `PATH`.
|
||||
|
||||
## Config
|
||||
|
||||
Here is an example with all available config options:
|
||||
|
||||
```lua
|
||||
require("starship"):setup({
|
||||
-- Hide flags (such as filter, find and search). This is recommended for starship themes which
|
||||
-- are intended to go across the entire width of the terminal.
|
||||
hide_flags = false, -- Default: false
|
||||
-- Whether to place flags after the starship prompt. False means the flags will be placed before the prompt.
|
||||
flags_after_prompt = true, -- Default: true
|
||||
-- Custom starship configuration file to use
|
||||
config_file = "~/.config/starship_full.toml", -- Default: nil
|
||||
})
|
||||
```
|
||||
|
||||
## Extra
|
||||
|
||||
If you use a `starship` theme with a background colour, it might look a bit to cramped on just the one line `Yazi` gives the header by default. To fix this, you can add this to your `init.lua`:
|
||||
|
||||
<details>
|
||||
<summary>Click to expand</summary>
|
||||
|
||||
```lua
|
||||
local old_build = Tab.build
|
||||
|
||||
Tab.build = function(self, ...)
|
||||
local bar = function(c, x, y)
|
||||
if x <= 0 or x == self._area.w - 1 then
|
||||
return ui.Bar(ui.Bar.TOP):area(ui.Rect.default)
|
||||
end
|
||||
|
||||
return ui.Bar(ui.Bar.TOP)
|
||||
:area(ui.Rect({
|
||||
x = x,
|
||||
y = math.max(0, y),
|
||||
w = ya.clamp(0, self._area.w - x, 1),
|
||||
h = math.min(1, self._area.h),
|
||||
}))
|
||||
:symbol(c)
|
||||
end
|
||||
|
||||
local c = self._chunks
|
||||
self._chunks = {
|
||||
c[1]:padding(ui.Padding.y(1)),
|
||||
c[2]:padding(ui.Padding(c[1].w > 0 and 0 or 1, c[3].w > 0 and 0 or 1, 1, 1)),
|
||||
c[3]:padding(ui.Padding.y(1)),
|
||||
}
|
||||
|
||||
local style = THEME.manager.border_style
|
||||
self._base = ya.list_merge(self._base or {}, {
|
||||
ui.Border(ui.Border.ALL):area(self._area):type(ui.Border.ROUNDED):style(style),
|
||||
ui.Bar(ui.Bar.RIGHT):area(self._chunks[1]):style(style),
|
||||
ui.Bar(ui.Bar.LEFT):area(self._chunks[1]):style(style),
|
||||
|
||||
bar("┬", c[1].right - 1, c[1].y),
|
||||
bar("┴", c[1].right - 1, c[1].bottom - 1),
|
||||
bar("┬", c[2].right, c[2].y),
|
||||
bar("┴", c[2].right, c[2].bottom - 1),
|
||||
})
|
||||
|
||||
old_build(self, ...)
|
||||
end
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
> [!NOTE]
|
||||
> This works by overriding your `Tab.build` function so make sure this is the only place you're doing that in your config. For example, this would be incompatible with the [full-border plugin](https://github.com/yazi-rs/plugins/tree/main/full-border.yazi)
|
||||
|
||||
## Thanks
|
||||
|
||||
- [sxyazi](https://github.com/sxyazi) for providing the code for this plugin and the demo video [in this comment](https://github.com/sxyazi/yazi/issues/767#issuecomment-1977082834)
|
|
@ -1,136 +0,0 @@
|
|||
--- @since 25.2.7
|
||||
|
||||
-- For development
|
||||
--[[ local function notify(message) ]]
|
||||
--[[ ya.notify({ title = "Starship", content = message, timeout = 3 }) ]]
|
||||
--[[ end ]]
|
||||
|
||||
local save = ya.sync(function(st, _cwd, output)
|
||||
st.output = output
|
||||
ya.render()
|
||||
end)
|
||||
|
||||
-- Helper function for accessing the `config_file` state variable
|
||||
---@return string
|
||||
local get_config_file = ya.sync(function(st)
|
||||
return st.config_file
|
||||
end)
|
||||
|
||||
return {
|
||||
---User arguments for setup method
|
||||
---@class SetupArgs
|
||||
---@field config_file string Absolute path to a starship config file
|
||||
---@field hide_flags boolean Whether to hide all flags (such as filter and search). Recommended for themes which are intended to take the full width of the terminal.
|
||||
---@field flags_after_prompt boolean Whether to place flags (such as filter and search) after the starship prompt. By default this is true.
|
||||
|
||||
--- Setup plugin
|
||||
--- @param st table State
|
||||
--- @param args SetupArgs|nil
|
||||
setup = function(st, args)
|
||||
local hide_flags = false
|
||||
local flags_after_prompt = true
|
||||
|
||||
-- Check setup args
|
||||
if args ~= nil then
|
||||
if args.config_file ~= nil then
|
||||
local url = Url(args.config_file)
|
||||
if url.is_regular then
|
||||
local config_file = args.config_file
|
||||
|
||||
-- Manually replace '~' and '$HOME' at the start of the path with the OS environment variable
|
||||
local home = os.getenv("HOME")
|
||||
if home then
|
||||
home = tostring(home)
|
||||
config_file = config_file:gsub("^~", home):gsub("^$HOME", home)
|
||||
end
|
||||
|
||||
st.config_file = config_file
|
||||
end
|
||||
end
|
||||
|
||||
if args.hide_flags ~= nil then
|
||||
hide_flags = args.hide_flags
|
||||
end
|
||||
|
||||
if args.flags_after_prompt ~= nil then
|
||||
flags_after_prompt = args.flags_after_prompt
|
||||
end
|
||||
end
|
||||
|
||||
-- Replace default header widget
|
||||
Header:children_remove(1, Header.LEFT)
|
||||
Header:children_add(function(self)
|
||||
local max = self._area.w - self._right_width
|
||||
if max <= 0 then
|
||||
return ""
|
||||
end
|
||||
|
||||
if hide_flags or not st.output then
|
||||
return ui.Line.parse(st.output or "")
|
||||
end
|
||||
|
||||
-- Split `st.output` at the first line break (or keep as is if none was found)
|
||||
local output = st.output:match("([^\n]*)\n?") or st.output
|
||||
|
||||
local flags = self:flags()
|
||||
if flags_after_prompt then
|
||||
output = output .. " " .. flags
|
||||
else
|
||||
output = flags .. " " .. output
|
||||
end
|
||||
|
||||
return ui.Line.parse(output)
|
||||
end, 1000, Header.LEFT)
|
||||
|
||||
-- Pass current working directory and custom config path (if specified) to the plugin's entry point
|
||||
---Callback for subscribers to update the prompt
|
||||
local callback = function()
|
||||
local cwd = cx.active.current.cwd
|
||||
if st.cwd ~= cwd then
|
||||
st.cwd = cwd
|
||||
|
||||
if ya.confirm then
|
||||
-- >= yazi 25.2.7
|
||||
ya.manager_emit("plugin", {
|
||||
st._id,
|
||||
ya.quote(tostring(cwd), true),
|
||||
})
|
||||
else
|
||||
-- < yazi 25.2.7
|
||||
ya.manager_emit("plugin", {
|
||||
st._id,
|
||||
args = ya.quote(tostring(cwd), true),
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Subscribe to events
|
||||
ps.sub("cd", callback)
|
||||
ps.sub("tab", callback)
|
||||
end,
|
||||
|
||||
entry = function(_, job_or_args)
|
||||
-- yazi 2024-11-29 changed the way arguments are passed to the plugin
|
||||
-- entry point. They were moved inside {args = {...}}. If the user is using
|
||||
-- a version before this change, they can use the old implementation.
|
||||
-- https://github.com/sxyazi/yazi/pull/1966
|
||||
local args = job_or_args.args or job_or_args
|
||||
local command = Command("starship")
|
||||
:arg("prompt")
|
||||
:stdin(Command.INHERIT)
|
||||
:cwd(args[1])
|
||||
:env("STARSHIP_SHELL", "")
|
||||
|
||||
-- Point to custom starship config
|
||||
local config_file = get_config_file()
|
||||
if config_file then
|
||||
command = command:env("STARSHIP_CONFIG", config_file)
|
||||
end
|
||||
|
||||
local output = command:output()
|
||||
if output then
|
||||
save(args[1], output.stdout:gsub("^%s+", ""))
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
column_width = 100
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 4
|
||||
quote_style = "AutoPreferDouble"
|
||||
call_parentheses = "Always"
|
||||
collapse_simple_statement = "Never"
|
||||
|
||||
[sort_requires]
|
||||
enabled = false
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 dawsers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,32 +0,0 @@
|
|||
# toggle-view.yazi
|
||||
|
||||
Toggle the different views: parent, current and preview.
|
||||
|
||||
## Requirements
|
||||
|
||||
- [Yazi](https://github.com/sxyazi/yazi/) v0.4 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
ya pack -a dawsers/toggle-view
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `~/.config/yazi/keymap.toml`:
|
||||
|
||||
``` toml
|
||||
[manager]
|
||||
prepend_keymap = [
|
||||
{ on = "<C-1>", run = "plugin toggle-view --args=parent", desc = "Toggle parent" },
|
||||
{ on = "<C-2>", run = "plugin toggle-view --args=current", desc = "Toggle current" },
|
||||
{ on = "<C-3>", run = "plugin toggle-view --args=preview", desc = "Toggle preview" },
|
||||
]
|
||||
```
|
||||
|
||||
Now each key will toggle on/off one of the three panels: `Ctrl+1` for
|
||||
*parent*, `Ctrl+2` for *current* and `Ctrl+3` for *preview*.
|
||||
|
||||
You can set your own key bindings.
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
--- @sync entry
|
||||
-- Toggle different views on/off: parent, current, preview
|
||||
local function entry(st, job)
|
||||
local args = job.args or job
|
||||
local action = args[1]
|
||||
if not action then
|
||||
return
|
||||
end
|
||||
|
||||
if st.view == nil then
|
||||
st.old_parent = MANAGER.ratio.parent
|
||||
st.old_current = MANAGER.ratio.current
|
||||
st.old_preview = MANAGER.ratio.preview
|
||||
|
||||
-- Get current tab ratios
|
||||
local all_old = st.old_parent + st.old_current + st.old_preview
|
||||
local area = ui.Rect { x= 0, y = 0, w = all_old, h = 10 }
|
||||
local tab = Tab:new(area, cx.active)
|
||||
st.parent = tab._chunks[1].w
|
||||
st.current = tab._chunks[2].w
|
||||
st.preview = tab._chunks[3].w
|
||||
st.layout = Tab.layout
|
||||
st.view = true -- initialized
|
||||
end
|
||||
|
||||
if action == "parent" then
|
||||
if st.parent > 0 then
|
||||
st.parent = 0
|
||||
else
|
||||
st.parent = st.old_parent
|
||||
end
|
||||
elseif action == "current" then
|
||||
if st.current > 0 then
|
||||
st.current = 0
|
||||
else
|
||||
st.current = st.old_current
|
||||
end
|
||||
elseif action == "preview" then
|
||||
if st.preview > 0 then
|
||||
st.preview = 0
|
||||
else
|
||||
st.preview = st.old_preview
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
Tab.layout = function(self)
|
||||
local all = st.parent + st.current + st.preview
|
||||
self._chunks = ui.Layout()
|
||||
:direction(ui.Layout.HORIZONTAL)
|
||||
:constraints({
|
||||
ui.Constraint.Ratio(st.parent, all),
|
||||
ui.Constraint.Ratio(st.current, all),
|
||||
ui.Constraint.Ratio(st.preview, all),
|
||||
})
|
||||
:split(self._area)
|
||||
end
|
||||
ya.app_emit("resize", {})
|
||||
end
|
||||
|
||||
local function enabled(st) return st.view ~= nil end
|
||||
|
||||
return { entry = entry, enabled = enabled }
|
|
@ -1,72 +0,0 @@
|
|||
|
||||
-- function to get paths of selected elements or current directory
|
||||
-- of no elements are selected
|
||||
local get_paths = ya.sync(function()
|
||||
local paths = {}
|
||||
-- get selected files
|
||||
for _, u in pairs(cx.active.selected) do
|
||||
paths[#paths + 1] = tostring(u)
|
||||
end
|
||||
-- if no files are selected, get current directory
|
||||
if #paths == 0 then
|
||||
if cx.active.current.cwd then
|
||||
paths[1] = tostring(cx.active.current.cwd)
|
||||
else
|
||||
ya.err("what-size would return nil paths")
|
||||
end
|
||||
end
|
||||
return paths
|
||||
end)
|
||||
|
||||
-- Function to get total size from du output
|
||||
local get_total_size = function(s)
|
||||
local lines = {}
|
||||
for line in s:gmatch("[^\n]+") do lines[#lines + 1] = line end
|
||||
local last_line = lines[#lines]
|
||||
local last_line_parts = {}
|
||||
for part in last_line:gmatch("%S+") do last_line_parts[#last_line_parts + 1] = part end
|
||||
local total_size = last_line_parts[1]
|
||||
return total_size
|
||||
end
|
||||
|
||||
-- Function to format file size
|
||||
local function format_size(size)
|
||||
local units = { "B", "KB", "MB", "GB", "TB" }
|
||||
local unit_index = 1
|
||||
|
||||
while size > 1024 and unit_index < #units do
|
||||
size = size / 1024
|
||||
unit_index = unit_index + 1
|
||||
end
|
||||
|
||||
return string.format("%.2f %s", size, units[unit_index])
|
||||
end
|
||||
|
||||
return {
|
||||
entry = function(self, job)
|
||||
-- defaults not to use clipboard, use it only if required by the user
|
||||
local clipboard = job.args.clipboard or job.args[1] == '-c'
|
||||
local items = get_paths()
|
||||
|
||||
local cmd = "du"
|
||||
local output, err = Command(cmd):arg("-scb"):args(items):output()
|
||||
if not output then
|
||||
ya.err("Failed to run diff, error: " .. err)
|
||||
else
|
||||
local total_size = get_total_size(output.stdout)
|
||||
local formatted_size = format_size(tonumber(total_size))
|
||||
|
||||
local notification_content = "Total size: " .. formatted_size
|
||||
if clipboard then
|
||||
ya.clipboard(formatted_size)
|
||||
notification_content = notification_content .. "\nCopied to clipboard."
|
||||
end
|
||||
|
||||
ya.notify {
|
||||
title = "What size",
|
||||
content = notification_content,
|
||||
timeout = 5,
|
||||
}
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 imsi32
|
||||
Copyright (c) 2024 llanosrocas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,39 +0,0 @@
|
|||
# yatline-symlink.yazi
|
||||
|
||||
An addon to show symlink target in your [yatline.yazi](https://github.com/imsi32/yatline.yazi)'s status or header line.
|
||||
|
||||

|
||||
|
||||
## Requirements
|
||||
|
||||
- yazi version >= 0.3.0
|
||||
- [yatline.yazi](https://github.com/imsi32/yatline.yazi)
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
ya pack -a lpanebr/yazi-plugins:yatline-symlink
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Add this to your `~/.config/yazi/init.lua` after yatline.yazi's initialization.
|
||||
|
||||
```lua
|
||||
require("yatline-symlink"):setup()
|
||||
```
|
||||
|
||||
Then, add it in one of your sections in the yatline configuration using:
|
||||
|
||||
```lua
|
||||
{ type = "coloreds", custom = false, name = "symlink" }
|
||||
```
|
||||
|
||||
**Optional configuration:**
|
||||
|
||||
```lua
|
||||
require("githead"):setup({
|
||||
symlink_color = "white"
|
||||
}
|
||||
```
|
|
@ -1,33 +0,0 @@
|
|||
function hovered()
|
||||
local hovered = cx.active.current.hovered
|
||||
if hovered then
|
||||
return hovered
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
local function setup(_, options)
|
||||
options = options or {}
|
||||
|
||||
local config = {
|
||||
symlink_color = options.symlink_color or "silver",
|
||||
}
|
||||
|
||||
if Yatline ~= nil then
|
||||
function Yatline.coloreds.get:symlink()
|
||||
local symlink = {}
|
||||
local linked = ""
|
||||
|
||||
local h = hovered()
|
||||
if h.link_to ~= nil then
|
||||
linked = " -> " .. tostring(h.link_to)
|
||||
end
|
||||
|
||||
table.insert(symlink, { linked, config.symlink_color })
|
||||
return symlink
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return { setup = setup }
|
|
@ -1,23 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 imsi32
|
||||
Copyright (c) 2023 - sxyazi
|
||||
Copyright (c) 2023 yazi-rs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,19 +0,0 @@
|
|||
# yatline.yazi
|
||||
The first Yazi plugin for customizing both header-line and status-line.
|
||||
|
||||

|
||||
|
||||
> [!NOTE]
|
||||
> Check out [wiki](https://github.com/imsi32/yatline.yazi/wiki) for installation steps, configuration and further information.
|
||||
|
||||
## Features
|
||||
- Lualine-like Design
|
||||
- Flexible
|
||||
- Simple
|
||||
- Automatic Configuration
|
||||
- Themes (See: [yatline-themes](https://github.com/imsi32/yatline-themes))
|
||||
- Add-ons (See: [yatline-addons](https://github.com/imsi32/yatline-addons))
|
||||
|
||||
## Credits
|
||||
- [Lualine](https://github.com/nvim-lualine/lualine.nvim)
|
||||
- [Yazi](https://github.com/sxyazi/yazi)
|
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
|||
Subproject commit 9a095057d698aaaedc4dd23d638285bd3fd647e9
|
Loading…
Add table
Add a link
Reference in a new issue