diff options
Diffstat (limited to 'fedora/.config/yazi/plugins')
41 files changed, 4071 insertions, 0 deletions
diff --git a/fedora/.config/yazi/plugins/chmod.yazi/README.md b/fedora/.config/yazi/plugins/chmod.yazi/README.md new file mode 100644 index 0000000..b2ad136 --- /dev/null +++ b/fedora/.config/yazi/plugins/chmod.yazi/README.md @@ -0,0 +1,28 @@ +# chmod.yazi + +Execute `chmod` on the selected files to change their mode. This plugin is only available on Unix platforms since it relies on [`chmod(2)`](https://man7.org/linux/man-pages/man2/chmod.2.html). + +https://github.com/yazi-rs/plugins/assets/17523360/7aa3abc2-d057-498c-8473-a6282c59c464 + +## Installation + +```sh +ya pkg add yazi-rs/plugins:chmod +``` + +## Usage + +Add this to your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = [ "c", "m" ] +run = "plugin chmod" +desc = "Chmod on selected files" +``` + +Make sure the <kbd>c</kbd> => <kbd>m</kbd> key is not used elsewhere. + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/chmod.yazi/main.lua b/fedora/.config/yazi/plugins/chmod.yazi/main.lua new file mode 100644 index 0000000..a50a864 --- /dev/null +++ b/fedora/.config/yazi/plugins/chmod.yazi/main.lua @@ -0,0 +1,42 @@ +--- @since 25.5.31 + +local selected_or_hovered = ya.sync(function() + local tab, paths = cx.active, {} + for _, u in pairs(tab.selected) do + paths[#paths + 1] = tostring(u) + end + if #paths == 0 and tab.current.hovered then + paths[1] = tostring(tab.current.hovered.url) + end + return paths +end) + +return { + entry = function() + ya.emit("escape", { visual = true }) + + local urls = selected_or_hovered() + if #urls == 0 then + return ya.notify { title = "Chmod", content = "No file selected", level = "warn", timeout = 5 } + end + + local value, event = ya.input { + title = "Chmod:", + pos = { "top-center", y = 3, w = 40 }, + position = { "top-center", y = 3, w = 40 }, -- TODO: remove + } + if event ~= 1 then + return + end + + local status, err = Command("chmod"):arg(value):arg(urls):spawn():wait() + if not status or not status.success then + ya.notify { + title = "Chmod", + content = string.format("Chmod on selected files failed, error: %s", status and status.code or err), + level = "error", + timeout = 5, + } + end + end, +} diff --git a/fedora/.config/yazi/plugins/compress.yazi/LICENSE b/fedora/.config/yazi/plugins/compress.yazi/LICENSE new file mode 100644 index 0000000..7ce7a2f --- /dev/null +++ b/fedora/.config/yazi/plugins/compress.yazi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 CiarΓ‘n O'Brien + +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. diff --git a/fedora/.config/yazi/plugins/compress.yazi/README.md b/fedora/.config/yazi/plugins/compress.yazi/README.md new file mode 100644 index 0000000..ae1f329 --- /dev/null +++ b/fedora/.config/yazi/plugins/compress.yazi/README.md @@ -0,0 +1,173 @@ +<h1 align="center">ποΈ compress.yazi</h1> +<p align="center"> + <b>A blazing fast, flexible archive plugin for <a href="https://github.com/sxyazi/yazi">Yazi</a></b><br> + <i>Effortlessly compress your files and folders with style!</i> +</p> + +--- + +## π Table of Contents + +- [Features](#-features) +- [Supported File Types](#-supported-file-types) +- [Installation](#%EF%B8%8F-installation) +- [Keymap Example](#-keymap-example) +- [Usage](#%EF%B8%8F-usage) +- [Flags](#%EF%B8%8F-flags) +- [Tips](#-tips) +- [Credits](#-credits) + +--- + +## π Features + +- ποΈ **Multi-format support:** zip, 7z, rar, tar, tar.gz, tar.xz, tar.bz2, tar.zst, tar.lz4, tar.lha +- π **Cross-platform:** Works on Unix & Windows +- π **Password protection:** Secure your archives (zip/7z/rar) +- π‘οΈ **Header encryption:** Hide file lists (7z/rar) +- β‘ **Compression level:** Choose your balance of speed vs. size +- π **Overwrite safety:** Never lose files by accident +- π― **Seamless Yazi integration:** Fast, native-like UX + +--- + +## π¦ Supported File Types + +| Extension | Default Command | 7z Command | Bsdtar Command (Win10+ & Unix) | +| ------------- | ----------------- | -------------- | ------------------------------ | +| `.zip` | `zip -r` | `7z a -tzip` | `tar -caf` | +| `.7z` | `7z a` | `7z a` | | +| `.rar` | `rar a` | | | +| `.tar` | `tar rpf` | | `tar rpf` | +| `.tar.gz` | `tar rpf + gzip` | `7z a -tgzip` | `tar -czf` | +| `.tar.xz` | `tar rpf + xz` | `7z a -txz` | `tar -cJf` | +| `.tar.bz2` | `tar rpf + bzip2` | `7z a -tbzip2` | `tar -cjf` | +| `.tar.zst` | `tar rpf + zstd` | | `tar --zstd -cf` | +| `.tar.lz4` | `tar rpf + lz4` | | | +| `.tar.lha` | `tar rpf + lha` | | | + +--- + +## β‘οΈ Installation + +```bash +# Unix +git clone https://github.com/KKV9/compress.yazi.git ~/.config/yazi/plugins/compress.yazi + +# Windows (CMD, not PowerShell!) +git clone https://github.com/KKV9/compress.yazi.git %AppData%\yazi\config\plugins\compress.yazi + +# Or with yazi plugin manager +ya pkg add KKV9/compress +``` + +--- + +### π§ Extras (Windows) + +To enable additional compression formats and features on Windows, follow these steps: + +1. **Install [7-Zip](https://www.7-zip.org/):** + Add `C:\Program Files\7-Zip` to your `PATH`. + This enables support for `.7z` archives and password-protected `.zip` files. + +2. **Alternative: Install [Nanazip](https://github.com/M2Team/NanaZip):** + A modern alternative to 7-Zip with similar functionality and extra features. + +3. **Install [WinRAR](https://www.win-rar.com/download.html):** + Add `C:\Program Files\WinRAR` to your `PATH`. + This enables support for `.rar` archives. + +4. **Install Additional Tools:** + To use formats like `lha`, `lz4`, `gzip`, etc., install their respective tools and ensure they are added to your `PATH`. + +--- + +## πΉ Keymap Example + +Add this to your `keymap.toml`: + + +```toml +[[mgr.prepend_keymap]] +on = [ "c", "a", "a" ] +run = "plugin compress" +desc = "Archive selected files" + +[[mgr.prepend_keymap]] +on = [ "c", "a", "p" ] +run = "plugin compress -p" +desc = "Archive selected files (password)" + +[[mgr.prepend_keymap]] +on = [ "c", "a", "h" ] +run = "plugin compress -ph" +desc = "Archive selected files (password+header)" + +[[mgr.prepend_keymap]] +on = [ "c", "a", "l" ] +run = "plugin compress -l" +desc = "Archive selected files (compression level)" + +[[mgr.prepend_keymap]] +on = [ "c", "a", "u" ] +run = "plugin compress -phl" +desc = "Archive selected files (password+header+level)" +``` + +--- + +## π οΈ Usage + +1. **Select files/folders** in Yazi. +2. Press <kbd>c</kbd> <kbd>a</kbd> to open the archive dialog. +3. Choose: + - <kbd>a</kbd> for a standard archive + - <kbd>p</kbd> for password protection (zip/7z/rar) + - <kbd>h</kbd> to encrypt header (7z/rar) + - <kbd>l</kbd> to set compression level (all compression algorithims) + - <kbd>u</kbd> for all options together +4. **Type a name** for your archive (or leave blank for suggested name). +5. **Enter password** and/or **compression level** if prompted. +6. **Overwrite protect** if a file already exists, the new file will be given a suffix _#. +7. Enjoy your shiny new archive! + +--- + +## π³οΈβπ Flags + +- Combine flags for more power! +- when separating flags with spaces, make sure to single quote them (eg., `'-ph rar'`) +- `-p` Password protect (zip/7z/rar) +- `-h` Encrypt header (7z/rar) +- `-l` Set compression level (all compression algorithims) +- `<extention>` Specify a default extention (eg., `7z`, `tar.gz`) + +#### Combining multiple flags: +```toml +[[mgr.prepend_keymap]] +on = [ "c", "a", "7" ] +run = "plugin compress '-ph 7z'" +desc = "Archive selected files to 7z (password+header)" +[[mgr.prepend_keymap]] +on = [ "c", "a", "r" ] +run = "plugin compress '-p -l rar'" +desc = "Archive selected files to rar (password+level)" +``` + +--- + +## π‘ Tips + +- The file extension **must** match a supported type. +- The required compression tool **must** be installed and in your `PATH` (7zip/rar etc.). +- If no extention is provided, the default extention (zip) will be appended automatically. + +--- + +## π£ Credits + +Made with β€οΈ for [Yazi](https://github.com/sxyazi/yazi) by [KKV9](https://github.com/KKV9). +Contributions are welcome! Feel free to submit a pull request. + +--- diff --git a/fedora/.config/yazi/plugins/compress.yazi/main.lua b/fedora/.config/yazi/plugins/compress.yazi/main.lua new file mode 100644 index 0000000..b695b3f --- /dev/null +++ b/fedora/.config/yazi/plugins/compress.yazi/main.lua @@ -0,0 +1,496 @@ +-- Check for windows +local is_windows = ya.target_family() == "windows" +-- Define flags and strings +local is_password, is_encrypted, is_level, cmd_password, cmd_level, default_extension = false, false, false, "", "", "zip" + +-- Function to check valid filename +local function is_valid_filename(name) + -- Trim whitespace from both ends + name = name:match("^%s*(.-)%s*$") + if name == "" then + return false + end + if is_windows then + -- Windows forbidden chars and reserved names + if name:find('[<>:"/\\|%?%*]') then + return false + end + else + -- Unix forbidden chars + if name:find("/") or name:find("%z") then + return false + end + end + return true +end + +-- Function to send notifications +local function notify_error(message, urgency) + ya.notify( + { + title = "Archive", + content = message, + level = urgency, + timeout = 5 + } + ) +end + +-- Function to check if command is available +local function is_command_available(cmd) + local stat_cmd + if is_windows then + stat_cmd = string.format("where %s > nul 2>&1", cmd) + else + stat_cmd = string.format("command -v %s >/dev/null 2>&1", cmd) + end + local cmd_exists = os.execute(stat_cmd) + if cmd_exists then + return true + else + return false + end +end + +-- Function to change command arrays --> string -- Use first command available or first command +local function find_command_name(cmd_list) + for _, cmd in ipairs(cmd_list) do + if is_command_available(cmd) then + return cmd + end + end + return cmd_list[1] -- Return first command as fallback +end + +-- Function to append filename to it's parent directory url +local function combine_url(path, file) + path, file = Url(path), Url(file) + return tostring(path:join(file)) +end + +-- Function to make a table of selected or hovered files: path = filenames +local selected_or_hovered = + ya.sync( + function() + local tab, paths, names, path_fnames = cx.active, {}, {}, {} + for _, u in pairs(tab.selected) do + paths[#paths + 1] = tostring(u.parent) + names[#names + 1] = tostring(u.name) + end + if #paths == 0 and tab.current.hovered then + paths[1] = tostring(tab.current.hovered.url.parent) + names[1] = tostring(tab.current.hovered.name) + end + for idx, name in ipairs(names) do + if not path_fnames[paths[idx]] then + path_fnames[paths[idx]] = {} + end + table.insert(path_fnames[paths[idx]], name) + end + return path_fnames, names, tostring(tab.current.cwd) + end +) + +-- Table of archive commands +local archive_commands = { + ["%.zip$"] = { + {command = "zip", args = {"-r"}, level_arg = "-", level_min = 0, level_max = 9, passwordable = true}, + { + command = {"7z", "7zz", "7za"}, + args = {"a", "-tzip"}, + level_arg = "-mx=", + level_min = 0, + level_max = 9, + passwordable = true + }, + { + command = {"tar", "bsdtar"}, + args = {"-caf"}, + level_arg = {"--option", "compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.7z$"] = { + { + command = {"7z", "7zz", "7za"}, + args = {"a"}, + level_arg = "-mx=", + level_min = 0, + level_max = 9, + header_arg = "-mhe=on", + passwordable = true + } + }, + ["%.rar$"] = { + { + command = "rar", + args = {"a"}, + level_arg = "-m", + level_min = 0, + level_max = 5, + header_arg = "-hp", + passwordable = true + } + }, + ["%.tar.gz$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "gzip"}, + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-mx=", + level_min = 1, + level_max = 9, + compress = "7z", + compress_args = {"a", "-tgzip"} + }, + { + command = {"tar", "bsdtar"}, + args = {"-czf"}, + level_arg = {"--option", "gzip:compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.tar.xz$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "xz"}, + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-mx=", + level_min = 1, + level_max = 9, + compress = "7z", + compress_args = {"a", "-txz"} + }, + { + command = {"tar", "bsdtar"}, + args = {"-cJf"}, + level_arg = {"--option", "xz:compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.tar.bz2$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "bzip2"}, + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-mx=", + level_min = 1, + level_max = 9, + compress = "7z", + compress_args = {"a", "-tbzip2"} + }, + { + command = {"tar", "bsdtar"}, + args = {"-cjf"}, + level_arg = {"--option", "bzip2:compression-level="}, + level_min = 1, + level_max = 9 + } + }, + ["%.tar.zst$"] = { + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-", + level_min = 1, + level_max = 22, + compress = "zstd", + compress_args = {"--ultra"} + } + }, + ["%.tar.lz4$"] = { + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-", + level_min = 1, + level_max = 12, + compress = "lz4" + } + }, + ["%.tar.lha$"] = { + { + command = {"tar", "bsdtar"}, + args = {"rpf"}, + level_arg = "-o", + level_min = 5, + level_max = 7, + compress = "lha", + compress_args = {"-a"} + } + }, + ["%.tar$"] = { + {command = {"tar", "bsdtar"}, args = {"rpf"}} + } +} + +return { + entry = function(_, job) + -- Parse flags and default extension + if job.args ~= nil then + for _, arg in ipairs(job.args) do + if arg:match("^%-(%w+)$") then + -- Handle combined flags (e.g., -phl) + for flag in arg:sub(2):gmatch(".") do + if flag == "p" then + is_password = true + elseif flag == "h" then + is_encrypted = true + elseif flag == "l" then + is_level = true + end + end + elseif arg:match("^%w[%w\\.]*$") then + -- Handle default extension (e.g., 7z, zip) + if archive_commands["%." .. arg .. "$"] then + default_extension = arg + else + notify_error(string.format("Unsupported extension: %s", arg), "warn") + end + else + notify_error(string.format("Unknown argument: %s", arg), "warn") + end + end + end + + -- Exit visual mode + ya.emit("escape", {visual = true}) + -- Define file table and output_dir (pwd) + local path_fnames, fnames, output_dir = selected_or_hovered() + -- Get archive filename + local output_name, event = + ya.input( + { + title = "Create archive:", + position = {"top-center", y = 3, w = 40} + } + ) + if event ~= 1 then + return + end + + -- Determine the default name for the archive + local default_name = #fnames == 1 and fnames[1] or Url(output_dir).name + output_name = output_name == "" and string.format("%s.%s", default_name, default_extension) or output_name + + -- Add default extension if none is specified + if not output_name:match("%.%w+$") then + output_name = string.format("%s.%s", output_name, default_extension) + end + + -- Validate the final archive filename + if not is_valid_filename(output_name) then + notify_error("Invalid archive filename", "error") + return + end + + -- Match user input to archive command + local archive_cmd, + archive_args, + archive_compress, + archive_level_arg, + archive_level_min, + archive_level_max, + archive_header_arg, + archive_passwordable, + archive_compress_args + local matched_pattern = false + for pattern, cmd_list in pairs(archive_commands) do + if output_name:match(pattern) then + matched_pattern = true -- Mark that file extension is correct + for _, cmd in ipairs(cmd_list) do + -- Check if archive_cmd is available + local find_command = type(cmd.command) == "table" and find_command_name(cmd.command) or cmd.command + if is_command_available(find_command) then + -- Check if compress_cmd (if listed) is available + if cmd.compress == nil or is_command_available(cmd.compress) then + archive_cmd = find_command + archive_args = cmd.args + archive_compress = cmd.compress or "" + archive_level_arg = is_level and cmd.level_arg or "" + archive_level_min = cmd.level_min + archive_level_max = cmd.level_max + archive_header_arg = is_encrypted and cmd.header_arg or "" + archive_passwordable = cmd.passwordable or false + archive_compress_args = cmd.compress_args or {} + break + end + end + end + if archive_cmd then + break + end + end + end + + -- Check if no archive command is available for the extension + if not matched_pattern then + notify_error("Unsupported file extension", "error") + return + end + + -- Check if no suitable archive program was found + if not archive_cmd then + notify_error("Could not find a suitable archive program for the selected file extension", "error") + return + end + + -- Check if archive command has multiple names + if type(archive_cmd) == "table" then + archive_cmd = find_command_name(archive_cmd) + end + + -- Exit if archive command is not available + if not is_command_available(archive_cmd) then + notify_error(string.format("%s not available", archive_cmd), "error") + return + end + + -- Exit if compress command is not available + if archive_compress ~= "" and not is_command_available(archive_compress) then + notify_error(string.format("%s compression not available", archive_compress), "error") + return + end + + -- Add password arg if selected + if archive_passwordable and is_password then + local output_password, event = + ya.input( + { + title = "Enter password:", + obscure = true, + position = {"top-center", y = 3, w = 40} + } + ) + if event ~= 1 then + return + end + if output_password ~= "" then + cmd_password = "-P" .. output_password + if archive_cmd == "rar" and is_encrypted then + cmd_password = archive_header_arg .. output_password -- Add archive arg for rar + end + table.insert(archive_args, cmd_password) + end + end + + -- Add header arg if selected for 7z + if is_encrypted and archive_header_arg ~= "" and archive_cmd ~= "rar" then + table.insert(archive_args, archive_header_arg) + end + + -- Add level arg if selected + if archive_level_arg ~= "" and is_level then + local output_level, event = + ya.input( + { + title = string.format("Enter compression level (%s - %s)", archive_level_min, archive_level_max), + position = {"top-center", y = 3, w = 40} + } + ) + if event ~= 1 then + return + end + -- Validate user input for compression level + if + output_level ~= "" and tonumber(output_level) ~= nil and tonumber(output_level) >= archive_level_min and + tonumber(output_level) <= archive_level_max + then + cmd_level = + type(archive_level_arg) == "table" and archive_level_arg[#archive_level_arg] .. output_level or + archive_level_arg .. output_level + local target_args = archive_compress == "" and archive_args or archive_compress_args + if type(archive_level_arg) == "table" then + -- Insert each element of archive_level_arg (except last) into target_args at the correct position + for i = 1, #archive_level_arg - 1 do + table.insert(target_args, i, archive_level_arg[i]) + end + table.insert(target_args, #archive_level_arg, cmd_level) -- Add level at the end + else + -- Insert the compression level argument at the start if not a table + table.insert(target_args, 1, cmd_level) + end + else + notify_error("Invalid level specified. Using defaults.", "warn") + end + end + + -- Store the original output name for later use + local original_name = output_name + + -- If compression is needed, adjust the output name to exclude extensions like ".tar" + if archive_compress ~= "" then + output_name = output_name:match("(.*%.tar)") or output_name + end + + -- Create a temporary directory for intermediate files + local temp_dir_name = ".tmp_compress" + local temp_dir = combine_url(output_dir, temp_dir_name) + local temp_dir, _ = tostring(fs.unique_name(Url(temp_dir))) + + -- Attempt to create the temporary directory + local temp_dir_status, temp_dir_err = fs.create("dir_all", Url(temp_dir)) + if not temp_dir_status then + -- Notify the user if the temporary directory creation fails + notify_error(string.format("Failed to create temp directory, error code: %s", temp_dir_err), "error") + return + end + + -- Define the temporary output file path within the temporary directory + local temp_output_url = combine_url(temp_dir, output_name) + + -- Add files to the output archive + for filepath, filenames in pairs(path_fnames) do + -- Execute the archive command for each path and its respective files + local archive_status, archive_err = + Command(archive_cmd):arg(archive_args):arg(temp_output_url):arg(filenames):cwd(filepath):spawn():wait() + if not archive_status or not archive_status.success then + -- Notify the user if the archiving process fails and clean up the temporary directory + notify_error(string.format("Failed to create archive %s with '%s', error: %s", output_name, archive_cmd, archive_err), "error") + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + return + end + end + + -- If compression is required, execute the compression command + if archive_compress ~= "" then + local compress_status, compress_err = + Command(archive_compress):arg(archive_compress_args):arg(temp_output_url):spawn():wait() + if not compress_status or not compress_status.success then + -- Notify the user if the compression process fails and clean up the temporary directory + notify_error(string.format("Failed to compress archive %s with '%s', error: %s", output_name, archive_compress, compress_err), "error") + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + return + end + end + + -- Move the final file from the temporary directory to the output directory + local final_output_url, temp_url_processed = combine_url(output_dir, original_name), combine_url(temp_dir, original_name) + final_output_url, _ = tostring(fs.unique_name(Url(final_output_url))) + local move_status, move_err = os.rename(temp_url_processed, final_output_url) + if not move_status then + -- Notify the user if the move operation fails and clean up the temporary directory + notify_error(string.format("Failed to move %s to %s, error: %s", temp_url_processed, final_output_url, move_err), "error") + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + return + end + + -- Cleanup the temporary directory after successful operation + local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir)) + if not cleanup_status then + notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error") + end + end +} diff --git a/fedora/.config/yazi/plugins/diff.yazi/README.md b/fedora/.config/yazi/plugins/diff.yazi/README.md new file mode 100644 index 0000000..1976541 --- /dev/null +++ b/fedora/.config/yazi/plugins/diff.yazi/README.md @@ -0,0 +1,28 @@ +# diff.yazi + +Diff the selected file with the hovered file, create a living patch, and copy it to the clipboard. + +https://github.com/yazi-rs/plugins/assets/17523360/eff5e949-386a-44ea-82f9-4cb4a2c37aad + +## Installation + +```sh +ya pkg add yazi-rs/plugins:diff +``` + +## Usage + +Add this to your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = "<C-d>" +run = "plugin diff" +desc = "Diff the selected with the hovered file" +``` + +Make sure the <kbd>C</kbd> + <kbd>d</kbd> key is not used elsewhere. + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/diff.yazi/main.lua b/fedora/.config/yazi/plugins/diff.yazi/main.lua new file mode 100644 index 0000000..21dde6d --- /dev/null +++ b/fedora/.config/yazi/plugins/diff.yazi/main.lua @@ -0,0 +1,41 @@ +--- @since 25.2.7 + +local function info(content) + return ya.notify { + title = "Diff", + content = content, + timeout = 5, + } +end + +local selected_url = ya.sync(function() + for _, u in pairs(cx.active.selected) do + return u + end +end) + +local hovered_url = ya.sync(function() + local h = cx.active.current.hovered + return h and h.url +end) + +return { + entry = function() + local a, b = selected_url(), hovered_url() + if not a then + return info("No file selected") + elseif not b then + return info("No file hovered") + end + + local output, err = Command("diff"):arg("-Naur"):arg(tostring(a)):arg(tostring(b)):output() + if not output then + return info("Failed to run diff, error: " .. err) + elseif output.stdout == "" then + return info("No differences found") + end + + ya.clipboard(output.stdout) + info("Diff copied to clipboard") + end, +} diff --git a/fedora/.config/yazi/plugins/folder-rules.yazi/main.lua b/fedora/.config/yazi/plugins/folder-rules.yazi/main.lua new file mode 100644 index 0000000..556f610 --- /dev/null +++ b/fedora/.config/yazi/plugins/folder-rules.yazi/main.lua @@ -0,0 +1,12 @@ +local function setup() + ps.sub("cd", function() + local cwd = cx.active.current.cwd + if cwd:ends_with("Downloads") then + ya.emit("sort", { "mtime", reverse = true, dir_first = false }) + else + ya.emit("sort", { "alphabetical", reverse = false, dir_first = true }) + end + end) +end + +return { setup = setup } diff --git a/fedora/.config/yazi/plugins/full-border.yazi/README.md b/fedora/.config/yazi/plugins/full-border.yazi/README.md new file mode 100644 index 0000000..269ca8e --- /dev/null +++ b/fedora/.config/yazi/plugins/full-border.yazi/README.md @@ -0,0 +1,32 @@ +# full-border.yazi + +Add a full border to Yazi to make it look fancier. + + + +## Installation + +```sh +ya pkg add yazi-rs/plugins:full-border +``` + +## Usage + +Add this to your `init.lua` to enable the plugin: + +```lua +require("full-border"):setup() +``` + +Or you can customize the border type: + +```lua +require("full-border"):setup { + -- Available values: ui.Border.PLAIN, ui.Border.ROUNDED + type = ui.Border.ROUNDED, +} +``` + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/full-border.yazi/main.lua b/fedora/.config/yazi/plugins/full-border.yazi/main.lua new file mode 100644 index 0000000..a917e1e --- /dev/null +++ b/fedora/.config/yazi/plugins/full-border.yazi/main.lua @@ -0,0 +1,43 @@ +--- @since 25.2.26 + +local function setup(_, opts) + local type = opts and opts.type or ui.Border.ROUNDED + local old_build = Tab.build + + Tab.build = function(self, ...) + local bar = function(c, x, y) + if x <= 0 or x == self._area.w - 1 or th.mgr.border_symbol ~= "β" then + return ui.Bar(ui.Edge.TOP) + end + + return ui.Bar(ui.Edge.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]:pad(ui.Pad.y(1)), + c[2]:pad(ui.Pad(1, c[3].w > 0 and 0 or 1, 1, c[1].w > 0 and 0 or 1)), + c[3]:pad(ui.Pad.y(1)), + } + + local style = th.mgr.border_style + self._base = ya.list_merge(self._base or {}, { + ui.Border(ui.Edge.ALL):area(self._area):type(type):style(style), + ui.Bar(ui.Edge.RIGHT):area(self._chunks[1]):style(style), + ui.Bar(ui.Edge.LEFT):area(self._chunks[3]):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 +end + +return { setup = setup } diff --git a/fedora/.config/yazi/plugins/git.yazi/README.md b/fedora/.config/yazi/plugins/git.yazi/README.md new file mode 100644 index 0000000..96a87a8 --- /dev/null +++ b/fedora/.config/yazi/plugins/git.yazi/README.md @@ -0,0 +1,78 @@ +# git.yazi + +Show the status of Git file changes as linemode in the file list. + +https://github.com/user-attachments/assets/34976be9-a871-4ffe-9d5a-c4cdd0bf4576 + +## Installation + +```sh +ya pkg add yazi-rs/plugins:git +``` + +## Setup + +Add the following to your `~/.config/yazi/init.lua`: + +```lua +require("git"):setup() +``` + +And register it as fetchers in your `~/.config/yazi/yazi.toml`: + +```toml +[[plugin.prepend_fetchers]] +id = "git" +name = "*" +run = "git" + +[[plugin.prepend_fetchers]] +id = "git" +name = "*/" +run = "git" +``` + +## Advanced + +> [!NOTE] +> The following configuration must be put before `require("git"):setup()` + +You can customize the [Style](https://yazi-rs.github.io/docs/plugins/layout#style) of the status sign with: + +- `th.git.modified` +- `th.git.added` +- `th.git.untracked` +- `th.git.ignored` +- `th.git.deleted` +- `th.git.updated` + +For example: + +```lua +-- ~/.config/yazi/init.lua +th.git = th.git or {} +th.git.modified = ui.Style():fg("blue") +th.git.deleted = ui.Style():fg("red"):bold() +``` + +You can also customize the text of the status sign with: + +- `th.git.modified_sign` +- `th.git.added_sign` +- `th.git.untracked_sign` +- `th.git.ignored_sign` +- `th.git.deleted_sign` +- `th.git.updated_sign` + +For example: + +```lua +-- ~/.config/yazi/init.lua +th.git = th.git or {} +th.git.modified_sign = "M" +th.git.deleted_sign = "D" +``` + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/git.yazi/main.lua b/fedora/.config/yazi/plugins/git.yazi/main.lua new file mode 100644 index 0000000..e6b3a36 --- /dev/null +++ b/fedora/.config/yazi/plugins/git.yazi/main.lua @@ -0,0 +1,261 @@ +--- @since 25.5.31 + +local WINDOWS = ya.target_family() == "windows" + +-- The code of supported git status, +-- also used to determine which status to show for directories when they contain different statuses +-- see `bubble_up` +---@enum CODES +local CODES = { + excluded = 100, -- ignored directory + ignored = 6, -- ignored file + untracked = 5, + modified = 4, + added = 3, + deleted = 2, + updated = 1, + unknown = 0, +} + +local PATTERNS = { + { "!$", CODES.ignored }, + { "?$", CODES.untracked }, + { "[MT]", CODES.modified }, + { "[AC]", CODES.added }, + { "D", CODES.deleted }, + { "U", CODES.updated }, + { "[AD][AD]", CODES.updated }, +} + +---@param line string +---@return CODES, string +local function match(line) + local signs = line:sub(1, 2) + for _, p in ipairs(PATTERNS) do + local path, pattern, code = nil, p[1], p[2] + if signs:find(pattern) then + path = line:sub(4, 4) == '"' and line:sub(5, -2) or line:sub(4) + path = WINDOWS and path:gsub("/", "\\") or path + end + if not path then + elseif path:find("[/\\]$") then + -- Mark the ignored directory as `excluded`, so we can process it further within `propagate_down` + return code == CODES.ignored and CODES.excluded or code, path:sub(1, -2) + else + return code, path + end + ---@diagnostic disable-next-line: missing-return + end +end + +---@param cwd Url +---@return string? +local function root(cwd) + local is_worktree = function(url) + local file, head = io.open(tostring(url)), nil + if file then + head = file:read(8) + file:close() + end + return head == "gitdir: " + end + + repeat + local next = cwd:join(".git") + local cha = fs.cha(next) + if cha and (cha.is_dir or is_worktree(next)) then + return tostring(cwd) + end + cwd = cwd.parent + until not cwd +end + +---@param changed Changes +---@return Changes +local function bubble_up(changed) + local new, empty = {}, Url("") + for path, code in pairs(changed) do + if code ~= CODES.ignored then + local url = Url(path).parent + while url and url ~= empty do + local s = tostring(url) + new[s] = (new[s] or CODES.unknown) > code and new[s] or code + url = url.parent + end + end + end + return new +end + +---@param excluded string[] +---@param cwd Url +---@param repo Url +---@return Changes +local function propagate_down(excluded, cwd, repo) + local new, rel = {}, cwd:strip_prefix(repo) + for _, path in ipairs(excluded) do + if rel:starts_with(path) then + -- If `cwd` is a subdirectory of an excluded directory, also mark it as `excluded` + new[tostring(cwd)] = CODES.excluded + elseif cwd == repo:join(path).parent then + -- If `path` is a direct subdirectory of `cwd`, mark it as `ignored` + new[path] = CODES.ignored + else + -- Skipping, we only care about `cwd` itself and its direct subdirectories for maximum performance + end + end + return new +end + +---@param cwd string +---@param repo string +---@param changed Changes +local add = ya.sync(function(st, cwd, repo, changed) + ---@cast st State + + st.dirs[cwd] = repo + st.repos[repo] = st.repos[repo] or {} + for path, code in pairs(changed) do + if code == CODES.unknown then + st.repos[repo][path] = nil + elseif code == CODES.excluded then + -- Mark the directory with a special value `excluded` so that it can be distinguished during UI rendering + st.dirs[path] = CODES.excluded + else + st.repos[repo][path] = code + end + end + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end +end) + +---@param cwd string +local remove = ya.sync(function(st, cwd) + ---@cast st State + + local repo = st.dirs[cwd] + if not repo then + return + end + + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end + st.dirs[cwd] = nil + if not st.repos[repo] then + return + end + + for _, r in pairs(st.dirs) do + if r == repo then + return + end + end + st.repos[repo] = nil +end) + +---@param st State +---@param opts Options +local function setup(st, opts) + st.dirs = {} + st.repos = {} + + opts = opts or {} + opts.order = opts.order or 1500 + + local t = th.git or {} + local styles = { + [CODES.ignored] = t.ignored and ui.Style(t.ignored) or ui.Style():fg("darkgray"), + [CODES.untracked] = t.untracked and ui.Style(t.untracked) or ui.Style():fg("magenta"), + [CODES.modified] = t.modified and ui.Style(t.modified) or ui.Style():fg("yellow"), + [CODES.added] = t.added and ui.Style(t.added) or ui.Style():fg("green"), + [CODES.deleted] = t.deleted and ui.Style(t.deleted) or ui.Style():fg("red"), + [CODES.updated] = t.updated and ui.Style(t.updated) or ui.Style():fg("yellow"), + } + local signs = { + [CODES.ignored] = t.ignored_sign or "ο΄", + [CODES.untracked] = t.untracked_sign or "?", + [CODES.modified] = t.modified_sign or "ο", + [CODES.added] = t.added_sign or "ο", + [CODES.deleted] = t.deleted_sign or "ο", + [CODES.updated] = t.updated_sign or "ο", + } + + Linemode:children_add(function(self) + local url = self._file.url + local repo = st.dirs[tostring(url.base)] + local code + if repo then + code = repo == CODES.excluded and CODES.ignored or st.repos[repo][tostring(url):sub(#repo + 2)] + end + + if not code or signs[code] == "" then + return "" + elseif self._file.is_hovered then + return ui.Line { " ", signs[code] } + else + return ui.Line { " ", ui.Span(signs[code]):style(styles[code]) } + end + end, opts.order) +end + +---@type UnstableFetcher +local function fetch(_, job) + local cwd = job.files[1].url.base + local repo = root(cwd) + if not repo then + remove(tostring(cwd)) + return true + end + + local paths = {} + for _, file in ipairs(job.files) do + paths[#paths + 1] = tostring(file.url) + end + + -- stylua: ignore + local output, err = Command("git") + :cwd(tostring(cwd)) + :arg({ "--no-optional-locks", "-c", "core.quotePath=", "status", "--porcelain", "-unormal", "--no-renames", "--ignored=matching" }) + :arg(paths) + :stdout(Command.PIPED) + :output() + if not output then + return true, Err("Cannot spawn `git` command, error: %s", err) + end + + local changed, excluded = {}, {} + for line in output.stdout:gmatch("[^\r\n]+") do + local code, path = match(line) + if code == CODES.excluded then + excluded[#excluded + 1] = path + else + changed[path] = code + end + end + + if job.files[1].cha.is_dir then + ya.dict_merge(changed, bubble_up(changed)) + end + ya.dict_merge(changed, propagate_down(excluded, cwd, Url(repo))) + + -- Reset the status of any files that don't appear in the output of `git status` to `unknown`, + -- so that cleaning up outdated statuses from `st.repos` + for _, path in ipairs(paths) do + local s = path:sub(#repo + 2) + changed[s] = changed[s] or CODES.unknown + end + + add(tostring(cwd), repo, changed) + + return false +end + +return { setup = setup, fetch = fetch } diff --git a/fedora/.config/yazi/plugins/git.yazi/types.lua b/fedora/.config/yazi/plugins/git.yazi/types.lua new file mode 100644 index 0000000..9936849 --- /dev/null +++ b/fedora/.config/yazi/plugins/git.yazi/types.lua @@ -0,0 +1,12 @@ +---@class State +---@field dirs table<string, string|CODES> Mapping between a directory and its corresponding repository +---@field repos table<string, Changes> Mapping between a repository and the status of each of its files + +---@class Options +---@field order number The order in which the status is displayed +---@field renamed boolean Whether to include renamed files in the status (or treat them as modified) + +-- TODO: move this to `types.yazi` once it's get stable +---@alias UnstableFetcher fun(self: unknown, job: { files: File[] }): boolean, Error? + +---@alias Changes table<string, CODES> diff --git a/fedora/.config/yazi/plugins/jump-to-char.yazi/README.md b/fedora/.config/yazi/plugins/jump-to-char.yazi/README.md new file mode 100644 index 0000000..d24f2e7 --- /dev/null +++ b/fedora/.config/yazi/plugins/jump-to-char.yazi/README.md @@ -0,0 +1,28 @@ +# jump-to-char.yazi + +Vim-like `f<char>`, jump to the next file whose name starts with `<char>`. + +https://github.com/yazi-rs/plugins/assets/17523360/aac9341c-b416-4e0c-aaba-889d48389869 + +## Installation + +```sh +ya pkg add yazi-rs/plugins:jump-to-char +``` + +## Usage + +Add this to your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = "f" +run = "plugin jump-to-char" +desc = "Jump to char" +``` + +Make sure the <kbd>f</kbd> key is not used elsewhere. + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/jump-to-char.yazi/main.lua b/fedora/.config/yazi/plugins/jump-to-char.yazi/main.lua new file mode 100644 index 0000000..8a434f1 --- /dev/null +++ b/fedora/.config/yazi/plugins/jump-to-char.yazi/main.lua @@ -0,0 +1,32 @@ +--- @since 25.5.31 + +local AVAILABLE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789." + +local changed = ya.sync(function(st, new) + local b = st.last ~= new + st.last = new + return b or not cx.active.finder +end) + +local escape = function(s) return s == "." and "\\." or s end + +return { + entry = function() + local cands = {} + for i = 1, #AVAILABLE_CHARS do + cands[#cands + 1] = { on = AVAILABLE_CHARS:sub(i, i) } + end + + local idx = ya.which { cands = cands, silent = true } + if not idx then + return + end + + local kw = escape(cands[idx].on) + if changed(kw) then + ya.emit("find_do", { "^" .. kw }) + else + ya.emit("find_arrow", {}) + end + end, +} diff --git a/fedora/.config/yazi/plugins/lsar.yazi/README.md b/fedora/.config/yazi/plugins/lsar.yazi/README.md new file mode 100644 index 0000000..e944442 --- /dev/null +++ b/fedora/.config/yazi/plugins/lsar.yazi/README.md @@ -0,0 +1,43 @@ +# lsar.yazi + +Previewing archive contents with `lsar`, which is something you might not want to use anyway. + +It was the default archive previewer before Yazi v0.3, and after then, it was replaced with a faster and more efficient `7zip` previewer. + +This plugin is here just in case you're still interested in the old behavior, +but we strongly discourage using it unless you encounter some issues with `7zip` when previewing CJK characters - `lsar` usually does a better job recognizing these characters. + +## Installation + +```sh +ya pkg add yazi-rs/plugins:lsar +``` + +## Usage + +Add this to your `~/.config/yazi/yazi.toml`: + +```toml +[[plugin.prepend_previewers]] +mime = "application/{,g}zip" +run = "lsar" + +[[plugin.prepend_previewers]] +mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}" +run = "lsar" +``` + +Make sure you have `unar` installed, and have `lsar` in your `$PATH`. You can install it with: + +```sh +# Arch Linux +sudo pacman -S unarchiver +# macOS +brew install unar +# Windows +scoop install unar +``` + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/lsar.yazi/main.lua b/fedora/.config/yazi/plugins/lsar.yazi/main.lua new file mode 100644 index 0000000..69bc480 --- /dev/null +++ b/fedora/.config/yazi/plugins/lsar.yazi/main.lua @@ -0,0 +1,43 @@ +--- @since 25.5.31 + +local M = {} + +function M:peek(job) + local child, err = Command("lsar"):arg(tostring(job.file.url)):stdout(Command.PIPED):spawn() + if not child then + return ya.err("spawn `lsar` command failed: " .. err) + end + + -- Skip the first line which is the archive file itself + while true do + local _, event = child:read_line() + if event == 0 or event ~= 1 then + break + end + end + + local limit = job.area.h + local i, lines = 0, {} + repeat + local next, event = child:read_line() + if event ~= 0 then + break + end + + i = i + 1 + if i > job.skip then + lines[#lines + 1] = next + end + until i >= job.skip + limit + + child:start_kill() + if job.skip > 0 and i < job.skip + limit then + ya.emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true }) + else + ya.preview_widget(job, ui.Text(lines):area(job.area)) + end +end + +function M:seek(job) require("code"):seek(job) end + +return M diff --git a/fedora/.config/yazi/plugins/mactag.yazi/README.md b/fedora/.config/yazi/plugins/mactag.yazi/README.md new file mode 100644 index 0000000..e5c060b --- /dev/null +++ b/fedora/.config/yazi/plugins/mactag.yazi/README.md @@ -0,0 +1,79 @@ +# mactag.yazi + +Bring macOS's awesome tagging feature to Yazi! The plugin it's only available for macOS just like the name says. + +Authors: [@AnirudhG07](https://github.com/AnirudhG07), and [@sxyazi](https://github.com/sxyazi) + +https://github.com/user-attachments/assets/7f26dc6d-67a5-4a85-a99e-4671ece9ae56 + +## Installation + +Install the plugin itself, and [jdberry/tag](https://github.com/jdberry/tag) used to tag files: + +```sh +ya pkg add yazi-rs/plugins:mactag +brew update && brew install tag +``` + +## Setup + +Add the following to your `~/.config/yazi/init.lua`: + +```lua +require("mactag"):setup { + -- Keys used to add or remove tags + keys = { + r = "Red", + o = "Orange", + y = "Yellow", + g = "Green", + b = "Blue", + p = "Purple", + }, + -- Colors used to display tags + colors = { + Red = "#ee7b70", + Orange = "#f5bd5c", + Yellow = "#fbe764", + Green = "#91fc87", + Blue = "#5fa3f8", + Purple = "#cb88f8", + }, +} +``` + +And register it as fetchers in your `~/.config/yazi/yazi.toml`: + +```toml +[[plugin.prepend_fetchers]] +id = "mactag" +name = "*" +run = "mactag" + +[[plugin.prepend_fetchers]] +id = "mactag" +name = "*/" +run = "mactag" +``` + +## Usage + +Besides displaying tags attached to files, you can also add or remove tags within Yazi using this plugin. + +Add following keybindings to your `~/.config/yazi/keymap.toml` to enable it: + +```toml +[[mgr.prepend_keymap]] +on = [ "b", "a" ] +run = "plugin mactag add" +desc = "Tag selected files" + +[[mgr.prepend_keymap]] +on = [ "b", "r" ] +run = "plugin mactag remove" +desc = "Untag selected files" +``` + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/mactag.yazi/main.lua b/fedora/.config/yazi/plugins/mactag.yazi/main.lua new file mode 100644 index 0000000..309a46a --- /dev/null +++ b/fedora/.config/yazi/plugins/mactag.yazi/main.lua @@ -0,0 +1,105 @@ +--- @since 25.5.31 + +local update = ya.sync(function(st, tags) + for path, tag in pairs(tags) do + st.tags[path] = #tag > 0 and tag or nil + end + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end +end) + +local selected_or_hovered = ya.sync(function() + local tab, urls = cx.active, {} + for _, u in pairs(tab.selected) do + urls[#urls + 1] = u + end + if #urls == 0 and tab.current.hovered then + urls[1] = tab.current.hovered.url + end + return urls +end) + +local function setup(st, opts) + st.tags = {} + st.keys = opts.keys + st.colors = opts.colors + + Linemode:children_add(function(self) + local url = tostring(self._file.url) + local spans = {} + for _, tag in ipairs(st.tags[url] or {}) do + if self._file.is_hovered then + spans[#spans + 1] = ui.Span(" β"):bg(st.colors[tag] or "reset") + else + spans[#spans + 1] = ui.Span(" β"):fg(st.colors[tag] or "reset") + end + end + return ui.Line(spans) + end, 500) +end + +local function fetch(_, job) + local paths = {} + for _, file in ipairs(job.files) do + paths[#paths + 1] = tostring(file.url) + end + + local output, err = Command("tag"):arg(paths):stdout(Command.PIPED):output() + if not output then + return true, Err("Cannot spawn `tag` command, error: %s", err) + end + + local i, tags = 1, {} + for line in output.stdout:gmatch("[^\r\n]+") do + if i > #paths then + break + end + tags[paths[i]] = tags[paths[i]] or {} + + local joint = line:match("\t(.+)$") or "" + for s in joint:gmatch("[^,]+") do + table.insert(tags[paths[i]], s) + end + i = i + 1 + end + + update(tags) + return true +end + +local cands = ya.sync(function(st) + local t = {} + for k, v in pairs(st.keys) do + t[#t + 1] = { on = k, desc = v } + end + return t +end) + +local function entry(self, job) + assert(job.args[1] == "add" or job.args[1] == "remove", "Invalid action") + ya.emit("escape", { visual = true }) + + local cands = cands() + local choice = ya.which { cands = cands } + if not choice then + return + end + + local t = { job.args[1] == "remove" and "-r" or "-a", cands[choice].desc } + local files = {} + for _, url in ipairs(selected_or_hovered()) do + t[#t + 1] = tostring(url) + files[#files + 1] = { url = url } + end + + local status = Command("tag"):arg(t):status() + if status.success then + fetch(self, { files = files }) + end +end + +return { setup = setup, fetch = fetch, entry = entry } diff --git a/fedora/.config/yazi/plugins/mime-ext.yazi/README.md b/fedora/.config/yazi/plugins/mime-ext.yazi/README.md new file mode 100644 index 0000000..3c2ee1f --- /dev/null +++ b/fedora/.config/yazi/plugins/mime-ext.yazi/README.md @@ -0,0 +1,56 @@ +# 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 pkg add 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. diff --git a/fedora/.config/yazi/plugins/mime-ext.yazi/main.lua b/fedora/.config/yazi/plugins/mime-ext.yazi/main.lua new file mode 100644 index 0000000..06c0005 --- /dev/null +++ b/fedora/.config/yazi/plugins/mime-ext.yazi/main.lua @@ -0,0 +1,1126 @@ +--- @since 25.5.31 + +local FILES = { + [".envrc"] = "text/plain", + [".gitconfig"] = "text/plain", + [".gitignore"] = "text/plain", + [".luacheckrc"] = "text/lua", + [".npmrc"] = "text/plain", + [".styluaignore"] = "text/plain", + [".zshenv"] = "text/plain", + [".zshrc"] = "text/plain", + ["cargo.lock"] = "application/json", + ["flake.lock"] = "application/json", + license = "text/plain", +} + +local EXTS = { + ["123"] = "application/lotus-1-2-3", + ["3dml"] = "text/in3d.3dml", + ["3ds"] = "image/3ds", + ["3g2"] = "video/3gpp2", + ["3gp"] = "video/3gpp", + ["7z"] = "application/7z-compressed", + ["for"] = "text/fortran", + ["in"] = "text/plain", + ["n-gage"] = "application/nokia.n-gage.symbian.install", + ["sfd-hdstx"] = "application/hydrostatix.sof-data", + aab = "application/authorware-bin", + aac = "audio/aac", + aam = "application/authorware-map", + aas = "application/authorware-seg", + abw = "application/abiword", + ac = "application/pkix-attr-cert", + acc = "application/americandynamics.acc", + ace = "application/ace-compressed", + acu = "application/acucobol", + acutc = "application/acucorp", + adp = "audio/adpcm", + aep = "application/audiograph", + afm = "application/font-type1", + afp = "application/ibm.modcap", + ahead = "application/ahead.space", + ai = "application/postscript", + aif = "audio/aiff", + aifc = "audio/aiff", + aiff = "audio/aiff", + air = "application/adobe.air-application-installer-package+zip", + ait = "application/dvb.ait", + ami = "application/amiga.ami", + apk = "application/android.package-archive", + appcache = "text/cache-manifest", + application = "application/ms-application", + apr = "application/lotus-approach", + arc = "application/freearc", + asc = "application/pgp-signature", + asf = "video/ms-asf", + asm = "text/asm", + aso = "application/accpac.simply.aso", + ass = "text/ass", + asx = "video/ms-asf", + atc = "application/acucorp", + atom = "application/atom+xml", + atomcat = "application/atomcat+xml", + atomsvc = "application/atomsvc+xml", + atx = "application/antix.game-component", + au = "audio/basic", + avi = "video/msvideo", + avif = "image/avif", + aw = "application/applixware", + azf = "application/airzip.filesecure.azf", + azs = "application/airzip.filesecure.azs", + azw = "application/amazon.ebook", + bash = "text/shellscript", + bat = "application/msdownload", + bcpio = "application/bcpio", + bdf = "application/font-bdf", + bdm = "application/syncml.dm+wbxml", + bean = "text/plain", + beancount = "text/plain", + bed = "application/realvnc.bed", + bh2 = "application/fujitsu.oasysprs", + bin = "application/octet-stream", + blb = "application/blorb", + blorb = "application/blorb", + bmi = "application/bmi", + bmp = "image/bmp", + book = "application/framemaker", + box = "application/previewsystems.box", + boz = "application/bzip2", + bpk = "application/octet-stream", + btif = "image/prs.btif", + bz = "application/bzip", + bz2 = "application/bzip2", + c = "text/c", + c11amc = "application/cluetrust.cartomobile-config", + c11amz = "application/cluetrust.cartomobile-config-pkg", + c4d = "application/clonk.c4group", + c4f = "application/clonk.c4group", + c4g = "application/clonk.c4group", + c4p = "application/clonk.c4group", + c4u = "application/clonk.c4group", + cab = "application/ms-cab-compressed", + caf = "audio/caf", + cap = "application/tcpdump.pcap", + car = "application/curl.car", + cat = "application/ms-pki.seccat", + cb7 = "application/cbr", + cba = "application/cbr", + cbr = "application/cbr", + cbt = "application/cbr", + cbz = "application/cbr", + cc = "text/c", + cct = "application/director", + ccxml = "application/ccxml+xml", + cdbcmsg = "application/contact.cmsg", + cdf = "application/netcdf", + cdkey = "application/mediastation.cdkey", + cdmia = "application/cdmi-capability", + cdmic = "application/cdmi-container", + cdmid = "application/cdmi-domain", + cdmio = "application/cdmi-object", + cdmiq = "application/cdmi-queue", + cdx = "chemical/cdx", + cdxml = "application/chemdraw+xml", + cdy = "application/cinderella", + cer = "application/pkix-cert", + cfg = "text/plain", + cfs = "application/cfs-compressed", + cgm = "image/cgm", + chat = "application/chat", + chm = "application/ms-htmlhelp", + chrt = "application/kde.kchart", + cif = "chemical/cif", + cii = "application/anser-web-certificate-issue-initiation", + cil = "application/ms-artgalry", + cla = "application/claymore", + class = "application/java-vm", + clkk = "application/crick.clicker.keyboard", + clkp = "application/crick.clicker.palette", + clkt = "application/crick.clicker.template", + clkw = "application/crick.clicker.wordbank", + clkx = "application/crick.clicker", + clp = "application/msclip", + cmc = "application/cosmocaller", + cmdf = "chemical/cmdf", + cml = "chemical/cml", + cmp = "application/yellowriver-custom-menu", + cmx = "image/cmx", + cod = "application/rim.cod", + com = "application/msdownload", + conf = "text/plain", + cpio = "application/cpio", + cpp = "text/c", + cpt = "application/mac-compactpro", + crd = "application/mscardfile", + crl = "application/pkix-crl", + crt = "application/x509-ca-cert", + cryptonote = "application/rig.cryptonote", + csh = "application/csh", + csml = "chemical/csml", + csp = "application/commonspace", + css = "text/css", + cst = "application/director", + csv = "text/csv", + cu = "application/cu-seeme", + curl = "text/curl", + cww = "application/prs.cww", + cxt = "application/director", + cxx = "text/c", + dae = "model/collada+xml", + daf = "application/mobius.daf", + dart = "application/dart", + dataless = "application/fdsn.seed", + davmount = "application/davmount+xml", + dbk = "application/docbook+xml", + dcr = "application/director", + dcurl = "text/curl.dcurl", + dd2 = "application/oma.dd2+xml", + ddd = "application/fujixerox.ddd", + deb = "application/debian-package", + def = "text/plain", + deploy = "application/octet-stream", + der = "application/x509-ca-cert", + dfac = "application/dreamfactory", + dgc = "application/dgc-compressed", + dic = "text/c", + dir = "application/director", + dis = "application/mobius.dis", + dist = "application/octet-stream", + distz = "application/octet-stream", + djv = "image/djvu", + djvu = "image/djvu", + dll = "application/msdownload", + dmg = "application/apple-diskimage", + dmp = "application/tcpdump.pcap", + dms = "application/octet-stream", + dna = "application/dna", + doc = "application/msword", + docm = "application/ms-word.document.macroenabled.12", + docx = "application/openxmlformats-officedocument.wordprocessingml.document", + dot = "application/msword", + dotm = "application/ms-word.template.macroenabled.12", + dotx = "application/openxmlformats-officedocument.wordprocessingml.template", + dp = "application/osgi.dp", + dpg = "application/dpgraph", + dra = "audio/dra", + dsc = "text/prs.lines.tag", + dssc = "application/dssc+der", + dtb = "application/dtbook+xml", + dtd = "application/xml-dtd", + dts = "audio/dts", + dtshd = "audio/dts.hd", + dump = "application/octet-stream", + dvb = "video/dvb.file", + dvi = "application/dvi", + dwf = "model/dwf", + dwg = "image/dwg", + dxf = "image/dxf", + dxp = "application/spotfire.dxp", + dxr = "application/director", + ebuild = "application/gentoo.ebuild", + ecelp4800 = "audio/nuera.ecelp4800", + ecelp7470 = "audio/nuera.ecelp7470", + ecelp9600 = "audio/nuera.ecelp9600", + eclass = "application/gentoo.eclass", + ecma = "application/ecmascript", + edm = "application/novadigm.edm", + edx = "application/novadigm.edx", + efif = "application/picsel", + ei6 = "application/pg.osasli", + elc = "application/octet-stream", + emf = "application/msmetafile", + eml = "message/rfc822", + emma = "application/emma+xml", + emz = "application/msmetafile", + env = "text/plain", + eol = "audio/digital-winds", + eot = "application/ms-fontobject", + eps = "application/postscript", + epub = "application/epub+zip", + es3 = "application/eszigno3+xml", + esa = "application/osgi.subsystem", + esf = "application/epson.esf", + et3 = "application/eszigno3+xml", + etx = "text/setext", + eva = "application/eva", + evy = "application/envoy", + exe = "application/msdownload", + exi = "application/exi", + ext = "application/novadigm.ext", + ez = "application/andrew-inset", + ez2 = "application/ezpix-album", + ez3 = "application/ezpix-package", + f = "text/fortran", + f4v = "video/f4v", + f77 = "text/fortran", + f90 = "text/fortran", + fbs = "image/fastbidsheet", + fcdt = "application/adobe.formscentral.fcdt", + fcs = "application/isac.fcs", + fdf = "application/fdf", + fe_launch = "application/denovo.fcselayout-link", + fg5 = "application/fujitsu.oasysgp", + fgd = "application/director", + fh = "image/freehand", + fh4 = "image/freehand", + fh5 = "image/freehand", + fh7 = "image/freehand", + fhc = "image/freehand", + fig = "application/xfig", + fish = "text/shellscript", + flac = "audio/flac", + fli = "video/fli", + flo = "application/micrografx.flo", + flv = "video/flv", + flw = "application/kde.kivio", + flx = "text/fmi.flexstor", + fly = "text/fly", + fm = "application/framemaker", + fnc = "application/frogans.fnc", + fpx = "image/fpx", + frame = "application/framemaker", + fsc = "application/fsc.weblaunch", + fst = "image/fst", + ftc = "application/fluxtime.clip", + fti = "application/anser-web-funds-transfer-initiation", + fvt = "video/fvt", + fxp = "application/adobe.fxp", + fxpl = "application/adobe.fxp", + fzs = "application/fuzzysheet", + g2w = "application/geoplan", + g3 = "image/g3fax", + g3w = "application/geospace", + gac = "application/groove-account", + gam = "application/tads", + gbr = "application/rpki-ghostbusters", + gca = "application/gca-compressed", + gdl = "model/gdl", + geo = "application/dynageo", + gex = "application/geometry-explorer", + ggb = "application/geogebra.file", + ggs = "application/geogebra.slides", + ggt = "application/geogebra.tool", + ghf = "application/groove-help", + gif = "image/gif", + gim = "application/groove-identity-message", + gml = "application/gml+xml", + gmx = "application/gmx", + gnumeric = "application/gnumeric", + go = "text/go", + gph = "application/flographit", + gpx = "application/gpx+xml", + gqf = "application/grafeq", + gqs = "application/grafeq", + gram = "application/srgs", + gramps = "application/gramps-xml", + gre = "application/geometry-explorer", + grv = "application/groove-injector", + grxml = "application/srgs+xml", + gsf = "application/font-ghostscript", + gtar = "application/gtar", + gtm = "application/groove-tool-message", + gtw = "model/gtw", + gv = "text/graphviz", + gxf = "application/gxf", + gxt = "application/geonext", + h = "text/c", + h261 = "video/h261", + h263 = "video/h263", + h264 = "video/h264", + hal = "application/hal+xml", + hbci = "application/hbci", + hcl = "text/hcl", + hdf = "application/hdf", + hh = "text/c", + hlp = "application/winhlp", + hpgl = "application/hp-hpgl", + hpid = "application/hp-hpid", + hpp = "text/c", + hps = "application/hp-hps", + hqx = "application/mac-binhex40", + htke = "application/kenameaapp", + htm = "text/html", + html = "text/html", + hvd = "application/yamaha.hv-dic", + hvp = "application/yamaha.hv-voice", + hvs = "application/yamaha.hv-script", + i2g = "application/intergeo", + icc = "application/iccprofile", + ice = "conference/cooltalk", + icm = "application/iccprofile", + ico = "image/icon", + ics = "text/calendar", + ief = "image/ief", + ifb = "text/calendar", + ifm = "application/shana.informed.formdata", + iges = "model/iges", + igl = "application/igloader", + igm = "application/insors.igm", + igs = "model/iges", + igx = "application/micrografx.igx", + iif = "application/shana.informed.interchange", + imp = "application/accpac.simply.imp", + ims = "application/ms-ims", + ini = "text/plain", + ink = "application/inkml+xml", + inkml = "application/inkml+xml", + install = "application/install-instructions", + iota = "application/astraea-software.iota", + ipfix = "application/ipfix", + ipk = "application/shana.informed.package", + irm = "application/ibm.rights-management", + irp = "application/irepository.package+xml", + iso = "application/iso9660-image", + itp = "application/shana.informed.formtemplate", + ivp = "application/immervision-ivp", + ivu = "application/immervision-ivu", + jad = "text/sun.j2me.app-descriptor", + jam = "application/jam", + jar = "application/java-archive", + java = "text/java-source", + jisp = "application/jisp", + jlt = "application/hp-jlyt", + jnlp = "application/java-jnlp-file", + joda = "application/joost.joda-archive", + jpe = "image/jpeg", + jpeg = "image/jpeg", + jpg = "image/jpeg", + jpgm = "video/jpm", + jpgv = "video/jpeg", + jpm = "video/jpm", + js = "text/javascript", + json = "application/json", + jsonc = "application/json", + jsonml = "application/jsonml+json", + jsx = "text/jsx", + jxl = "image/jxl", + kar = "audio/midi", + karbon = "application/kde.karbon", + kfo = "application/kde.kformula", + kia = "application/kidspiration", + kml = "application/google-earth.kml+xml", + kmz = "application/google-earth.kmz", + kne = "application/kinar", + knp = "application/kinar", + kon = "application/kde.kontour", + kpr = "application/kde.kpresenter", + kpt = "application/kde.kpresenter", + kpxx = "application/ds-keypoint", + ksp = "application/kde.kspread", + ktr = "application/kahootz", + ktx = "image/ktx", + ktz = "application/kahootz", + kwd = "application/kde.kword", + kwt = "application/kde.kword", + lasxml = "application/las.las+xml", + latex = "application/latex", + lbd = "application/llamagraphics.life-balance.desktop", + lbe = "application/llamagraphics.life-balance.exchange+xml", + les = "application/hhe.lesson-player", + lha = "application/lzh-compressed", + link66 = "application/route66.link66+xml", + list = "text/plain", + list3820 = "application/ibm.modcap", + listafp = "application/ibm.modcap", + lnk = "application/ms-shortcut", + log = "text/plain", + lostxml = "application/lost+xml", + lrf = "application/octet-stream", + lrm = "application/ms-lrm", + ltf = "application/frogans.ltf", + lua = "text/lua", + lvp = "audio/lucent.voice", + lwp = "application/lotus-wordpro", + lzh = "application/lzh-compressed", + m13 = "application/msmediaview", + m14 = "application/msmediaview", + m1v = "video/mpeg", + m21 = "application/mp21", + m2a = "audio/mpeg", + m2t = "video/mp2t", + m2ts = "video/mp2t", + m2v = "video/mpeg", + m3a = "audio/mpeg", + m3u = "audio/mpegurl", + m3u8 = "application/apple.mpegurl", + m4a = "audio/mp4", + m4u = "video/mpegurl", + m4v = "video/m4v", + ma = "application/mathematica", + mads = "application/mads+xml", + mag = "application/ecowin.chart", + maker = "application/framemaker", + man = "text/troff", + mar = "application/octet-stream", + mathml = "application/mathml+xml", + mb = "application/mathematica", + mbk = "application/mobius.mbk", + mbox = "application/mbox", + mc1 = "application/medcalcdata", + mcd = "application/mcd", + mcurl = "text/curl.mcurl", + md = "text/markdown", + mdb = "application/msaccess", + mdi = "image/ms-modi", + me = "text/troff", + mesh = "model/mesh", + meta4 = "application/metalink4+xml", + metalink = "application/metalink+xml", + mets = "application/mets+xml", + mfm = "application/mfmp", + mft = "application/rpki-manifest", + mgp = "application/osgeo.mapguide.package", + mgz = "application/proteus.magazine", + mid = "audio/midi", + midi = "audio/midi", + mie = "application/mie", + mif = "application/mif", + mime = "message/rfc822", + mj2 = "video/mj2", + mjp2 = "video/mj2", + mjs = "text/javascript", + mk3d = "video/matroska", + mka = "audio/matroska", + mks = "video/matroska", + mkv = "video/matroska", + mlp = "application/dolby.mlp", + mmd = "application/chipnuts.karaoke-mmd", + mmf = "application/smaf", + mmr = "image/fujixerox.edmics-mmr", + mng = "video/mng", + mny = "application/msmoney", + mobi = "application/mobipocket-ebook", + mods = "application/mods+xml", + mov = "video/quicktime", + movie = "video/sgi-movie", + mp2 = "audio/mpeg", + mp21 = "application/mp21", + mp2a = "audio/mpeg", + mp3 = "audio/mpeg", + mp4 = "video/mp4", + mp4a = "audio/mp4", + mp4s = "application/mp4", + mp4v = "video/mp4", + mpc = "application/mophun.certificate", + mpe = "video/mpeg", + mpeg = "video/mpeg", + mpg = "video/mpeg", + mpg4 = "video/mp4", + mpga = "audio/mpeg", + mpkg = "application/apple.installer+xml", + mpm = "application/blueice.multipass", + mpn = "application/mophun.application", + mpp = "application/ms-project", + mpt = "application/ms-project", + mpy = "application/ibm.minipay", + mqy = "application/mobius.mqy", + mrc = "application/marc", + mrcx = "application/marcxml+xml", + ms = "text/troff", + mscml = "application/mediaservercontrol+xml", + mseed = "application/fdsn.mseed", + mseq = "application/mseq", + msf = "application/epson.msf", + msh = "model/mesh", + msi = "application/msdownload", + msl = "application/mobius.msl", + msty = "application/muvee.style", + mts = "video/mp2t", + mus = "application/musician", + musicxml = "application/recordare.musicxml+xml", + mvb = "application/msmediaview", + mwf = "application/mfer", + mxf = "application/mxf", + mxl = "application/recordare.musicxml", + mxml = "application/xv+xml", + mxs = "application/triscape.mxs", + mxu = "video/mpegurl", + n3 = "text/n3", + nb = "application/mathematica", + nbp = "application/wolfram.player", + nc = "application/netcdf", + ncx = "application/dtbncx+xml", + nfo = "text/nfo", + ngdat = "application/nokia.n-gage.data", + nitf = "application/nitf", + nix = "text/nix", + nlu = "application/neurolanguage.nlu", + nml = "application/enliven", + nnd = "application/noblenet-directory", + nns = "application/noblenet-sealer", + nnw = "application/noblenet-web", + npx = "image/net-fpx", + nsc = "application/conference", + nsf = "application/lotus-notes", + ntf = "application/nitf", + nzb = "application/nzb", + oa2 = "application/fujitsu.oasys2", + oa3 = "application/fujitsu.oasys3", + oas = "application/fujitsu.oasys", + obd = "application/msbinder", + obj = "application/tgif", + oda = "application/oda", + odb = "application/oasis.opendocument.database", + odc = "application/oasis.opendocument.chart", + odf = "application/oasis.opendocument.formula", + odft = "application/oasis.opendocument.formula-template", + odg = "application/oasis.opendocument.graphics", + odi = "application/oasis.opendocument.image", + odm = "application/oasis.opendocument.text-master", + odp = "application/oasis.opendocument.presentation", + ods = "application/oasis.opendocument.spreadsheet", + odt = "application/oasis.opendocument.text", + oga = "audio/ogg", + ogg = "audio/ogg", + ogv = "video/ogg", + ogx = "application/ogg", + omdoc = "application/omdoc+xml", + onepkg = "application/onenote", + onetmp = "application/onenote", + onetoc = "application/onenote", + onetoc2 = "application/onenote", + opf = "application/oebps-package+xml", + opml = "text/opml", + oprc = "application/palm", + opus = "audio/ogg", + org = "application/lotus-organizer", + osf = "application/yamaha.openscoreformat", + osfpvg = "application/yamaha.openscoreformat.osfpvg+xml", + otc = "application/oasis.opendocument.chart-template", + otf = "font/otf", + otg = "application/oasis.opendocument.graphics-template", + oth = "application/oasis.opendocument.text-web", + oti = "application/oasis.opendocument.image-template", + otp = "application/oasis.opendocument.presentation-template", + ots = "application/oasis.opendocument.spreadsheet-template", + ott = "application/oasis.opendocument.text-template", + oxps = "application/oxps", + oxt = "application/openofficeorg.extension", + p = "text/pascal", + p10 = "application/pkcs10", + p12 = "application/pkcs12", + p7b = "application/pkcs7-certificates", + p7c = "application/pkcs7-mime", + p7m = "application/pkcs7-mime", + p7r = "application/pkcs7-certreqresp", + p7s = "application/pkcs7-signature", + p8 = "application/pkcs8", + pas = "text/pascal", + patch = "text/diff", + paw = "application/pawaafile", + pbd = "application/powerbuilder6", + pbm = "image/portable-bitmap", + pcap = "application/tcpdump.pcap", + pcf = "application/font-pcf", + pcl = "application/hp-pcl", + pclxl = "application/hp-pclxl", + pct = "image/pict", + pcurl = "application/curl.pcurl", + pcx = "image/pcx", + pdb = "application/palm", + pdf = "application/pdf", + pfa = "application/font-type1", + pfb = "application/font-type1", + pfm = "application/font-type1", + pfr = "application/font-tdpfr", + pfx = "application/pkcs12", + pgm = "image/portable-graymap", + pgn = "application/chess-pgn", + pgp = "application/pgp-encrypted", + php = "text/php", + pic = "image/pict", + pkg = "application/octet-stream", + pki = "application/pkixcmp", + pkipath = "application/pkix-pkipath", + plb = "application/3gpp.pic-bw-large", + plc = "application/mobius.plc", + plf = "application/pocketlearn", + pls = "application/pls+xml", + pml = "application/ctc-posml", + png = "image/png", + pnm = "image/portable-anymap", + portpkg = "application/macports.portpkg", + pot = "application/ms-powerpoint", + potm = "application/ms-powerpoint.template.macroenabled.12", + potx = "application/openxmlformats-officedocument.presentationml.template", + ppam = "application/ms-powerpoint.addin.macroenabled.12", + ppd = "application/cups-ppd", + ppm = "image/portable-pixmap", + pps = "application/ms-powerpoint", + ppsm = "application/ms-powerpoint.slideshow.macroenabled.12", + ppsx = "application/openxmlformats-officedocument.presentationml.slideshow", + ppt = "application/ms-powerpoint", + pptm = "application/ms-powerpoint.presentation.macroenabled.12", + pptx = "application/openxmlformats-officedocument.presentationml.presentation", + pqa = "application/palm", + prc = "application/mobipocket-ebook", + pre = "application/lotus-freelance", + prf = "application/pics-rules", + ps = "application/postscript", + psb = "application/3gpp.pic-bw-small", + psd = "image/adobe.photoshop", + psf = "application/font-linux-psf", + pskcxml = "application/pskc+xml", + ptid = "application/pvi.ptid1", + pub = "application/mspublisher", + pvb = "application/3gpp.pic-bw-var", + pwn = "application/3m.post-it-notes", + py = "text/python", + pya = "audio/ms-playready.media.pya", + pyv = "video/ms-playready.media.pyv", + qam = "application/epson.quickanime", + qbo = "application/intu.qbo", + qfx = "application/intu.qfx", + qml = "text/qml", + qps = "application/publishare-delta-tree", + qt = "video/quicktime", + qwd = "application/quark.quarkxpress", + qwt = "application/quark.quarkxpress", + qxb = "application/quark.quarkxpress", + qxd = "application/quark.quarkxpress", + qxl = "application/quark.quarkxpress", + qxt = "application/quark.quarkxpress", + r = "text/r", + ra = "audio/pn-realaudio", + ram = "audio/pn-realaudio", + rar = "application/rar", + ras = "image/cmu-raster", + rb = "text/ruby", + rcprofile = "application/ipunplugged.rcprofile", + rdf = "application/rdf+xml", + rdz = "application/data-vision.rdz", + rep = "application/businessobjects", + res = "application/dtbresource+xml", + rgb = "image/rgb", + rif = "application/reginfo+xml", + rip = "audio/rip", + ris = "application/research-info-systems", + rl = "application/resource-lists+xml", + rlc = "image/fujixerox.edmics-rlc", + rld = "application/resource-lists-diff+xml", + rm = "application/rn-realmedia", + rmi = "audio/midi", + rmp = "audio/pn-realaudio-plugin", + rms = "application/jcp.javame.midlet-rms", + rmvb = "application/rn-realmedia-vbr", + rnc = "application/relax-ng-compact-syntax", + roa = "application/rpki-roa", + roff = "text/troff", + rp9 = "application/cloanto.rp9", + rpm = "application/rpm", + rpss = "application/nokia.radio-presets", + rpst = "application/nokia.radio-preset", + rq = "application/sparql-query", + rs = "text/rust", + rsd = "application/rsd+xml", + rss = "application/rss+xml", + rtf = "application/rtf", + rtx = "text/richtext", + s = "text/asm", + s3m = "audio/s3m", + saf = "application/yamaha.smaf-audio", + sbml = "application/sbml+xml", + sc = "application/ibm.secure-container", + scd = "application/msschedule", + scm = "application/lotus-screencam", + scq = "application/scvp-cv-request", + scs = "application/scvp-cv-response", + scss = "text/scss", + scurl = "text/curl.scurl", + sda = "application/stardivision.draw", + sdc = "application/stardivision.calc", + sdd = "application/stardivision.impress", + sdkd = "application/solent.sdkm+xml", + sdkm = "application/solent.sdkm+xml", + sdp = "application/sdp", + sdw = "application/stardivision.writer", + see = "application/seemail", + seed = "application/fdsn.seed", + sema = "application/sema", + semd = "application/semd", + semf = "application/semf", + ser = "application/java-serialized-object", + setpay = "application/set-payment-initiation", + setreg = "application/set-registration-initiation", + sfs = "application/spotfire.sfs", + sfv = "text/sfv", + sgi = "image/sgi", + sgl = "application/stardivision.writer-global", + sgm = "text/sgml", + sgml = "text/sgml", + sh = "text/shellscript", + shar = "application/shar", + shf = "application/shf+xml", + sid = "image/mrsid-image", + sig = "application/pgp-signature", + sil = "audio/silk", + silo = "model/mesh", + sis = "application/symbian.install", + sisx = "application/symbian.install", + sit = "application/stuffit", + sitx = "application/stuffitx", + skd = "application/koan", + skm = "application/koan", + skp = "application/koan", + skt = "application/koan", + sldm = "application/ms-powerpoint.slide.macroenabled.12", + sldx = "application/openxmlformats-officedocument.presentationml.slide", + slt = "application/epson.salt", + sm = "application/stepmania.stepchart", + smf = "application/stardivision.math", + smi = "application/smil+xml", + smil = "application/smil+xml", + smv = "video/smv", + smzip = "application/stepmania.package", + snd = "audio/basic", + snf = "application/font-snf", + so = "application/octet-stream", + spc = "application/pkcs7-certificates", + spf = "application/yamaha.smaf-phrase", + spl = "application/futuresplash", + spot = "text/in3d.spot", + spp = "application/scvp-vp-response", + spq = "application/scvp-vp-request", + spx = "audio/ogg", + sql = "application/sql", + src = "application/wais-source", + srt = "application/subrip", + sru = "application/sru+xml", + srx = "application/sparql-results+xml", + ssdl = "application/ssdl+xml", + sse = "application/kodak-descriptor", + ssf = "application/epson.ssf", + ssml = "application/ssml+xml", + st = "application/sailingtracker.track", + stc = "application/sun.xml.calc.template", + std = "application/sun.xml.draw.template", + stf = "application/wt.stf", + sti = "application/sun.xml.impress.template", + stk = "application/hyperstudio", + stl = "application/ms-pki.stl", + str = "application/pg.format", + stw = "application/sun.xml.writer.template", + sub = "text/dvb.subtitle", + sus = "application/sus-calendar", + susp = "application/sus-calendar", + sv4cpio = "application/sv4cpio", + sv4crc = "application/sv4crc", + svc = "application/dvb.service", + svd = "application/svd", + svg = "image/svg+xml", + svgz = "image/svg+xml", + swa = "application/director", + swf = "application/shockwave-flash", + swi = "application/aristanetworks.swi", + sxc = "application/sun.xml.calc", + sxd = "application/sun.xml.draw", + sxg = "application/sun.xml.writer.global", + sxi = "application/sun.xml.impress", + sxm = "application/sun.xml.math", + sxw = "application/sun.xml.writer", + t = "text/troff", + t3 = "application/t3vm-image", + taglet = "application/mynfc", + tao = "application/tao.intent-module-archive", + tar = "application/tar", + tcap = "application/3gpp2.tcap", + tcl = "application/tcl", + teacher = "application/smart.teacher", + tei = "application/tei+xml", + teicorpus = "application/tei+xml", + tex = "application/tex", + texi = "application/texinfo", + texinfo = "application/texinfo", + text = "text/plain", + tf = "text/hcl", + tfi = "application/thraud+xml", + tfm = "application/tex-tfm", + tfrc = "text/hcl", + tfstate = "application/json", + tfvars = "text/hcl", + tga = "image/tga", + thmx = "application/ms-officetheme", + tif = "image/tiff", + tiff = "image/tiff", + tmo = "application/tmobile-livetv", + toml = "text/toml", + torrent = "application/bittorrent", + tpl = "application/groove-tool-template", + tpt = "application/trid.tpt", + tr = "text/troff", + tra = "application/trueapp", + trm = "application/msterminal", + ts = "text/typescript", + tsd = "application/timestamped-data", + tsv = "text/tab-separated-values", + tsx = "text/tsx", + ttc = "font/collection", + ttf = "font/ttf", + ttl = "text/turtle", + twd = "application/simtech-mindmapper", + twds = "application/simtech-mindmapper", + txd = "application/genomatix.tuxedo", + txf = "application/mobius.txf", + txt = "text/plain", + u32 = "application/authorware-bin", + udeb = "application/debian-package", + ufd = "application/ufdl", + ufdl = "application/ufdl", + ulx = "application/glulx", + umj = "application/umajin", + unityweb = "application/unity", + uoml = "application/uoml+xml", + uri = "text/uri-list", + uris = "text/uri-list", + urls = "text/uri-list", + ustar = "application/ustar", + utz = "application/uiq.theme", + uu = "text/uuencode", + uva = "audio/dece.audio", + uvd = "application/dece.data", + uvf = "application/dece.data", + uvg = "image/dece.graphic", + uvh = "video/dece.hd", + uvi = "image/dece.graphic", + uvm = "video/dece.mobile", + uvp = "video/dece.pd", + uvs = "video/dece.sd", + uvt = "application/dece.ttml+xml", + uvu = "video/uvvu.mp4", + uvv = "video/dece.video", + uvva = "audio/dece.audio", + uvvd = "application/dece.data", + uvvf = "application/dece.data", + uvvg = "image/dece.graphic", + uvvh = "video/dece.hd", + uvvi = "image/dece.graphic", + uvvm = "video/dece.mobile", + uvvp = "video/dece.pd", + uvvs = "video/dece.sd", + uvvt = "application/dece.ttml+xml", + uvvu = "video/uvvu.mp4", + uvvv = "video/dece.video", + uvvx = "application/dece.unspecified", + uvvz = "application/dece.zip", + uvx = "application/dece.unspecified", + uvz = "application/dece.zip", + vcard = "text/vcard", + vcd = "application/cdlink", + vcf = "text/vcard", + vcg = "application/groove-vcard", + vcs = "text/vcalendar", + vcx = "application/vcx", + vis = "application/visionary", + viv = "video/vivo", + vob = "video/ms-vob", + vor = "application/stardivision.writer", + vox = "application/authorware-bin", + vrml = "model/vrml", + vsd = "application/visio", + vsf = "application/vsf", + vss = "application/visio", + vst = "application/visio", + vsw = "application/visio", + vtu = "model/vtu", + vxml = "application/voicexml+xml", + w3d = "application/director", + wad = "application/doom", + wasm = "application/wasm", + wav = "audio/wav", + wax = "audio/ms-wax", + wbmp = "image/wap.wbmp", + wbs = "application/criticaltools.wbs+xml", + wbxml = "application/wap.wbxml", + wcm = "application/ms-works", + wdb = "application/ms-works", + wdp = "image/ms-photo", + weba = "audio/webm", + webm = "video/webm", + webp = "image/webp", + wg = "application/pmi.widget", + wgt = "application/widget", + wks = "application/ms-works", + wm = "video/ms-wm", + wma = "audio/ms-wma", + wmd = "application/ms-wmd", + wmf = "application/msmetafile", + wml = "text/wap.wml", + wmlc = "application/wap.wmlc", + wmls = "text/wap.wmlscript", + wmlsc = "application/wap.wmlscriptc", + wmv = "video/ms-wmv", + wmx = "video/ms-wmx", + wmz = "application/ms-wmz", + woff = "font/woff", + woff2 = "font/woff2", + wpd = "application/wordperfect", + wpl = "application/ms-wpl", + wps = "application/ms-works", + wqd = "application/wqd", + wri = "application/mswrite", + wrl = "model/vrml", + wsdl = "application/wsdl+xml", + wspolicy = "application/wspolicy+xml", + wtb = "application/webturbo", + wvx = "video/ms-wvx", + x32 = "application/authorware-bin", + x3d = "model/x3d+xml", + x3db = "model/x3d+binary", + x3dbz = "model/x3d+binary", + x3dv = "model/x3d+vrml", + x3dvz = "model/x3d+vrml", + x3dz = "model/x3d+xml", + xaml = "application/xaml+xml", + xap = "application/silverlight-app", + xar = "application/xara", + xbap = "application/ms-xbap", + xbd = "application/fujixerox.docuworks.binder", + xbm = "image/xbitmap", + xdf = "application/xcap-diff+xml", + xdm = "application/syncml.dm+xml", + xdp = "application/adobe.xdp+xml", + xdssc = "application/dssc+xml", + xdw = "application/fujixerox.docuworks", + xenc = "application/xenc+xml", + xer = "application/patch-ops-error+xml", + xfdf = "application/adobe.xfdf", + xfdl = "application/xfdl", + xht = "application/xhtml+xml", + xhtml = "application/xhtml+xml", + xhvml = "application/xv+xml", + xif = "image/xiff", + xla = "application/ms-excel", + xlam = "application/ms-excel.addin.macroenabled.12", + xlc = "application/ms-excel", + xlf = "application/xliff+xml", + xlm = "application/ms-excel", + xls = "application/ms-excel", + xlsb = "application/ms-excel.sheet.binary.macroenabled.12", + xlsm = "application/ms-excel.sheet.macroenabled.12", + xlsx = "application/openxmlformats-officedocument.spreadsheetml.sheet", + xlt = "application/ms-excel", + xltm = "application/ms-excel.template.macroenabled.12", + xltx = "application/openxmlformats-officedocument.spreadsheetml.template", + xlw = "application/ms-excel", + xm = "audio/xm", + xml = "application/xml", + xo = "application/olpc-sugar", + xop = "application/xop+xml", + xpak = "application/gentoo.xpak", + xpi = "application/xpinstall", + xpl = "application/xproc+xml", + xpm = "image/xpixmap", + xpr = "application/is-xpr", + xps = "application/ms-xpsdocument", + xpw = "application/intercon.formnet", + xpx = "application/intercon.formnet", + xsl = "application/xml", + xslt = "application/xslt+xml", + xsm = "application/syncml+xml", + xspf = "application/xspf+xml", + xul = "application/mozilla.xul+xml", + xvm = "application/xv+xml", + xvml = "application/xv+xml", + xwd = "image/xwindowdump", + xyz = "chemical/xyz", + xz = "application/xz", + yaml = "text/yaml", + yang = "application/yang", + yin = "application/yin+xml", + yml = "text/yaml", + z1 = "application/zmachine", + z2 = "application/zmachine", + z3 = "application/zmachine", + z4 = "application/zmachine", + z5 = "application/zmachine", + z6 = "application/zmachine", + z7 = "application/zmachine", + z8 = "application/zmachine", + zaz = "application/zzazz.deck+xml", + zip = "application/zip", + zir = "application/zul", + zirz = "application/zul", + zmm = "application/handheld-entertainment+xml", + zsh = "text/shellscript", +} + +local options = ya.sync( + function(st) + return { + with_files = st.with_files, + with_exts = st.with_exts, + fallback_file1 = st.fallback_file1, + } + end +) + +local M = {} + +function M:setup(opts) + opts = opts or {} + + self.with_files = opts.with_files + self.with_exts = opts.with_exts + self.fallback_file1 = opts.fallback_file1 +end + +function M:fetch(job) + local opts = options() + local merged_files = ya.dict_merge(FILES, opts.with_files or {}) + local merged_exts = ya.dict_merge(EXTS, opts.with_exts or {}) + + local updates, unknown, state = {}, {}, {} + for i, file in ipairs(job.files) do + if file.cha.is_dummy then + state[i] = false + goto continue + end + + local mime + if file.cha.len == 0 then + mime = "inode/empty" + else + mime = merged_files[(file.url.name or ""):lower()] + mime = mime or merged_exts[(file.url.ext or ""):lower()] + end + + if mime then + updates[tostring(file.url)], state[i] = mime, true + elseif opts.fallback_file1 then + unknown[#unknown + 1] = file + else + updates[tostring(file.url)], state[i] = "application/octet-stream", true + end + ::continue:: + end + + if next(updates) then + ya.emit("update_mimes", { updates = updates }) + end + + if #unknown > 0 then + return self.fallback_builtin(job, unknown, state) + end + + return state +end + +function M.fallback_builtin(job, unknown, state) + local indices = {} + for i, f in ipairs(job.files) do + indices[f:hash()] = i + end + + local result = require("mime"):fetch(ya.dict_merge(job, { files = unknown })) + for i, f in ipairs(unknown) do + if type(result) == "table" then + state[indices[f:hash()]] = result[i] + else + state[indices[f:hash()]] = result + end + end + return state +end + +return M diff --git a/fedora/.config/yazi/plugins/mount.yazi/README.md b/fedora/.config/yazi/plugins/mount.yazi/README.md new file mode 100644 index 0000000..b35881f --- /dev/null +++ b/fedora/.config/yazi/plugins/mount.yazi/README.md @@ -0,0 +1,48 @@ +# mount.yazi + +A mount manager for Yazi, providing disk mount, unmount, and eject functionality. + +Supported platforms: + +- Linux with [`udisksctl`](https://github.com/storaged-project/udisks), `lsblk` and `eject` both provided by [`util-linux`](https://github.com/util-linux/util-linux) +- macOS with `diskutil`, which is pre-installed + +https://github.com/user-attachments/assets/c6f780ab-458b-420f-85cf-2fc45fcfe3a2 + +## Installation + +```sh +ya pkg add yazi-rs/plugins:mount +``` + +## Usage + +Add this to your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = "M" +run = "plugin mount" +``` + +Available keybindings: + +| Key binding | Alternate key | Action | +| ------------ | ------------- | --------------------- | +| <kbd>q</kbd> | - | Quit the plugin | +| <kbd>k</kbd> | <kbd>β</kbd> | Move up | +| <kbd>j</kbd> | <kbd>β</kbd> | Move down | +| <kbd>l</kbd> | <kbd>β</kbd> | Enter the mount point | +| <kbd>m</kbd> | - | Mount the partition | +| <kbd>u</kbd> | - | Unmount the partition | +| <kbd>e</kbd> | - | Eject the disk | + +## TODO + +- Custom keybindings +- Windows support (I don't use Windows myself, PRs welcome!) +- Support mount, unmount, and eject the entire disk + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. 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 diff --git a/fedora/.config/yazi/plugins/office.yazi/LICENSE b/fedora/.config/yazi/plugins/office.yazi/LICENSE new file mode 100644 index 0000000..fb5b1d6 --- /dev/null +++ b/fedora/.config/yazi/plugins/office.yazi/LICENSE @@ -0,0 +1,21 @@ +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. diff --git a/fedora/.config/yazi/plugins/office.yazi/README.md b/fedora/.config/yazi/plugins/office.yazi/README.md new file mode 100644 index 0000000..c6fdf37 --- /dev/null +++ b/fedora/.config/yazi/plugins/office.yazi/README.md @@ -0,0 +1,76 @@ +<div align="center"> + +# office.yazi +### A plugin to preview office documents in <a href="https://github.com/sxyazi/yazi">Yazi <img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="a duck" width="24px" height="24px"></a> + +<img src="https://github.com/macydnah/office.yazi/blob/assets/preview_test.gif" alt="preview test" width="88%"> + +## + +</div> + +## Installation +> [!TIP] +> Installing this plugin with `ya` will conveniently clone the plugin from GitHub, +> copy it to your plugins directory, and update the `package.toml` to lock its version [^1]. +> +> To install it with `ya` run: +> ```sh +> ya pkg add macydnah/office +> ``` + +> Or if you prefer a manual approach: +> ```sh +> ## For linux and MacOS +> git clone https://github.com/macydnah/office.yazi.git ~/.config/yazi/plugins/office.yazi +> +> ## For Windows +> git clone https://github.com/macydnah/office.yazi.git %AppData%\yazi\config\plugins\office.yazi +> ``` + +## Usage +In your `yazi.toml` add rules to preloaders[^2] and previewers[^3] to run `office` plugin with office documents. + +> [!NOTE] +> Your config may be different depending if you're *appending*, *prepending* or *overriding* default rules. +> If unsure, take a look at [Configuration](https://yazi-rs.github.io/docs/configuration/overview)[^4] +> and [Configuration mixing](https://yazi-rs.github.io/docs/configuration/overview#mixing)[^5] + +For a general usecase, you may use the following rules +```toml +[plugin] + +prepend_preloaders = [ + # Office Documents + { mime = "application/openxmlformats-officedocument.*", run = "office" }, + { mime = "application/oasis.opendocument.*", run = "office" }, + { mime = "application/ms-*", run = "office" }, + { mime = "application/msword", run = "office" }, + { name = "*.docx", run = "office" }, +] + +prepend_previewers = [ + # Office Documents + { mime = "application/openxmlformats-officedocument.*", run = "office" }, + { mime = "application/oasis.opendocument.*", run = "office" }, + { mime = "application/ms-*", run = "office" }, + { mime = "application/msword", run = "office" }, + { name = "*.docx", run = "office" }, +] +``` + +## Dependencies +> [!IMPORTANT] +> Make sure that these commands are installed in your system and can be found in `PATH`: +> +> - `libreoffice` +> - `pdftoppm` + +## License +office.yazi is licensed under the terms of the [MIT License](LICENSE) + +[^1]: [The official package manager for Yazi](https://yazi-rs.github.io/docs/cli) +[^2]: [Preloaders rules](https://yazi-rs.github.io/docs/configuration/yazi#plugin.preloaders) +[^3]: [Previewers rules](https://yazi-rs.github.io/docs/configuration/yazi#plugin.previewers) +[^4]: [Configuration](https://yazi-rs.github.io/docs/configuration/overview) +[^5]: [Configuration mixing](https://yazi-rs.github.io/docs/configuration/overview#mixing) diff --git a/fedora/.config/yazi/plugins/office.yazi/main.lua b/fedora/.config/yazi/plugins/office.yazi/main.lua new file mode 100644 index 0000000..3ec7385 --- /dev/null +++ b/fedora/.config/yazi/plugins/office.yazi/main.lua @@ -0,0 +1,121 @@ +--- @since 25.2.7 + +local M = {} + +function M:peek(job) + local start, cache = os.clock(), ya.file_cache(job) + if not cache then + return + end + + local ok, err = self:preload(job) + if not ok or err then + return + end + + ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock())) + ya.image_show(cache, job.area) + ya.preview_widgets(job, {}) +end + +function M:seek(job) + local h = cx.active.current.hovered + if h and h.url == job.file.url then + local step = ya.clamp(-1, job.units, 1) + ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = job.file.url }) + end +end + +function M:doc2pdf(job) + local tmp = "/tmp/yazi-" .. ya.uid() .. "/" .. ya.hash("office.yazi") .. "/" + + --[[ For Future Reference: Regarding `libreoffice` as preconverter + 1. It prints errors to stdout (always, doesn't matter if it succeeded or it failed) + 2. Always writes the converted files to the filesystem, so no "Mario|Bros|Piping|Magic" for the data stream (https://ask.libreoffice.org/t/using-convert-to-output-to-stdout/38753) + 3. The `pdf:draw_pdf_Export` filter needs literal double quotes when defining its options (https://help.libreoffice.org/latest/en-US/text/shared/guide/pdf_params.html?&DbPAR=SHARED&System=UNIX#generaltext/shared/guide/pdf_params.xhp) + 3.1 Regarding double quotes and Lua strings, see https://www.lua.org/manual/5.1/manual.html#2.1 --]] + local libreoffice = Command("libreoffice") + :arg({ + "--headless", + "--convert-to", + "pdf:draw_pdf_Export:{" + .. '"PageRange":{' + .. '"type":"string",' + .. '"value":' + .. '"' + .. job.skip + 1 + .. '"' + .. "}" + .. "}", + "--outdir", + tmp, + tostring(job.file.url), + }) + :stdin(Command.NULL) + :stdout(Command.PIPED) + :stderr(Command.NULL) + :output() + + if not libreoffice.status.success then + ya.err( + libreoffice.stdout:match("LibreOffice .+"):gsub("%\n.*", "") + .. " " + .. libreoffice.stdout:match("Error .+"):gsub("%\n.*", "") + ) + return nil, Err("Failed to preconvert `%s` to a temporary PDF", job.file.name) + end + + local tmp = tmp .. job.file.name:gsub("%.[^%.]+$", ".pdf") + local read_permission = io.open(tmp, "r") + if not read_permission then + return nil, Err("Failed to read `%s`: make sure file exists and have read access", tmp) + end + read_permission:close() + + return tmp +end + +function M:preload(job) + local cache = ya.file_cache(job) + if not cache or fs.cha(cache) then + return true + end + + local tmp_pdf, err = self:doc2pdf(job) + if not tmp_pdf then + return true, Err(" " .. "%s", err) + end + + local output, err = Command("pdftoppm") + :arg({ + "-singlefile", + "-jpeg", + "-jpegopt", + "quality=" .. rt.preview.image_quality, + "-f", + 1, + tostring(tmp_pdf), + }) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() + + local rm_tmp_pdf, rm_err = fs.remove("file", Url(tmp_pdf)) + if not rm_tmp_pdf then + return true, Err("Failed to remove %s, error: %s", tmp_pdf, rm_err) + end + + if not output then + return true, Err("Failed to start `pdftoppm`, error: %s", err) + elseif not output.status.success then + local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0 + if job.skip > 0 and pages > 0 then + ya.mgr_emit("peek", { math.max(0, pages - 1), only_if = job.file.url, upper_bound = true }) + end + return true, Err("Failed to convert %s to image, stderr: %s", tmp_pdf, output.stderr) + end + + return fs.write(cache, output.stdout) +end + +return M diff --git a/fedora/.config/yazi/plugins/parent-arrow.yazi/main.lua b/fedora/.config/yazi/plugins/parent-arrow.yazi/main.lua new file mode 100644 index 0000000..a4fd880 --- /dev/null +++ b/fedora/.config/yazi/plugins/parent-arrow.yazi/main.lua @@ -0,0 +1,24 @@ +--- @sync entry +local function entry(_, job) + local parent = cx.active.parent + if not parent then + return + end + + local offset = tonumber(job.args[1]) + if not offset then + return ya.err(job.args[1], "is not a number") + end + + local start = parent.cursor + 1 + offset + local end_ = offset < 0 and 1 or #parent.files + local step = offset < 0 and -1 or 1 + for i = start, end_, step do + local target = parent.files[i] + if target and target.cha.is_dir then + return ya.emit("cd", { target.url }) + end + end +end + +return { entry = entry } diff --git a/fedora/.config/yazi/plugins/piper.yazi/README.md b/fedora/.config/yazi/plugins/piper.yazi/README.md new file mode 100644 index 0000000..1cb238f --- /dev/null +++ b/fedora/.config/yazi/plugins/piper.yazi/README.md @@ -0,0 +1,90 @@ +# piper.yazi + +Pipe any shell command as a previewer. + +## Installation + +```sh +ya pkg add yazi-rs/plugins:piper +``` + +## Usage + +Piper is a general-purpose previewer - you can pass any shell command to `piper` and it will use the command's output as the preview content. + +It accepts a string parameter, which is the shell command to be executed, for example: + +```toml +# ~/.config/yazi/yazi.toml +[[plugin.prepend_previewers]] +name = "*" +run = 'piper -- echo "$1"' +``` + +This will set `piper` as the previewer for all file types and use `$1` (file path) as the preview content. + +## Variables + +Available variables: + +- `$w`: the width of the preview area. +- `$h`: the height of the preview area. +- `$1`: the path to the file being previewed. + +## Examples + +Here are some configuration examples: + +### Preview tarballs with [`tar`](https://man7.org/linux/man-pages/man1/tar.1.html) + +```toml +[[plugin.prepend_previewers]] +name = "*.tar*" +run = 'piper --format=url -- tar tf "$1"' +``` + +In this example, `--format=url` tells `piper` to parse the `tar` output as file URLs, so you'll be able to get a list of files with icons. + +### Preview CSV with [`bat`](https://github.com/sharkdp/bat) + +```toml +[[plugin.prepend_previewers]] +name = "*.csv" +run = 'piper -- bat -p --color=always "$1"' +``` + +Note that certain distributions might use a different name for `bat`, like Debian and Ubuntu uses `batcat` instead, so please adjust accordingly. + +### Preview Markdown with [`glow`](https://github.com/charmbracelet/glow) + +```toml +[[plugin.prepend_previewers]] +name = "*.md" +run = 'piper -- CLICOLOR_FORCE=1 glow -w=$w -s=dark "$1"' +``` + +Note that there's [a bug in Glow v2.0](https://github.com/charmbracelet/glow/issues/440#issuecomment-2307992634) that causes slight color differences between tty and non-tty environments. + +### Preview directory tree with [`eza`](https://github.com/eza-community/eza) + +```toml +[[plugin.prepend_previewers]] +name = "*/" +run = 'piper -- eza -TL=3 --color=always --icons=always --group-directories-first --no-quotes "$1"' +``` + +### Use [`hexyl`](https://github.com/sharkdp/hexyl) as fallback previewer + +Yazi defaults to using [`file -bL "$1"`](https://github.com/sxyazi/yazi/blob/main/yazi-plugin/preset/plugins/file.lua) if there's no matched previewer. + +This example uses `hexyl` as a fallback previewer instead of `file`. + +```toml +[[plugin.append_previewers]] +name = "*" +run = 'piper -- hexyl --border=none --terminal-width=$w "$1"' +``` + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/piper.yazi/main.lua b/fedora/.config/yazi/plugins/piper.yazi/main.lua new file mode 100644 index 0000000..aef08eb --- /dev/null +++ b/fedora/.config/yazi/plugins/piper.yazi/main.lua @@ -0,0 +1,70 @@ +--- @since 25.5.31 + +local M = {} + +local function fail(job, s) ya.preview_widget(job, ui.Text.parse(s):area(job.area):wrap(ui.Wrap.YES)) end + +function M:peek(job) + local child, err = Command("sh") + :arg({ "-c", job.args[1], "sh", tostring(job.file.url) }) + :env("w", job.area.w) + :env("h", job.area.h) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :spawn() + + if not child then + return fail(job, "sh: " .. err) + end + + local limit = job.area.h + local i, outs, errs = 0, {}, {} + repeat + local next, event = child:read_line() + if event == 1 then + errs[#errs + 1] = next + elseif event ~= 0 then + break + end + + i = i + 1 + if i > job.skip then + outs[#outs + 1] = next + end + until i >= job.skip + limit + + child:start_kill() + if #errs > 0 then + fail(job, table.concat(errs, "")) + elseif job.skip > 0 and i < job.skip + limit then + ya.emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true }) + else + ya.preview_widget(job, M.format(job, outs)) + end +end + +function M:seek(job) require("code"):seek(job) end + +function M.format(job, lines) + local format = job.args.format + if format ~= "url" then + local s = table.concat(lines, ""):gsub("\r", ""):gsub("\t", string.rep(" ", rt.preview.tab_size)) + return ui.Text.parse(s):area(job.area) + end + + for i = 1, #lines do + lines[i] = lines[i]:gsub("[\r\n]+$", "") + + local icon = File({ + url = Url(lines[i]), + cha = Cha { kind = lines[i]:sub(-1) == "/" and 1 or 0 }, + }):icon() + + if icon then + lines[i] = ui.Line { ui.Span(" " .. icon.text .. " "):style(icon.style), lines[i] } + end + end + return ui.Text(lines):area(job.area) +end + +return M diff --git a/fedora/.config/yazi/plugins/smart-enter.yazi/README.md b/fedora/.config/yazi/plugins/smart-enter.yazi/README.md new file mode 100644 index 0000000..742f2e1 --- /dev/null +++ b/fedora/.config/yazi/plugins/smart-enter.yazi/README.md @@ -0,0 +1,40 @@ +# smart-enter.yazi + +[`Open`][open] files or [`enter`][enter] directories all in one key! + +## Installation + +```sh +ya pkg add yazi-rs/plugins:smart-enter +``` + +## Usage + +Bind your <kbd>l</kbd> key to the plugin, in your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = "l" +run = "plugin smart-enter" +desc = "Enter the child directory, or open the file" +``` + +## Advanced + +By default, `--hovered` is passed to the [`open`][open] command, make the behavior consistent with [`enter`][enter] avoiding accidental triggers, +which means both will only target the currently hovered file. + +If you still want `open` to target multiple selected files, add this to your `~/.config/yazi/init.lua`: + +```lua +require("smart-enter"):setup { + open_multi = true, +} +``` + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. + +[open]: https://yazi-rs.github.io/docs/configuration/keymap/#mgr.open +[enter]: https://yazi-rs.github.io/docs/configuration/keymap/#mgr.enter diff --git a/fedora/.config/yazi/plugins/smart-enter.yazi/main.lua b/fedora/.config/yazi/plugins/smart-enter.yazi/main.lua new file mode 100644 index 0000000..e9e2ec6 --- /dev/null +++ b/fedora/.config/yazi/plugins/smart-enter.yazi/main.lua @@ -0,0 +1,11 @@ +--- @since 25.5.31 +--- @sync entry + +local function setup(self, opts) self.open_multi = opts.open_multi end + +local function entry(self) + local h = cx.active.current.hovered + ya.emit(h and h.cha.is_dir and "enter" or "open", { hovered = not self.open_multi }) +end + +return { entry = entry, setup = setup } diff --git a/fedora/.config/yazi/plugins/smart-filter.yazi/README.md b/fedora/.config/yazi/plugins/smart-filter.yazi/README.md new file mode 100644 index 0000000..97be2ac --- /dev/null +++ b/fedora/.config/yazi/plugins/smart-filter.yazi/README.md @@ -0,0 +1,28 @@ +# smart-filter.yazi + +A Yazi plugin that makes filters smarter: continuous filtering, automatically enter unique directory, open file on submitting. + +https://github.com/yazi-rs/plugins/assets/17523360/72aaf117-1378-4f7e-93ba-d425a79deac5 + +## Installation + +```sh +ya pkg add yazi-rs/plugins:smart-filter +``` + +## Usage + +Add this to your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = "F" +run = "plugin smart-filter" +desc = "Smart filter" +``` + +Make sure the <kbd>F</kbd> key is not used elsewhere. + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/smart-filter.yazi/main.lua b/fedora/.config/yazi/plugins/smart-filter.yazi/main.lua new file mode 100644 index 0000000..146e265 --- /dev/null +++ b/fedora/.config/yazi/plugins/smart-filter.yazi/main.lua @@ -0,0 +1,51 @@ +--- @since 25.5.31 + +local hovered = ya.sync(function() + local h = cx.active.current.hovered + if not h then + return {} + end + + return { + url = h.url, + is_dir = h.cha.is_dir, + unique = #cx.active.current.files == 1, + } +end) + +local function prompt() + return ya.input { + title = "Smart filter:", + pos = { "center", w = 50 }, + position = { "center", w = 50 }, -- TODO: remove + realtime = true, + debounce = 0.1, + } +end + +local function entry() + local input = prompt() + + while true do + local value, event = input:recv() + if event ~= 1 and event ~= 3 then + ya.emit("escape", { filter = true }) + break + end + + ya.emit("filter_do", { value, smart = true }) + + local h = hovered() + if h.unique and h.is_dir then + ya.emit("escape", { filter = true }) + ya.emit("enter", {}) + input = prompt() + elseif event == 1 then + ya.emit("escape", { filter = true }) + ya.emit(h.is_dir and "enter" or "open", { h.url }) + break + end + end +end + +return { entry = entry } diff --git a/fedora/.config/yazi/plugins/smart-paste.yazi/README.md b/fedora/.config/yazi/plugins/smart-paste.yazi/README.md new file mode 100644 index 0000000..b32f475 --- /dev/null +++ b/fedora/.config/yazi/plugins/smart-paste.yazi/README.md @@ -0,0 +1,26 @@ +# smart-paste.yazi + +Paste files into the hovered directory or to the CWD if hovering over a file. + +https://github.com/user-attachments/assets/b3f6348e-abbe-42fe-9a67-a96e68f11255 + +## Installation + +```sh +ya pkg add yazi-rs/plugins:smart-paste +``` + +## Usage + +Add this to your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = "p" +run = "plugin smart-paste" +desc = "Paste into the hovered directory or CWD" +``` + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/smart-paste.yazi/main.lua b/fedora/.config/yazi/plugins/smart-paste.yazi/main.lua new file mode 100644 index 0000000..0837a4b --- /dev/null +++ b/fedora/.config/yazi/plugins/smart-paste.yazi/main.lua @@ -0,0 +1,14 @@ +--- @since 25.5.31 +--- @sync entry +return { + entry = function() + local h = cx.active.current.hovered + if h and h.cha.is_dir then + ya.emit("enter", {}) + ya.emit("paste", {}) + ya.emit("leave", {}) + else + ya.emit("paste", {}) + end + end, +} diff --git a/fedora/.config/yazi/plugins/sudo-demo.yazi/README.md b/fedora/.config/yazi/plugins/sudo-demo.yazi/README.md new file mode 100644 index 0000000..8068691 --- /dev/null +++ b/fedora/.config/yazi/plugins/sudo-demo.yazi/README.md @@ -0,0 +1,25 @@ +# sudo-demo.yazi + +Just an example showing how to use `sudo` in a Yazi plugin, and the plugin itself doesn't offer any features beyond logging a message. + +## Installation + +```sh +ya pkg add yazi-rs/plugins:sudo-demo +``` + +## Usage + +Add this to your `~/.config/yazi/keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = "<C-t>" +run = "plugin sudo-demo" +``` + +Press <kbd>Ctrl</kbd> + <kbd>t</kbd> to run the plugin, you should [see a message in the log](https://yazi-rs.github.io/docs/plugins/overview#logging). + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/sudo-demo.yazi/main.lua b/fedora/.config/yazi/plugins/sudo-demo.yazi/main.lua new file mode 100644 index 0000000..599afe4 --- /dev/null +++ b/fedora/.config/yazi/plugins/sudo-demo.yazi/main.lua @@ -0,0 +1,45 @@ +--- @since 25.5.31 + +--- Verify if `sudo` is already authenticated +--- @return boolean +local function sudo_already() + local status = Command("sudo"):arg({ "--validate", "--non-interactive" }):status() + assert(status, "Failed to run `sudo --validate --non-interactive`") + return status.success +end + +--- Run a program with `sudo` privilege +--- @param program string +--- @param args table +--- @return Output|nil output +--- @return integer|nil err +--- nil: no error +--- 1: sudo failed +local function run_with_sudo(program, args) + local cmd = Command("sudo"):arg(program):arg(args) + if sudo_already() then + return cmd:output() + end + + local permit = ui.hide and ui.hide() or ya.hide() -- TODO: remove this + print(string.format("Sudo password required to run: `%s %s`", program, table.concat(args))) + local output = cmd:output() + permit:drop() + + if output.status.success or sudo_already() then + return output + end + return nil, 1 +end + +return { + entry = function() + local output = run_with_sudo("ls", { "-l" }) + if not output then + return ya.err("Failed to run `sudo ls -l`") + end + + ya.err("stdout", output.stdout) + ya.err("status.code", output.status.code) + end, +} diff --git a/fedora/.config/yazi/plugins/toggle-pane.yazi/README.md b/fedora/.config/yazi/plugins/toggle-pane.yazi/README.md new file mode 100644 index 0000000..3ef4095 --- /dev/null +++ b/fedora/.config/yazi/plugins/toggle-pane.yazi/README.md @@ -0,0 +1,78 @@ +# toggle-pane.yazi + +Toggle the show, hide, and maximize states for different panes: parent, current, and preview. It respects the user's [`ratio` settings](https://yazi-rs.github.io/docs/configuration/yazi#mgr.ratio)! + +Assume the user's `ratio` is $$[A, B, C]$$, that is, $$\text{parent}=A, \text{current}=B, \text{preview}=C$$: + +- `min-parent`: Toggles between $$0$$ and $$A$$ - the parent is either completely hidden or showed with width $$A$$. +- `max-parent`: Toggles between $$A$$ and $$\infty$$ - the parent is either showed with width $$A$$ or fills the entire screen. +- `min-current`: Toggles between $$0$$ and $$B$$ - the current is either completely hidden or showed with width $$B$$. +- `max-current`: Toggles between $$B$$ and $$\infty$$ - the current is either showed with width $$B$$ or fills the entire screen. +- `min-preview`: Toggles between $$0$$ and $$C$$ - the preview is either completely hidden or showed with width $$C$$. +- `max-preview`: Toggles between $$C$$ and $$\infty$$ - the preview is either showed with width $$C$$ or fills the entire screen. +- `reset`: Resets to the user's configured `ratio`. + +## Installation + +```sh +ya pkg add yazi-rs/plugins:toggle-pane +``` + +## Usage + +Hide/Show preview: + +```toml +# keymap.toml +[[mgr.prepend_keymap]] +on = "T" +run = "plugin toggle-pane min-preview" +desc = "Show or hide the preview pane" +``` + +Maximize/Restore preview: + +```toml +# keymap.toml +[[mgr.prepend_keymap]] +on = "T" +run = "plugin toggle-pane max-preview" +desc = "Maximize or restore the preview pane" +``` + +You can replace `preview` with `current` or `parent` to toggle the other panes. + +## Advanced + +In addition to triggering the plugin with a keypress, you can also trigger it in your `init.lua` file: + +```lua +if os.getenv("NVIM") then + require("toggle-pane"):entry("min-preview") +end +``` + +In the example above, when it detects that you're [using Yazi in nvim](https://yazi-rs.github.io/docs/resources#vim), the preview is hidden by default β you can always press `T` (or any key you've bound) to show it again. + +## Tips + +This plugin only maximizes the "available preview area", without actually changing the content size. + +This means that the appearance of your preview largely depends on the previewer you are using. +However, most previewers tend to make the most of the available space, so this usually isn't an issue. + +For image previews, you may want to tune up the [`max_width`][max-width] and [`max_height`][max-height] options in your `yazi.toml`: + +```toml +[preview] +# Change them to your desired values +max_width = 1000 +max_height = 1000 +``` + +[max-width]: https://yazi-rs.github.io/docs/configuration/yazi/#preview.max_width +[max-height]: https://yazi-rs.github.io/docs/configuration/yazi/#preview.max_height + +## License + +This plugin is MIT-licensed. For more information, check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/toggle-pane.yazi/main.lua b/fedora/.config/yazi/plugins/toggle-pane.yazi/main.lua new file mode 100644 index 0000000..72bbf0e --- /dev/null +++ b/fedora/.config/yazi/plugins/toggle-pane.yazi/main.lua @@ -0,0 +1,45 @@ +--- @since 25.5.31 +--- @sync entry + +local function entry(st, job) + local R = rt.mgr.ratio + job = type(job) == "string" and { args = { job } } or job + + st.parent = st.parent or R.parent + st.current = st.current or R.current + st.preview = st.preview or R.preview + + local act, to = string.match(job.args[1] or "", "(.-)-(.+)") + if act == "min" then + st[to] = st[to] == R[to] and 0 or R[to] + elseif act == "max" then + local max = st[to] == 65535 and R[to] or 65535 + st.parent = st.parent == 65535 and R.parent or st.parent + st.current = st.current == 65535 and R.current or st.current + st.preview = st.preview == 65535 and R.preview or st.preview + st[to] = max + end + + if not st.old then + st.old = Tab.layout + 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 + end + + if not act then + Tab.layout, st.old = st.old, nil + st.parent, st.current, st.preview = nil, nil, nil + end + ya.emit("app:resize", {}) +end + +return { entry = entry } diff --git a/fedora/.config/yazi/plugins/zoom.yazi/README.md b/fedora/.config/yazi/plugins/zoom.yazi/README.md new file mode 100644 index 0000000..9be8025 --- /dev/null +++ b/fedora/.config/yazi/plugins/zoom.yazi/README.md @@ -0,0 +1,56 @@ +> [!NOTE] +> The latest Yazi nightly build is required to use this plugin at the moment. + +# zoom.yazi + +Enlarge or shrink the preview image of a file, which is useful for magnifying small files for viewing. + +Supported formats: + +- Images - requires [ImageMagick](https://imagemagick.org/) (>= 7.1.1) + +Note that, the maximum size of enlarged images is limited by the [`max_width`][max_width] and [`max_height`][max_height] configuration options, so you may need to increase them as needed. + +https://github.com/user-attachments/assets/b28912b1-da63-43d3-a21f-b9e6767ed4a9 + +[max_width]: https://yazi-rs.github.io/docs/configuration/yazi#preview.max_width +[max_height]: https://yazi-rs.github.io/docs/configuration/yazi#preview.max_height + +## Installation + +```sh +ya pkg add yazi-rs/plugins:zoom +``` + +## Usage + +```toml +# keymap.toml +[[mgr.prepend_keymap]] +on = "+" +run = "plugin zoom 1" +desc = "Zoom in hovered file" + +[[mgr.prepend_keymap]] +on = "-" +run = "plugin zoom -1" +desc = "Zoom out hovered file" +``` + +## Advanced + +If you want to apply a default zoom parameter to image previews, you can specify it while setting this plugin up as a custom previewer, for example: + +```toml +[[plugin.prepend_previewers]] +mime = "image/{jpeg,png,webp}" +run = "zoom 5" +``` + +## TODO + +- [ ] Support more file types (e.g., videos, PDFs), PRs welcome! + +## License + +This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. diff --git a/fedora/.config/yazi/plugins/zoom.yazi/main.lua b/fedora/.config/yazi/plugins/zoom.yazi/main.lua new file mode 100644 index 0000000..8aea0cd --- /dev/null +++ b/fedora/.config/yazi/plugins/zoom.yazi/main.lua @@ -0,0 +1,119 @@ +--- @since 25.6.11 + +local get = ya.sync(function(st, url) return st.last == url and st.level end) + +local save = ya.sync(function(st, url, new) + local h = cx.active.current.hovered + if h and h.url == url then + st.last, st.level = url, new + return true + end +end) + +local lock = ya.sync(function(st, url, old, new) + if st.last == url and st.level == old then + st.level = new + return true + end +end) + +local move = ya.sync(function(st) + local h = cx.active.current.hovered + if not h then + return + end + + if st.last ~= h.url then + st.last, st.level = Url(h.url), 0 + end + + return { url = h.url, level = st.level } +end) + +local function end_(job, err) + if not job.old_level then + ya.preview_widget(job, err and ui.Text(err):area(job.area):wrap(ui.Wrap.YES)) + elseif err then + ya.notify { title = "Zoom", content = tostring(err), timeout = 5, level = "error" } + end +end + +local function canvas(area) + local cw, ch = rt.term.cell_size() + if not cw then + return rt.preview.max_width, rt.preview.max_height + end + + return math.min(rt.preview.max_width, math.floor(area.w * cw)), + math.min(rt.preview.max_height, math.floor(area.h * ch)) +end + +local function peek(_, job) + local url = job.file.url + local info, err = ya.image_info(url) + if not info then + return end_(job, Err("Failed to get image info: %s", err)) + end + + local level = ya.clamp(-10, job.new_level or get(Url(url)) or tonumber(job.args[1]) or 0, 10) + local sync = function() + if job.old_level then + return lock(url, job.old_level, level) + else + return save(url, level) + end + end + + local max_w, max_h = canvas(job.area) + local min_w, min_h = math.min(max_w, info.w), math.min(max_h, info.h) + local new_w = min_w + math.floor(min_w * level * 0.1) + local new_h = min_h + math.floor(min_h * level * 0.1) + if new_w > max_w or new_h > max_h then + if job.old_level then + return sync() -- Image larger than available preview area after zooming + else + new_w, new_h = max_w, max_h -- Run as a previewer, render the image anyway + end + end + + local tmp = os.tmpname() + -- stylua: ignore + local status, err = Command("magick"):arg { + tostring(url), + "-auto-orient", "-strip", + "-sample", string.format("%dx%d", new_w, new_h), + "-quality", rt.preview.image_quality, + string.format("JPG:%s", tmp), + }:status() + + if not status then + end_(job, Err("Failed to run `magick` command: %s", err)) + elseif not status.success then + end_(job, Err("`magick` command exited with error code %d", status.code)) + elseif sync() then + ya.image_show(Url(tmp), job.area) + end + end_(job) +end + +local function entry(self, job) + local st = move() + if not st then + return + end + + local motion = tonumber(job.args[1]) or 0 + local new = ya.clamp(-10, st.level + motion, 10) + if new ~= st.level then + peek(self, { + area = ui.area("preview"), + args = {}, + file = { url = st.url }, -- FIXME: use `File` instead of a dummy file + skip = 0, + new_level = new, + old_level = st.level, + }) + end +end + +return { peek = peek, entry = entry } |
