Compare commits
2 Commits
f08170029b
...
0e1551dc61
| Author | SHA1 | Date |
|---|---|---|
|
|
0e1551dc61 | 2 weeks ago |
|
|
ade62929ac | 2 weeks ago |
64 changed files with 10753 additions and 9 deletions
@ -0,0 +1,22 @@
|
||||
# Root level - UI/Color options go here |
||||
highlight_color = "LightBlue" |
||||
main_border_color = "White" |
||||
pin_color = "Orange" |
||||
terminal_launcher = "kitty -e" # or "tty" for TTY mode (-t/--tty) |
||||
|
||||
# App launcher specific options |
||||
[app_launcher] |
||||
filter_desktop = true |
||||
filter_actions = false |
||||
list_executables_in_path = true |
||||
ranking_mode = "frecency" |
||||
pinned_order = "ranking" |
||||
|
||||
# Dmenu mode overrides |
||||
[dmenu] |
||||
delimiter = " " |
||||
show_line_numbers = true |
||||
|
||||
# Clipboard mode overrides |
||||
[cclip] |
||||
image_preview = true |
||||
@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2024 Chromium Oxide |
||||
|
||||
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. |
||||
@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2020 Mitchell Hanberg |
||||
|
||||
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. |
||||
|
After Width: | Height: | Size: 162 KiB |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2023 - Matthew Dong |
||||
|
||||
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. |
||||
@ -0,0 +1,201 @@
|
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
|
After Width: | Height: | Size: 226 KiB |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
require("full-border"):setup({ |
||||
-- Available values: ui.Border.PLAIN, ui.Border.ROUNDED |
||||
type = ui.Border.ROUNDED, |
||||
}) |
||||
|
||||
require("git"):setup({ |
||||
-- Order of status signs showing in the linemode |
||||
order = 1500, |
||||
}) |
||||
|
||||
-- require("relative-motions"):setup({ show_numbers = "relative", show_motion = true, enter_mode = "first" }) |
||||
@ -0,0 +1,56 @@
|
||||
[[mgr.prepend_keymap]] |
||||
on = "M" |
||||
run = "plugin mount" |
||||
|
||||
# [[mgr.prepend_keymap]] |
||||
# on = [ "m" ] |
||||
# run = "plugin relative-motions" |
||||
# desc = "Trigger a new relative motion" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = "f" |
||||
run = "plugin jump-to-char" |
||||
desc = "Jump to char" |
||||
|
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = "F" |
||||
run = "filter --smart" |
||||
desc = "Filter files" |
||||
|
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = "l" |
||||
run = "plugin smart-enter" |
||||
desc = "Enter the child directory, or open the file" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "C" ] |
||||
run = "plugin chmod" |
||||
desc = "Chmod on selected files" |
||||
|
||||
[[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" |
||||
|
||||
# [[mgr.prepend_keymap]] |
||||
# on = "o" |
||||
# run = "plugin open-with-cmd -- block" |
||||
# desc = "Open with command in the terminal" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = "L" |
||||
run = "plugin open-with-cmd" |
||||
desc = "Open with command" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = "<C-d>" |
||||
run = "plugin diff" |
||||
desc = "Diff the selected with the hovered file" |
||||
|
||||
@ -0,0 +1,69 @@
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:mount" |
||||
rev = "598cdb6" |
||||
hash = "e3260d0b1feded8c8e3ae3afa1169e30" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:smart-enter" |
||||
rev = "598cdb6" |
||||
hash = "187cc58ba7ac3befd49c342129e6f1b6" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:full-border" |
||||
rev = "598cdb6" |
||||
hash = "7b625412411be411153886894d9acaf" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:git" |
||||
rev = "598cdb6" |
||||
hash = "88e56a64b7ce7c4314427452343fef17" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:chmod" |
||||
rev = "598cdb6" |
||||
hash = "f0c8c378184d5f8abd1b095a443d336d" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:zoom" |
||||
rev = "598cdb6" |
||||
hash = "fa268cb59989a87780605fdcfb8d99a9" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:piper" |
||||
rev = "598cdb6" |
||||
hash = "d99cfbc3812e7738505f9e4bedb759cd" |
||||
|
||||
[[plugin.deps]] |
||||
use = "boydaihungst/gvfs" |
||||
rev = "3abc0a2" |
||||
hash = "eed89e75671723c8f9390a2862af0e4e" |
||||
|
||||
[[plugin.deps]] |
||||
use = "Ape/open-with-cmd" |
||||
rev = "eba191d" |
||||
hash = "722a60eca1b338c9c24bb508d2d48578" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:diff" |
||||
rev = "598cdb6" |
||||
hash = "8b1af6b5a69797ee951f2a80ce570818" |
||||
|
||||
[[plugin.deps]] |
||||
use = "dedukun/relative-motions" |
||||
rev = "a603d9e" |
||||
hash = "e02a788e5b8ae0fb47fd0193dda589cc" |
||||
|
||||
[[plugin.deps]] |
||||
use = "yazi-rs/plugins:jump-to-char" |
||||
rev = "598cdb6" |
||||
hash = "7a4b4237223aaa94e589d1c01a23542a" |
||||
|
||||
[[flavor.deps]] |
||||
use = "Chromium-3-Oxide/everforest-medium" |
||||
rev = "e1ead7b" |
||||
hash = "f7dc46200e53da5e94858630184a9220" |
||||
|
||||
[[flavor.deps]] |
||||
use = "matt-dong-123/gruvbox-material" |
||||
rev = "e0fd2d8" |
||||
hash = "8f46ca96c0bbe1bdfb4fa201dd5eed4b" |
||||
@ -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. |
||||
@ -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" |
||||
``` |
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins. |
||||
|
||||
## License |
||||
|
||||
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. |
||||
@ -0,0 +1,47 @@
|
||||
--- @since 26.1.22 |
||||
|
||||
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) |
||||
|
||||
local function fail(s, ...) |
||||
ya.notify { |
||||
title = "Chmod", |
||||
content = string.format(s, ...), |
||||
level = "error", |
||||
timeout = 5, |
||||
} |
||||
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 }, |
||||
} |
||||
if event ~= 1 then |
||||
return |
||||
end |
||||
|
||||
local output, err = Command("chmod"):arg(value):arg(urls):output() |
||||
if not output then |
||||
fail("Failed to run chmod: %s", err) |
||||
elseif not output.status.success then |
||||
fail("Chmod failed with stderr:\n%s", output.stderr:gsub("^chmod:%s*", "")) |
||||
end |
||||
end, |
||||
} |
||||
@ -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. |
||||
@ -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" |
||||
``` |
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins. |
||||
|
||||
## License |
||||
|
||||
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. |
||||
@ -0,0 +1,41 @@
|
||||
--- @since 26.1.22 |
||||
|
||||
local function info(content) |
||||
return ya.notify { |
||||
title = "Diff", |
||||
content = content, |
||||
timeout = 5, |
||||
} |
||||
end |
||||
|
||||
local selected_path = ya.sync(function() |
||||
for _, u in pairs(cx.active.selected) do |
||||
return u.cache or u |
||||
end |
||||
end) |
||||
|
||||
local hovered_path = ya.sync(function() |
||||
local h = cx.active.current.hovered |
||||
return h and h.path |
||||
end) |
||||
|
||||
return { |
||||
entry = function() |
||||
local a, b = selected_path(), hovered_path() |
||||
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, |
||||
} |
||||
@ -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. |
||||
@ -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. |
||||
@ -0,0 +1,44 @@
|
||||
--- @since 26.5.6 |
||||
|
||||
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.y(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), |
||||
|
||||
bar("┬", c[2].x, c[1].y), |
||||
bar("┴", c[2].x, c[1].bottom - 1), |
||||
bar("┬", c[2].right - 1, c[2].y), |
||||
bar("┴", c[2].right - 1, c[2].bottom - 1), |
||||
}) |
||||
|
||||
old_build(self, ...) |
||||
end |
||||
end |
||||
|
||||
return { setup = setup } |
||||
@ -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. |
||||
@ -0,0 +1,86 @@
|
||||
# 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 { |
||||
-- Order of status signs showing in the linemode |
||||
order = 1500, |
||||
} |
||||
``` |
||||
|
||||
And register it as fetchers in your `~/.config/yazi/yazi.toml`: |
||||
|
||||
```toml |
||||
[[plugin.prepend_fetchers]] |
||||
id = "git" # Remove if Yazi > v26.1.22 |
||||
url = "*" |
||||
run = "git" |
||||
group = "git" |
||||
|
||||
[[plugin.prepend_fetchers]] |
||||
id = "git" # Remove if Yazi > v26.1.22 |
||||
url = "*/" |
||||
run = "git" |
||||
group = "git" |
||||
``` |
||||
|
||||
## Advanced |
||||
|
||||
You can customize the [Style](https://yazi-rs.github.io/docs/configuration/theme#types.style) of the status sign with: |
||||
|
||||
- `[git].unknown` - status cannot/not yet determined |
||||
- `[git].modified` - modified file |
||||
- `[git].added` - added file |
||||
- `[git].untracked` - untracked file |
||||
- `[git].ignored` - ignored file |
||||
- `[git].deleted` - deleted file |
||||
- `[git].updated` - updated file |
||||
- `[git].clean` - clean file |
||||
|
||||
For example: |
||||
|
||||
```toml |
||||
# theme.toml / flavor.toml |
||||
[git] |
||||
modified = { fg = "blue" } |
||||
deleted = { fg = "red", bold = true } |
||||
``` |
||||
|
||||
You can also customize the text of the status sign with: |
||||
|
||||
- `[git].unknown_sign` - status cannot/not yet determined |
||||
- `[git].modified_sign` - modified file |
||||
- `[git].added_sign` - added file |
||||
- `[git].untracked_sign` - untracked file |
||||
- `[git].ignored_sign` - ignored file |
||||
- `[git].deleted_sign` - deleted file |
||||
- `[git].updated_sign` - updated file |
||||
- `[git].clean_sign` - clean file |
||||
|
||||
For example: |
||||
|
||||
```toml |
||||
# theme.toml / flavor.toml |
||||
[git] |
||||
unknown_sign = " " |
||||
modified_sign = "M" |
||||
deleted_sign = "D" |
||||
clean_sign = "✔" |
||||
``` |
||||
|
||||
## License |
||||
|
||||
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. |
||||
@ -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> |
||||
@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2025 boydaihungst |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
@ -0,0 +1,324 @@
|
||||
# gvfs.yazi |
||||
|
||||
<!-- toc --> |
||||
|
||||
- [Preview](#preview) |
||||
- [Features](#features) |
||||
- [Requirements](#requirements) |
||||
- [Installation](#installation) |
||||
- [Usage](#usage) |
||||
- [Note for mounting using fstab](#note-for-mounting-using-fstab) |
||||
- [Troubleshooting](#troubleshooting) |
||||
|
||||
<!-- tocstop --> |
||||
|
||||
[gvfs.yazi](https://github.com/boydaihungst/gvfs.yazi) uses [gvfs](https://wiki.gnome.org/Projects/gvfs) and [gio from glib](https://github.com/GNOME/glib) to transparently mount and unmount devices or remote storage in read and write mode, |
||||
allowing you to navigate inside, view, and edit individual or groups of files and folders. |
||||
|
||||
Supported protocols: MTP, Hard disk/drive, SMB, SFTP, NFS, GPhoto2 (PTP), FTP, Google Drive (via [GOA](./GNOME_ONLINE_ACCOUNTS_GOA.md)), One drive (via [GOA](./GNOME_ONLINE_ACCOUNTS_GOA.md)), DNS-SD, DAV (WebDAV), AFP, AFC. You need to install corresponding packages to use them. |
||||
|
||||
Tested: MTP, Hard disk/drive (Encrypted and Unencrypted), GPhoto2 (PTP), DAV, SFTP, FTP, SMB, NFSv4, Google Drive, One Drive. You may need to unlock and turn screen on to mount some devices (Android MTP, etc.) |
||||
|
||||
> [!NOTE] |
||||
> |
||||
> - This plugin only supports Linux |
||||
|
||||
## Preview |
||||
|
||||
https://github.com/user-attachments/assets/6aad98f7-081a-4e06-b398-5f7e8ca4ab39 |
||||
|
||||
Google-drive: |
||||
|
||||
https://github.com/user-attachments/assets/fb74a710-5f05-4bf4-b95f-10f40583c5a0 |
||||
|
||||
## Features |
||||
|
||||
- Supports all gvfs schemes/protocols (mtp, smb, ftp, sftp, nfs, gphoto2, afp, afc, sshfs, dav, davs, dav+sd, davs+sd, dns-sd) |
||||
- Mount hardware device or saved scheme/mount URI (use `--select-then-mount`) |
||||
- Auto-jump to mounted location after mount (use `select-then-mount --jump`) |
||||
- Unmount and eject hardware devices (use `select-then-unmount --eject`) |
||||
- Auto select the first device or saved scheme/mount URI if there is only one listed. |
||||
- Jump to mounted location (use `jump-to-device`) |
||||
- After jumped to mounted location, jump back to the previous location |
||||
with a single keybind. Make it easier to copy/paste files. (use `jump-back-prev-cwd`) |
||||
- Add/Edit/Remove scheme/mount URI (use `add-mount`, `edit-mount`, `remove-mount`). Check this for schemes/mount URI format: [schemes.html](<https://wiki.gnome.org/Projects(2f)gvfs(2f)schemes.html>) |
||||
- (Optional) Remember passwords using Keyring or Password Store (need `secret-tool` + `keyring` or `pass` + `gpg` installed) |
||||
|
||||
## Requirements |
||||
|
||||
1. [yazi >= 25.5.31](https://github.com/sxyazi/yazi) |
||||
|
||||
2. This plugin only supports Linux, and requires having [GLib](https://github.com/GNOME/glib), [gvfs](https://gitlab.gnome.org/GNOME/gvfs) (need D-Bus Session) |
||||
|
||||
```sh |
||||
# Ubuntu |
||||
sudo apt install gvfs libglib2.0-dev |
||||
|
||||
# Fedora |
||||
sudo dnf install gvfs gvfs-fuse |
||||
|
||||
# Arch |
||||
sudo pacman -S gvfs glib2 |
||||
|
||||
# Gentoo (Use elogind (openrc) or systemd) |
||||
# Add fuse USE flag to /etc/portage/package.use/gvfs.conf: |
||||
gnome-base/gvfs fuse |
||||
# Then run this to install gvfs: |
||||
sudo emerge -av gnome-base/gvfs |
||||
``` |
||||
|
||||
3. And other `gvfs` protocol packages, choose what you need, all of them are optional: |
||||
|
||||
```sh |
||||
# Ubuntu |
||||
# This included all protocols |
||||
sudo apt install gvfs-backends gvfs-libs |
||||
|
||||
# Fedora |
||||
sudo dnf install gvfs-afc gvfs-afp gvfs-archive gvfs-goa gvfs-gphoto2 gvfs-mtp gvfs-nfs gvfs-smb |
||||
|
||||
# Arch |
||||
sudo pacman -S gvfs-mtp gvfs-afc gvfs-google gvfs-gphoto2 gvfs-nfs gvfs-smb gvfs-afc gvfs-dnssd gvfs-goa gvfs-onedrive gvfs-wsdd |
||||
|
||||
# Gentoo |
||||
# Edit /etc/portage/package.use/gvfs.conf again, add more USE flags: |
||||
# https://wiki.gentoo.org/wiki/GVfs |
||||
gnome-base/gvfs afp gnome-online-accounts google ios mtp nfs onedrive samba zeroconf fuse gphoto2 |
||||
# Then run this to update gvfs: |
||||
sudo emerge -av gnome-base/gvfs |
||||
``` |
||||
|
||||
4. For headless session (non-active console, like connect to a computer via SSH, etc.) |
||||
If you see `GVFS.yazi can only run on DBUS session` error message, please refer to [HEADLESS_WORKAROUND.md](./HEADLESS_WORKAROUND.md) for a workaround. |
||||
|
||||
5. (Optional) Store passwords with Keyring or Password Store (secret-tool + keyring or pass + gpg) |
||||
There are two methods to securely store passwords. Please refer to [SECURE_SAVED_PASSWORD.md](./SECURE_SAVED_PASSWORD.md) for more information. |
||||
|
||||
6. Restart to make sure gvfs deamon is started. |
||||
|
||||
## Installation |
||||
|
||||
```sh |
||||
ya pkg add boydaihungst/gvfs |
||||
``` |
||||
|
||||
Modify your `~/.config/yazi/init.lua` to include (`setup` function is required): |
||||
|
||||
```lua |
||||
require("gvfs"):setup({ |
||||
-- (Optional) Allowed keys to select device. |
||||
which_keys = "1234567890qwertyuiopasdfghjklzxcvbnm-=[]\\;',./!@#$%^&*()_+{}|:\"<>?", |
||||
|
||||
-- (Optional) Table of blacklisted devices. These devices will be ignored in any actions |
||||
-- List of device properties to match, or a string to match the device name: |
||||
-- https://github.com/boydaihungst/gvfs.yazi/blob/master/main.lua#L144 |
||||
blacklist_devices = { { name = "Wireless Device", scheme = "mtp" }, { scheme = "file" }, "Device Name"}, |
||||
|
||||
-- (Optional) Save file. |
||||
-- Default: ~/.config/yazi/gvfs.private |
||||
save_path = os.getenv("HOME") .. "/.config/yazi/gvfs.private", |
||||
|
||||
-- (Optional) Save file for automount devices. Use with `automount-when-cd` action. |
||||
-- Default: ~/.config/yazi/gvfs_automounts.private |
||||
save_path_automounts = os.getenv("HOME") .. "/.config/yazi/gvfs_automounts.private", |
||||
|
||||
-- (Optional) Input box position. |
||||
-- Default: { "top-center", y = 3, w = 60 }, |
||||
-- Position, which is a table: |
||||
-- `1`: Origin position, available values: "top-left", "top-center", "top-right", |
||||
-- "bottom-left", "bottom-center", "bottom-right", "center", and "hovered". |
||||
-- "hovered" is the position of hovered file/folder |
||||
-- `x`: X offset from the origin position. |
||||
-- `y`: Y offset from the origin position. |
||||
-- `w`: Width of the input. |
||||
-- `h`: Height of the input. |
||||
input_position = { "center", y = 0, w = 60 }, |
||||
|
||||
-- (Optional) Select where to save passwords. |
||||
-- Default: nil |
||||
-- Available options: "keyring", "pass", or nil |
||||
password_vault = "keyring", |
||||
|
||||
-- (Optional) Only need if you set password_vault = "pass" |
||||
-- Read the guide at SECURE_SAVED_PASSWORD.md to get your key_grip |
||||
key_grip = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", |
||||
|
||||
-- (Optional) Auto-save password after mount. |
||||
-- Default: false |
||||
save_password_autoconfirm = true, |
||||
-- (Optional) mountpoint of gvfs. Default: /run/user/USER_ID/gvfs |
||||
-- On some system it could be ~/.gvfs |
||||
-- You can't decide this path, it will be created automatically. Only changed if you know where gvfs mountpoint is. |
||||
-- Use command `ps aux | grep gvfs` to search for gvfs process and get the mountpoint path. |
||||
-- root_mountpoint = (os.getenv("XDG_RUNTIME_DIR") or ("/run/user/" .. ya.uid())) .. "/gvfs" |
||||
}) |
||||
``` |
||||
|
||||
## Usage |
||||
|
||||
> [!NOTE] |
||||
> |
||||
> - Moving files to the Trash bin does not work with some protocols (e.g., Android MTP). Please use permanent delete instead. |
||||
> - Scheme/Mount URIs shouldn't contain password, because they are saved as plain text in `yazi/config/gvfs.private`. |
||||
> - Google Drive, One drive are created via GNOME Online Accounts (GOA). |
||||
> Guide to setup [GNOME_ONLINE_ACCOUNTS_GOA.md](./GNOME_ONLINE_ACCOUNTS_GOA.md) |
||||
> - MTP, GPhoto2, AFC, Hard disk/drive, fstab with x-gvfs-show mount option, Google Drive + One drive protocols are listed automatically. So you don't need to add them via `add-mount` command. |
||||
> For other protocols (smb, ftp, sftp, etc), use `add-mount` command to add [Schemes/Mount URI](<https://wiki.gnome.org/Projects(2f)gvfs(2f)schemes.html>). |
||||
> - There is a bug in Yazi that prevents mounted folders from refreshing after they are mounted/unmounted. |
||||
> If you encounter this issue, try opening a new tab or moving the cursor up and down to trigger a refresh. |
||||
> - Mount Windows bitlocker drives requires bitlocker password to unlock: https://aka.ms/myrecoverykey or https://support.microsoft.com/en-us/windows/find-your-bitlocker-recovery-key-6b71ad27-0b89-ea08-f143-056f5ab347d6 |
||||
> - Mount hard drives may require polkit rule to fix permission denied error. Refer to [HEADLESS_WORKAROUND.md](./HEADLESS_WORKAROUND.md) for a workaround. |
||||
|
||||
Add this to your `~/.config/yazi/keymap.toml`: |
||||
|
||||
```toml |
||||
[mgr] |
||||
prepend_keymap = [ |
||||
# Mount |
||||
{ on = [ "M", "m" ], run = "plugin gvfs -- select-then-mount", desc = "Select device then mount" }, |
||||
# or this if you want to jump to mountpoint after mounted |
||||
{ on = [ "M", "m" ], run = "plugin gvfs -- select-then-mount --jump", desc = "Select device to mount and jump to its mount point" }, |
||||
|
||||
# This will remount device under current working directory (cwd) |
||||
# -> cwd = /run/user/1000/gvfs/DEVICE_1/FOLDER_A |
||||
# -> device mountpoint = /run/user/1000/gvfs/DEVICE_1 |
||||
# -> remount this DEVIEC_1 if needed |
||||
{ on = [ "M", "R" ], run = "plugin gvfs -- remount-current-cwd-device", desc = "Remount device under cwd" }, |
||||
|
||||
{ on = [ "M", "u" ], run = "plugin gvfs -- select-then-unmount", desc = "Select device then unmount" }, |
||||
# Or this if you want to unmount and eject device. |
||||
# -> Ejected device can safely be removed. |
||||
# -> Ejecting a device will unmount all paritions/volumes under it. |
||||
# -> Fallback to normal unmount if not supported by device. |
||||
{ on = [ "M", "u" ], run = "plugin gvfs -- select-then-unmount --eject", desc = "Select device then eject" }, |
||||
|
||||
# Also support force unmount/eject. |
||||
# -> Ignore outstanding file operations when unmounting or ejecting |
||||
{ on = [ "M", "U" ], run = "plugin gvfs -- select-then-unmount --eject --force", desc = "Select device then force to eject/unmount" }, |
||||
|
||||
# Add Scheme/Mount URI: |
||||
# -> Available schemes: mtp, gphoto2, smb, sftp, ftp, nfs, dns-sd, dav, davs, dav+sd, davs+sd, afp, afc, sshfs |
||||
# -> Read more about the schemes here: https://wiki.gnome.org/Projects(2f)gvfs(2f)schemes.html |
||||
# -> Explain about the scheme: |
||||
# -> If it shows like this: {ftp,ftps,ftpis}://[user@]host[:port] |
||||
# -> All of the value within [] is optional. For values within {}, you must choose exactly one. All others are required. |
||||
# -> empty [user] or "anonymous" user is anonymous user in (ftp) |
||||
# -> ftp://anonymous@192.168.1.2:9999 -> skip user input step. |
||||
# -> ftp://192.168.1.2:9999 -> input empty value in user input box. |
||||
# -> Example: {ftp,ftps,ftpis}://[user@]host[:port] => ip and port: "ftp://myusername@192.168.1.2:9999" or domain: "ftps://myusername@github.com" |
||||
# -> More examples: smb://user@192.168.1.2/share, smb://WORKGROUP;user@192.168.1.2/share, sftp://user@192.168.1.2/, ftp://192.168.1.2/ |
||||
# !WARNING: - Scheme/Mount URI shouldn't contain password. |
||||
# - Google Drive, One drive are listed automatically via GNOME Online Accounts (GOA). Avoid adding them. |
||||
# - MTP, GPhoto2, AFC, Hard disk/drive, fstab with x-gvfs-show are also listed automatically. Avoid adding them. |
||||
# - SSH, SFTP, FTP(s), AFC, DNS_SD now support [/share]. For example: sftp://user@192.168.1.2/home/user_name -> /share = /home/user_name |
||||
# - ssh:// is alias for sftp://. |
||||
# -> {sftp,ssh}://[user@]host[:port]. Host can be Host alias in .ssh/config file, ip or domain. |
||||
# -> For example (home is Host alias in .ssh/config file: Host home): |
||||
# -> ssh://user_name@home/home/user_name -> this will mount root path, but jump to subfolder /home/user_name |
||||
# -> sftp://user_name@192.168.1.2/home/user_name -> same as above but with ip |
||||
# -> sftp://user_name@192.168.1.2:9999/home/user_name -> same as above but with ip and port |
||||
{ on = [ "M", "a" ], run = "plugin gvfs -- add-mount", desc = "Add a GVFS mount URI" }, |
||||
|
||||
# Edit a Scheme/Mount URI |
||||
# -> Will clear saved passwords for that mount URI. |
||||
{ on = [ "M", "e" ], run = "plugin gvfs -- edit-mount", desc = "Edit a GVFS mount URI" }, |
||||
|
||||
# Remove a Scheme/Mount URI |
||||
# -> Will clear saved passwords for that mount URI. |
||||
{ on = [ "M", "r" ], run = "plugin gvfs -- remove-mount", desc = "Remove a GVFS mount URI" }, |
||||
|
||||
# Jump |
||||
{ on = [ "g", "m" ], run = "plugin gvfs -- jump-to-device", desc = "Select device then jump to its mount point" }, |
||||
# If you use `x-systemd.automount` in /etc/fstab or manually added automount unit, |
||||
# then you can use `--automount` argument to auto mount device before jump. |
||||
# Otherwise it won't show up in the jump list. |
||||
{ on = [ "g", "m" ], run = "plugin gvfs -- jump-to-device --automount", desc = "Automount then select device to jump to its mount point" }, |
||||
{ on = [ "`", "`" ], run = "plugin gvfs -- jump-back-prev-cwd", desc = "Jump back to the position before jumped to device" }, |
||||
|
||||
# Automount (This is different from `x-systemd.automount` in /etc/fstab) |
||||
# -> Hover over any file/folder under a mounted device then run `automount-when-cd` action to enable automount when cd/jump for that device. |
||||
# -> When you cd/jump to unmounted device mountpoint or its sub folder, this will auto-mount the device before jump. |
||||
# -> Works with any command or any bookmark plugin that change cwd. For example, use `yamb` to add bookmarks and jump to them, use yazi's built-in `cd` `back` `forward` commands: |
||||
|
||||
# -> { on = [ "m", "a" ], run = [ "plugin yamb -- save", "plugin gvfs -- automount-when-cd" ], desc = "Add bookmark and enable automount when cd"} |
||||
{ on = [ "M", "t" ], run = "plugin gvfs -- automount-when-cd", desc = "Enable automount when cd to device under cwd" }, |
||||
{ on = [ "M", "T" ], run = "plugin gvfs -- automount-when-cd --disabled", desc = "Disable automount when cd to device under cwd" }, |
||||
] |
||||
``` |
||||
|
||||
It's highly recommended to add these lines to your `~/.config/yazi/yazi.toml`, |
||||
because GVFS is slow that can make yazi freeze when it preloads or previews a large number of files. |
||||
Especially when you use `Google-drive` or `One-drive`. |
||||
|
||||
- Replace `1000` with your real user id (run `id -u` to get user id). |
||||
- Replace `USER_NAME` with your real user name (run `whoami` to get username). |
||||
|
||||
> [!IMPORTANT] |
||||
> |
||||
> For yazi (>=v25.12.29) replace `name` with `url` |
||||
|
||||
```toml |
||||
[plugin] |
||||
prepend_preloaders = [ |
||||
# Do not preload files in mounted locations: |
||||
# Environment variable won't work here. |
||||
# Using absolute path instead. |
||||
{ name = "/run/user/1000/gvfs/**/*", run = "noop" }, |
||||
|
||||
# For mounted hard disk/drive |
||||
{ name = "/run/media/USER_NAME/**/*", run = "noop" }, |
||||
] |
||||
prepend_previewers = [ |
||||
# Allow to preview folder. |
||||
{ name = "*/", run = "folder" }, |
||||
|
||||
# Do not previewing files in mounted locations. |
||||
# Uncomment the line below to allow previewing text files. |
||||
# { mime = "{text/*,application/x-subrip}", run = "code" }, |
||||
|
||||
# Using absolute path. |
||||
{ name = "/run/user/1000/gvfs/**/*", run = "noop" }, |
||||
|
||||
# For mounted hard disk/drive. |
||||
{ name = "/run/media/USER_NAME/**/*", run = "noop" }, |
||||
] |
||||
``` |
||||
|
||||
## Note for mounting using fstab |
||||
|
||||
If you are using fstab to mount, you need to add `x-gvfs-show` to the mount options. |
||||
And with it you can only use `jump-to-device` and `jump-back-prev-cwd` actions. |
||||
|
||||
- Example `/etc/fstab`: |
||||
- Mount on demand (manually mount): |
||||
|
||||
``` |
||||
192.168.1.10/hdd /mnt/myshare cifs noauto,credentials=/etc/samba/credentials,x-gvfs-show,iocharset=utf8,uid=1000,gid=1000,file_mode=0660,dir_mode=0770,nofail 0 0 |
||||
UUID=XXXX-XXXX /mnt/myshare2 exfat noauto,defaults,x-gvfs-show 0 0 |
||||
``` |
||||
|
||||
- Mount on access, add `x-systemd-automount` to the mount options: |
||||
|
||||
Use `jump-to-device --automount`. |
||||
This will auto mount all mount entries that have `x-systemd-automount`, before jump to. |
||||
|
||||
- Mount at boot, remove `noauto` from the mount options |
||||
|
||||
Reload fstab: |
||||
|
||||
```sh |
||||
sudo systemctl daemon-reload && sudo systemctl restart local-fs.target && sudo mount -a |
||||
``` |
||||
|
||||
If you changed mount options (like uid=, gid=, umask=, exfat, ntfs, etc.), already-mounted filesystems won't update unless you unmount and remount them. |
||||
You can manually remount a specific entry with these commands: |
||||
|
||||
```sh |
||||
sudo umount /mnt/myshare |
||||
sudo mount -a |
||||
``` |
||||
|
||||
## Troubleshooting |
||||
|
||||
If you have any problems with one of the protocols, please manually mount it using `gio mount Scheme/Mount URI`. See the [list of supported schemes](<https://wiki.gnome.org/Projects(2f)gvfs(2f)schemes.html>). |
||||
Then create an issue ticket and include the output of `gio mount -li` along with the list of mount points under `/run/user/1000/gvfs/` and `/run/media/USERNAME` |
||||
@ -0,0 +1,23 @@
|
||||
#!/bin/sh |
||||
|
||||
# Iterate over all automount units |
||||
for unit in $(systemctl list-units --type=automount --no-legend | awk '{print $1}'); do |
||||
# Extract the mount path from the unit name |
||||
path=$(systemctl show -p Where --value "$unit") |
||||
|
||||
# Skip certain system directories |
||||
case "$path" in |
||||
/proc* | /sys* | /dev*) continue ;; |
||||
esac |
||||
|
||||
# Derive the corresponding .mount unit name from the path |
||||
mount_unit=$(systemd-escape -p --suffix=mount "$path") |
||||
|
||||
# Check if the .mount unit is active (i.e., the filesystem is mounted) |
||||
if systemctl is-active --quiet "$mount_unit"; then |
||||
continue |
||||
fi |
||||
|
||||
# If not mounted, log and then access the directory to trigger the automount |
||||
ls "$path" >/dev/null 2>&1 & |
||||
done |
||||
@ -0,0 +1,9 @@
|
||||
[Unit] |
||||
Description=GNOME Keyring Daemon |
||||
|
||||
[Service] |
||||
ExecStart=/usr/bin/gnome-keyring-daemon --foreground --components=secrets,pkcs11,ssh |
||||
Restart=on-failure |
||||
|
||||
[Install] |
||||
WantedBy=default.target |
||||
File diff suppressed because it is too large
Load Diff
@ -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. |
||||
@ -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" |
||||
``` |
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins. |
||||
|
||||
## License |
||||
|
||||
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. |
||||
@ -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, |
||||
} |
||||
@ -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. |
||||
@ -0,0 +1,50 @@
|
||||
# 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" |
||||
``` |
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins. |
||||
|
||||
## Actions |
||||
|
||||
| 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. |
||||
@ -0,0 +1,71 @@
|
||||
local M = {} |
||||
|
||||
--- @param type "mount"|"unmount"|"eject" |
||||
--- @param partition table |
||||
function M.operate(type, partition) |
||||
if not partition then |
||||
return |
||||
elseif not partition.sub then |
||||
return -- TODO: mount/unmount main disk |
||||
end |
||||
|
||||
local cmd, output, err |
||||
if ya.target_os() == "macos" then |
||||
cmd, output, err = "diskutil", M.diskutil(type, partition.src) |
||||
elseif ya.target_os() == "linux" then |
||||
if type == "eject" and partition.src:match("^/dev/sr%d+") then |
||||
M.udisksctl("unmount", partition.src) |
||||
cmd, output, err = "eject", M.eject(partition.src) |
||||
elseif type == "eject" then |
||||
M.udisksctl("unmount", partition.src) |
||||
cmd, output, err = "udisksctl", M.udisksctl("power-off", partition.src) |
||||
else |
||||
cmd, output, err = "udisksctl", M.udisksctl(type, partition.src) |
||||
end |
||||
end |
||||
|
||||
if not cmd then |
||||
M.fail("mount.yazi is not currently supported on your platform") |
||||
elseif not output then |
||||
M.fail("Failed to spawn `%s`: %s", cmd, err) |
||||
elseif not output.status.success then |
||||
M.fail("Failed to %s `%s`: %s", type, partition.src, output.stderr) |
||||
end |
||||
end |
||||
|
||||
--- @param type "mount"|"unmount"|"eject" |
||||
--- @param src string |
||||
--- @return Output? output |
||||
--- @return Error? err |
||||
function M.diskutil(type, src) return Command("diskutil"):arg({ type, src }):output() end |
||||
|
||||
--- @param type "mount"|"unmount"|"power-off" |
||||
--- @param src string |
||||
--- @return Output? output |
||||
--- @return Error? err |
||||
function M.udisksctl(type, src) |
||||
local args = { type, "-b", src, "--no-user-interaction" } |
||||
local output, err = Command("udisksctl"):arg(args):output() |
||||
|
||||
if not output or err then |
||||
return nil, err |
||||
elseif output.stderr:find("org.freedesktop.UDisks2.Error.NotAuthorizedCanObtain", 1, true) then |
||||
local tx, rx = table.unpack(require(".main").permit) |
||||
tx:send(true) |
||||
ya.emit("which:dismiss", {}) |
||||
local output, err = require(".sudo").run_with_sudo("udisksctl", args) |
||||
rx:recv() |
||||
return output, err |
||||
else |
||||
return output |
||||
end |
||||
end |
||||
|
||||
--- @param src string |
||||
--- @return Output? output |
||||
--- @return Error? err |
||||
function M.eject(src) return Command("eject"):arg({ "--traytoggle", src }):output() end |
||||
|
||||
function M.fail(...) ya.notify { title = "Mount", content = string.format(...), timeout = 10, level = "error" } end |
||||
|
||||
return M |
||||
@ -0,0 +1,272 @@
|
||||
--- @since 26.5.6 |
||||
|
||||
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 |
||||
ui.render() |
||||
end) |
||||
|
||||
local subscribe = ya.sync(function() |
||||
ps.unsub("mount") |
||||
ps.sub("mount", function() |
||||
-- TODO: use `ya.async()` |
||||
ya.emit("plugin", { "mount", "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)) |
||||
ui.render() |
||||
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 |
||||
ui.render() |
||||
end) |
||||
|
||||
local M = { |
||||
keys = { |
||||
{ on = "q", run = "quit" }, |
||||
{ on = "<Esc>", run = "quit" }, |
||||
{ on = "<Enter>", run = { "enter", "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" }, |
||||
}, |
||||
permit = table.pack(ya.chan("mpsc", 1)), |
||||
} |
||||
|
||||
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() |
||||
self.permit[1]:send(true) |
||||
while true do |
||||
self.permit[2]:recv() |
||||
local idx = ya.which { cands = self.keys, silent = true } |
||||
self.permit[1]:send(true) |
||||
|
||||
local cand = self.keys[idx] 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 |
||||
require(".cross").operate("mount", active_partition()) |
||||
elseif run == "unmount" then |
||||
require(".cross").operate("unmount", active_partition()) |
||||
elseif run == "eject" then |
||||
require(".cross").operate("eject", active_partition()) |
||||
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(20), |
||||
}, |
||||
} |
||||
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 |
||||
{ "^/dev/fd%d+", ".+$" }, -- /dev/fd0 |
||||
{ "^/dev/md%d+", "p%d+$" }, -- /dev/md0p1 |
||||
{ "^/dev/nbd%d+", "p%d+$" }, -- /dev/nbd0p1 |
||||
{ "^/dev/bcache%d+", "p%d+$" }, -- /dev/bcache0p1 |
||||
{ "^/dev/mapper/", ".+$" }, -- /dev/mapper/<name> |
||||
} |
||||
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:click() end |
||||
|
||||
function M:scroll() end |
||||
|
||||
function M:touch() end |
||||
|
||||
return M |
||||
@ -0,0 +1,54 @@
|
||||
local M = {} |
||||
|
||||
--- Verify if `sudo` is already authenticated |
||||
--- @return boolean |
||||
--- @return Error? |
||||
function M.sudo_already() |
||||
local status, err = Command("sudo"):arg({ "--validate", "--non-interactive" }):status() |
||||
return status and status.success or false, err |
||||
end |
||||
|
||||
--- Run a program with `sudo` privilege |
||||
--- @param program string |
||||
--- @param args table |
||||
--- @return Output? output |
||||
--- @return Error? err |
||||
function M.run_with_sudo(program, args) |
||||
local cmd = Command("sudo") |
||||
:arg({ "--stdin", "--user", "#" .. ya.uid(), "--", program }) |
||||
:arg(args) |
||||
:stdin(Command.PIPED) |
||||
:stdout(Command.PIPED) |
||||
:stderr(Command.PIPED) |
||||
|
||||
if M.sudo_already() then |
||||
return cmd:output() |
||||
end |
||||
|
||||
local value, event = ya.input { |
||||
pos = { "top-center", y = 3, w = 40 }, |
||||
title = string.format("Password for `sudo %s`:", program), |
||||
obscure = true, |
||||
} |
||||
if not value or event ~= 1 then |
||||
return nil, Err("Sudo password input cancelled") |
||||
end |
||||
|
||||
local child, err = cmd:spawn() |
||||
if not child or err then |
||||
return nil, err |
||||
end |
||||
|
||||
child:write_all(value .. "\n") |
||||
child:flush() |
||||
local output, err = child:wait_with_output() |
||||
if not output or err then |
||||
return nil, err |
||||
elseif output.status.success or M.sudo_already() then |
||||
return output |
||||
else |
||||
return nil, Err("Incorrect sudo password") |
||||
end |
||||
end |
||||
|
||||
return M |
||||
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2024 Lauri Niskanen |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
||||
@ -0,0 +1,25 @@
|
||||
# open-with-cmd.yazi |
||||
|
||||
This is a Yazi plugin for opening files with a prompted command. |
||||
|
||||
## Installation |
||||
|
||||
Install the plugin: |
||||
|
||||
``` |
||||
ya pkg add Ape/open-with-cmd |
||||
``` |
||||
|
||||
Create `~/.config/yazi/keymap.toml` and add: |
||||
|
||||
``` |
||||
[[manager.prepend_keymap]] |
||||
on = "o" |
||||
run = "plugin open-with-cmd -- block" |
||||
desc = "Open with command in the terminal" |
||||
|
||||
[[manager.prepend_keymap]] |
||||
on = "O" |
||||
run = "plugin open-with-cmd" |
||||
desc = "Open with command" |
||||
``` |
||||
@ -0,0 +1,19 @@
|
||||
return { |
||||
entry = function(_, job) |
||||
local block = job.args[1] and job.args[1] == "block" |
||||
|
||||
local value, event = ya.input({ |
||||
title = block and "Open with (block):" or "Open with:", |
||||
pos = { "hovered", y = 1, w = 50 }, |
||||
}) |
||||
|
||||
if event == 1 then |
||||
local s = ya.target_family() == "windows" and " %*" or ' "$@"' |
||||
ya.emit("shell", { |
||||
value .. s, |
||||
block = block, |
||||
orphan = not block, |
||||
}) |
||||
end |
||||
end, |
||||
} |
||||
@ -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. |
||||
@ -0,0 +1,102 @@
|
||||
# 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]] |
||||
url = "*" |
||||
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]] |
||||
url = "*.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]] |
||||
url = "*.csv" |
||||
run = 'piper -- bat -p --color=always "$1"' |
||||
``` |
||||
|
||||
Note that certain distributions might use a different name than `bat`, like Debian and Ubuntu use `batcat`, so please adjust accordingly. |
||||
|
||||
### Preview Markdown with [`glow`](https://github.com/charmbracelet/glow) |
||||
|
||||
```toml |
||||
[[plugin.prepend_previewers]] |
||||
url = "*.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]] |
||||
url = "*/" |
||||
run = 'piper -- eza -TL=3 --color=always --icons=always --group-directories-first --no-quotes "$1"' |
||||
``` |
||||
|
||||
### Preview the schema of a SQLite database |
||||
|
||||
```toml |
||||
[[plugin.prepend_previewers]] |
||||
mime = "application/sqlite3" |
||||
run = 'piper -- sqlite3 "$1" ".schema --indent"' |
||||
``` |
||||
|
||||
### 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]] |
||||
url = "*" |
||||
run = 'piper -- hexyl --border=none --terminal-width=$w "$1"' |
||||
``` |
||||
|
||||
## Related projects |
||||
|
||||
[`faster-piper.yazi`](https://github.com/alberti42/faster-piper.yazi): a cache-based, scrolling-optimized rewrite compatible with `piper.yazi`. |
||||
|
||||
## License |
||||
|
||||
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. |
||||
@ -0,0 +1,71 @@
|
||||
--- @since 26.1.22 |
||||
|
||||
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.path) }) |
||||
:env("w", job.area.w) |
||||
:env("h", job.area.h) |
||||
:env("CYGWIN", "noupcaseenv") |
||||
: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("\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 { mode = tonumber(lines[i]:sub(-1) == "/" and "40700" or "100644", 8) }, |
||||
}):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 |
||||
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2024 dedukun |
||||
|
||||
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. |
||||
@ -0,0 +1,145 @@
|
||||
# relative-motions.yazi |
||||
|
||||
A [Yazi](https://github.com/sxyazi/yazi) plugin based about vim motions. |
||||
|
||||
<https://github.com/dedukun/relative-motions.yazi/assets/25795432/04fb186a-5efe-442d-8d7b-2dccb6eee408> |
||||
|
||||
## Requirements |
||||
|
||||
- [Yazi](https://github.com/sxyazi/yazi) v25.5.28+ |
||||
|
||||
## Installation |
||||
|
||||
```sh |
||||
ya pkg add dedukun/relative-motions |
||||
``` |
||||
|
||||
## Configuration |
||||
|
||||
If you want to use the numbers directly to start a motion add this to your `keymap.toml`: |
||||
|
||||
<details> |
||||
|
||||
```toml |
||||
[[mgr.prepend_keymap]] |
||||
on = [ "1" ] |
||||
run = "plugin relative-motions 1" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "2" ] |
||||
run = "plugin relative-motions 2" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "3" ] |
||||
run = "plugin relative-motions 3" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "4" ] |
||||
run = "plugin relative-motions 4" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "5" ] |
||||
run = "plugin relative-motions 5" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "6" ] |
||||
run = "plugin relative-motions 6" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "7" ] |
||||
run = "plugin relative-motions 7" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "8" ] |
||||
run = "plugin relative-motions 8" |
||||
desc = "Move in relative steps" |
||||
|
||||
[[mgr.prepend_keymap]] |
||||
on = [ "9" ] |
||||
run = "plugin relative-motions 9" |
||||
desc = "Move in relative steps" |
||||
``` |
||||
|
||||
</details> |
||||
|
||||
Alternatively you can use a key to trigger a new motion without any initial value, for that add the following in `keymap.toml`: |
||||
|
||||
```toml |
||||
[[mgr.prepend_keymap]] |
||||
on = [ "m" ] |
||||
run = "plugin relative-motions" |
||||
desc = "Trigger a new relative motion" |
||||
``` |
||||
|
||||
--- |
||||
|
||||
Additionally there are a couple of initial configurations that can be given to the plugin's `setup` function: |
||||
|
||||
| Configuration | Values | Default | Description | |
||||
| -------------- | ----------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- | |
||||
| `show_numbers` | `relative`, `absolute`, `relative_absolute` or `none` | `none` | Shows relative or absolute numbers before the file icon | |
||||
| `show_motion` | `true` or `false` | `false` | Shows current motion in Status bar | |
||||
| `only_motions` | `true` or `false` | `false` | If true, only the motion movements will be enabled, i.e., the commands for delete, cut, yank and visual selection will be disabled | |
||||
| `enter_mode` | `cache`, `first` or `cache_or_first` | `cache_or_first` | The method to enter folders | |
||||
|
||||
If you want, for example, to enable relative numbers as well as to show the motion in the status bar, |
||||
add the following to Yazi's `init.lua`, i.e. `~/.config/yazi/init.lua`: |
||||
|
||||
```lua |
||||
-- ~/.config/yazi/init.lua |
||||
require("relative-motions"):setup({ show_numbers="relative", show_motion = true, enter_mode ="first" }) |
||||
``` |
||||
|
||||
> [!NOTE] |
||||
> The `show_numbers` and `show_motion` functionalities overwrite [`Current:redraw`](https://github.com/sxyazi/yazi/blob/e3c91115a2c096724303a0b364e7625691e4beba/yazi-plugin/preset/components/current.lua#L28) |
||||
> and [`Status:children_redraw`](https://github.com/sxyazi/yazi/blob/e3c91115a2c096724303a0b364e7625691e4beba/yazi-plugin/preset/components/status.lua#L177) respectively. |
||||
> If you have custom implementations for any of this functions |
||||
> you can add the provided `Entity:number` and `Status:motion` to your implementations, just check [here](https://github.com/dedukun/relative-motions.yazi/blob/main/init.lua#L126) how we are doing things. |
||||
|
||||
## Usage |
||||
|
||||
This plugin adds the some basic vim motions like `3k`, `12j`, `10gg`, etc. |
||||
The following table show all the available motions: |
||||
|
||||
| Command | Description | |
||||
| -------------- | --------------------- | |
||||
| `j`/`<Down>` | Move `n` lines down | |
||||
| `k`/`<Up>` | Move `n` lines up | |
||||
| `h`/`<Left>` | Move `n` folders back | |
||||
| `l`/`<Right>` | Enter `n` folders | |
||||
| `gj`/`g<Down>` | Go `n` lines down | |
||||
| `gk`/`g<Up>` | Go `n` lines up | |
||||
| `gg` | Go to line | |
||||
|
||||
Furthermore, the following operations were also added: |
||||
|
||||
| Command | Description | |
||||
| ------- | ------------- | |
||||
| `v` | visual select | |
||||
| `y` | Yank | |
||||
| `x` | Cut | |
||||
| `d` | Delete motion | |
||||
|
||||
This however must be followed by a direction, which can be `j`/`<Down>`, `k`/`<Up>` or repeating the command key, |
||||
which will operate from the cursor down, e.g. `2yy` will copy two files. |
||||
|
||||
Finally, we also support some tab operations: |
||||
|
||||
| Command | Description | |
||||
| ------- | ------------------------------------ | |
||||
| `t` | create `n` tabs | |
||||
| `H` | Move `n` tabs left | |
||||
| `L` | Move `n` tabs right | |
||||
| `gt` | Go to the `n` tab | |
||||
| `w` | Close tab `n` | |
||||
| `W` | Close `n` tabs right | |
||||
| `<` | Swap current tab with `n` tabs left | |
||||
| `>` | Swap current tab with `n` tabs right | |
||||
| `~` | Swap current tab with tab `n` | |
||||
@ -0,0 +1,406 @@
|
||||
--- @since 25.5.28 |
||||
-- stylua: ignore |
||||
local MOTIONS_AND_OP_KEYS = { |
||||
{ on = "0" }, { on = "1" }, { on = "2" }, { on = "3" }, { on = "4" }, |
||||
{ on = "5" }, { on = "6" }, { on = "7" }, { on = "8" }, { on = "9" }, |
||||
-- commands |
||||
{ on = "d" }, { on = "v" }, { on = "y" }, { on = "x" }, |
||||
-- tab commands |
||||
{ on = "t" }, { on = "L" }, { on = "H" }, { on = "w" }, |
||||
{ on = "W" }, { on = "<" }, { on = ">" }, { on = "~" }, |
||||
-- movement |
||||
{ on = "g" }, { on = "j" }, { on = "k" }, { on = "h" }, { on = "l" }, { on = "<Down>" }, { on = "<Up>" }, { on = "<Left>" }, { on = "<Right>" } |
||||
} |
||||
|
||||
-- stylua: ignore |
||||
local MOTION_KEYS = { |
||||
{ on = "0" }, { on = "1" }, { on = "2" }, { on = "3" }, { on = "4" }, |
||||
{ on = "5" }, { on = "6" }, { on = "7" }, { on = "8" }, { on = "9" }, |
||||
-- movement |
||||
{ on = "g" }, { on = "j" }, { on = "k" }, { on = "h" }, { on = "l" }, { on = "<Down>" }, { on = "<Up>" }, { on = "<Left>" }, { on = "<Right>" } |
||||
} |
||||
|
||||
-- stylua: ignore |
||||
local DIRECTION_KEYS = { |
||||
{ on = "j" }, { on = "k" }, { on = "<Down>" }, { on = "<Up>" }, |
||||
-- tab movement |
||||
{ on = "t" } |
||||
} |
||||
|
||||
local SHOW_NUMBERS_ABSOLUTE = 0 |
||||
local SHOW_NUMBERS_RELATIVE = 1 |
||||
local SHOW_NUMBERS_RELATIVE_ABSOLUTE = 2 |
||||
|
||||
local ENTER_MODE_FIRST = 0 |
||||
local ENTER_MODE_CACHE = 1 |
||||
local ENTER_MODE_CACHE_OR_FIRST = 2 |
||||
|
||||
----------------------------------------------- |
||||
----------------- R E N D E R ----------------- |
||||
----------------------------------------------- |
||||
|
||||
local render_motion_setup = ya.sync(function(_) |
||||
if ui.render then |
||||
ui.render() |
||||
else |
||||
ya.render() |
||||
end |
||||
|
||||
Status.motion = function() return ui.Span("") end |
||||
|
||||
Status.children_redraw = function(self, side) |
||||
local lines = {} |
||||
if side == self.RIGHT then |
||||
lines[1] = self:motion(self) |
||||
end |
||||
for _, c in ipairs(side == self.RIGHT and self._right or self._left) do |
||||
lines[#lines + 1] = (type(c[1]) == "string" and self[c[1]] or c[1])(self) |
||||
end |
||||
return ui.Line(lines) |
||||
end |
||||
|
||||
-- TODO: check why it doesn't work line this |
||||
-- Status:children_add(function() return ui.Span("") end, 1000, Status.RIGHT) |
||||
end) |
||||
|
||||
local render_motion = ya.sync(function(_, motion_num, motion_cmd) |
||||
if ui.render then |
||||
ui.render() |
||||
else |
||||
ya.render() |
||||
end |
||||
|
||||
Status.motion = function(self) |
||||
if not motion_num then |
||||
return ui.Span("") |
||||
end |
||||
|
||||
local style = self:style() |
||||
|
||||
local motion_span |
||||
if not motion_cmd then |
||||
motion_span = ui.Span(string.format(" %3d ", motion_num)) |
||||
else |
||||
motion_span = ui.Span(string.format(" %3d%s ", motion_num, motion_cmd)) |
||||
end |
||||
|
||||
local status_config = th.status |
||||
local separator_open = status_config.sep_right.open |
||||
local separator_close = status_config.sep_right.close |
||||
|
||||
-- TODO: REMOVE THIS IN NEXT RELEASE |
||||
local bg_style |
||||
if type(style.main.bg) == "function" then |
||||
bg_style = style.main:bg() |
||||
else |
||||
bg_style = style.main.bg |
||||
end |
||||
|
||||
return ui.Line { |
||||
ui.Span(separator_open):fg(bg_style), |
||||
motion_span:style(style.main), |
||||
ui.Span(separator_close):fg(bg_style), |
||||
ui.Span(" "), |
||||
} |
||||
end |
||||
end) |
||||
|
||||
local render_numbers = ya.sync(function(_, mode) |
||||
if ui.render then |
||||
ui.render() |
||||
else |
||||
ya.render() |
||||
end |
||||
|
||||
Entity.number = function(_, index, total, file, hovered) |
||||
local idx |
||||
if mode == SHOW_NUMBERS_RELATIVE then |
||||
idx = math.abs(hovered - index) |
||||
elseif mode == SHOW_NUMBERS_ABSOLUTE then |
||||
idx = file.idx |
||||
else -- SHOW_NUMBERS_RELATIVE_ABSOLUTE |
||||
if hovered == index then |
||||
idx = file.idx |
||||
else |
||||
idx = math.abs(hovered - index) |
||||
end |
||||
end |
||||
|
||||
local num_format = "%" .. #tostring(total) .. "d" |
||||
|
||||
-- emulate vim's hovered offset |
||||
if hovered == index then |
||||
return ui.Span(string.format(num_format .. " ", idx)) |
||||
else |
||||
return ui.Span(string.format(" " .. num_format, idx)) |
||||
end |
||||
end |
||||
|
||||
Current.redraw = function(self) |
||||
local files = self._folder.window |
||||
if #files == 0 then |
||||
return self:empty() |
||||
end |
||||
|
||||
local hovered_index |
||||
for i, f in ipairs(files) do |
||||
if f.is_hovered then |
||||
hovered_index = i |
||||
break |
||||
end |
||||
end |
||||
|
||||
local entities, linemodes = {}, {} |
||||
for i, f in ipairs(files) do |
||||
linemodes[#linemodes + 1] = Linemode:new(f):redraw() |
||||
|
||||
local entity = Entity:new(f) |
||||
entities[#entities + 1] = ui.Line({ Entity:number(i, #self._folder.files, f, hovered_index), entity:redraw() }) |
||||
:style(entity:style()) |
||||
end |
||||
|
||||
return { |
||||
ui.List(entities):area(self._area), |
||||
ui.Text(linemodes):area(self._area):align(ui.Align.RIGHT), |
||||
} |
||||
end |
||||
end) |
||||
|
||||
local function render_clear() render_motion() end |
||||
|
||||
----------------------------------------------- |
||||
--------- C O M M A N D P A R S E R --------- |
||||
----------------------------------------------- |
||||
|
||||
local get_keys = ya.sync(function(state) return state._only_motions and MOTION_KEYS or MOTIONS_AND_OP_KEYS end) |
||||
|
||||
local function normal_direction(dir) |
||||
if dir == "<Down>" then |
||||
return "j" |
||||
elseif dir == "<Up>" then |
||||
return "k" |
||||
elseif dir == "<Left>" then |
||||
return "h" |
||||
elseif dir == "<Right>" then |
||||
return "l" |
||||
end |
||||
return dir |
||||
end |
||||
|
||||
local function get_cmd(first_char, keys) |
||||
local last_key |
||||
local lines = first_char or "" |
||||
|
||||
while true do |
||||
render_motion(tonumber(lines)) |
||||
local key = ya.which { cands = keys, silent = true } |
||||
if not key then |
||||
return nil, nil, nil |
||||
end |
||||
|
||||
last_key = keys[key].on |
||||
if not tonumber(last_key) then |
||||
last_key = normal_direction(last_key) |
||||
break |
||||
end |
||||
|
||||
lines = lines .. last_key |
||||
end |
||||
|
||||
render_motion(tonumber(lines), last_key) |
||||
|
||||
-- command direction |
||||
local direction |
||||
if last_key == "g" or last_key == "v" or last_key == "d" or last_key == "y" or last_key == "x" then |
||||
DIRECTION_KEYS[#DIRECTION_KEYS + 1] = { |
||||
on = last_key, |
||||
} |
||||
local direction_key = ya.which { cands = DIRECTION_KEYS, silent = true } |
||||
if not direction_key then |
||||
return nil, nil, nil |
||||
end |
||||
|
||||
direction = DIRECTION_KEYS[direction_key].on |
||||
direction = normal_direction(direction) |
||||
end |
||||
|
||||
return tonumber(lines), last_key, direction |
||||
end |
||||
|
||||
local function is_tab_command(command) |
||||
local tab_commands = { "t", "L", "H", "w", "W", "<", ">", "~" } |
||||
for _, cmd in ipairs(tab_commands) do |
||||
if command == cmd then |
||||
return true |
||||
end |
||||
end |
||||
return false |
||||
end |
||||
|
||||
local get_active_tab = ya.sync(function(_) return cx.tabs.idx end) |
||||
|
||||
local get_cache_or_first_dir = ya.sync(function(state) |
||||
if state._enter_mode == ENTER_MODE_CACHE then |
||||
return nil |
||||
elseif state._enter_mode == ENTER_MODE_CACHE_OR_FIRST then |
||||
local hovered_file = cx.active.current.hovered |
||||
|
||||
if hovered_file ~= nil and hovered_file.cha.is_dir then |
||||
return cx.active.current.cursor |
||||
end |
||||
end |
||||
|
||||
local files = cx.active.current.files |
||||
local index = 1 |
||||
|
||||
for i = 1, #files do |
||||
if files[i].cha.is_dir then |
||||
index = i |
||||
break |
||||
end |
||||
end |
||||
|
||||
return index - 1 |
||||
end) |
||||
----------------------------------------------- |
||||
---------- E N T R Y / S E T U P ---------- |
||||
----------------------------------------------- |
||||
|
||||
return { |
||||
entry = function(_, job) |
||||
local initial_value |
||||
|
||||
local args = job.args |
||||
-- this is checking if the argument is a valid number |
||||
if #args > 0 then |
||||
initial_value = tostring(tonumber(args[1])) |
||||
if initial_value == "nil" then |
||||
return |
||||
end |
||||
end |
||||
|
||||
local lines, cmd, direction = get_cmd(initial_value, get_keys()) |
||||
if not lines or not cmd then |
||||
-- command was cancelled |
||||
render_clear() |
||||
return |
||||
end |
||||
|
||||
if cmd == "g" then |
||||
if direction == "g" then |
||||
ya.mgr_emit("arrow", { "top" }) |
||||
ya.mgr_emit("arrow", { lines - 1 }) |
||||
render_clear() |
||||
return |
||||
elseif direction == "j" then |
||||
cmd = "j" |
||||
elseif direction == "k" then |
||||
cmd = "k" |
||||
elseif direction == "t" then |
||||
ya.mgr_emit("tab_switch", { lines - 1 }) |
||||
render_clear() |
||||
return |
||||
else |
||||
-- no valid direction |
||||
render_clear() |
||||
return |
||||
end |
||||
end |
||||
|
||||
if cmd == "j" then |
||||
ya.mgr_emit("arrow", { lines }) |
||||
elseif cmd == "k" then |
||||
ya.mgr_emit("arrow", { -lines }) |
||||
elseif cmd == "h" then |
||||
for _ = 1, lines do |
||||
ya.mgr_emit("leave", {}) |
||||
end |
||||
elseif cmd == "l" then |
||||
for _ = 1, lines do |
||||
ya.mgr_emit("enter", {}) |
||||
local file_idx = get_cache_or_first_dir() |
||||
if file_idx then |
||||
ya.mgr_emit("arrow", { "top" }) |
||||
ya.mgr_emit("arrow", { file_idx }) |
||||
end |
||||
end |
||||
elseif is_tab_command(cmd) then |
||||
if cmd == "t" then |
||||
for _ = 1, lines do |
||||
ya.mgr_emit("tab_create", {}) |
||||
end |
||||
elseif cmd == "H" then |
||||
ya.mgr_emit("tab_switch", { -lines, relative = true }) |
||||
elseif cmd == "L" then |
||||
ya.mgr_emit("tab_switch", { lines, relative = true }) |
||||
elseif cmd == "w" then |
||||
ya.mgr_emit("tab_close", { lines - 1 }) |
||||
elseif cmd == "W" then |
||||
local curr_tab = get_active_tab() |
||||
local del_tab = curr_tab + lines - 1 |
||||
for _ = curr_tab, del_tab do |
||||
ya.mgr_emit("tab_close", { curr_tab - 1 }) |
||||
end |
||||
ya.mgr_emit("tab_switch", { curr_tab - 1 }) |
||||
elseif cmd == "<" then |
||||
ya.mgr_emit("tab_swap", { -lines }) |
||||
elseif cmd == ">" then |
||||
ya.mgr_emit("tab_swap", { lines }) |
||||
elseif cmd == "~" then |
||||
local jump = lines - get_active_tab() |
||||
ya.mgr_emit("tab_swap", { jump }) |
||||
end |
||||
else |
||||
ya.mgr_emit("visual_mode", {}) |
||||
-- invert direction when user specifies it |
||||
if direction == "k" then |
||||
ya.mgr_emit("arrow", { -lines }) |
||||
elseif direction == "j" then |
||||
ya.mgr_emit("arrow", { lines }) |
||||
else |
||||
ya.mgr_emit("arrow", { lines - 1 }) |
||||
end |
||||
ya.mgr_emit("escape", {}) |
||||
|
||||
if cmd == "d" then |
||||
ya.mgr_emit("remove", {}) |
||||
elseif cmd == "y" then |
||||
ya.mgr_emit("yank", {}) |
||||
elseif cmd == "x" then |
||||
ya.mgr_emit("yank", { cut = true }) |
||||
end |
||||
end |
||||
|
||||
render_clear() |
||||
end, |
||||
setup = function(state, args) |
||||
if not args then |
||||
return |
||||
end |
||||
|
||||
-- initialize state variables |
||||
state._only_motions = args["only_motions"] or false |
||||
|
||||
if args["show_motion"] then |
||||
render_motion_setup() |
||||
end |
||||
|
||||
if args["enter_mode"] == "cache" then |
||||
state._enter_mode = ENTER_MODE_CACHE |
||||
elseif args["enter_mode"] == "first" then |
||||
state._enter_mode = ENTER_MODE_FIRST |
||||
elseif args["enter_mode"] == "cache_or_first" then |
||||
state._enter_mode = ENTER_MODE_CACHE_OR_FIRST |
||||
else |
||||
state._enter_mode = ENTER_MODE_CACHE_OR_FIRST |
||||
end |
||||
|
||||
if args["show_numbers"] == "absolute" then |
||||
render_numbers(SHOW_NUMBERS_ABSOLUTE) |
||||
elseif args["show_numbers"] == "relative" then |
||||
render_numbers(SHOW_NUMBERS_RELATIVE) |
||||
elseif args["show_numbers"] == "relative_absolute" then |
||||
render_numbers(SHOW_NUMBERS_RELATIVE_ABSOLUTE) |
||||
end |
||||
end, |
||||
} |
||||
@ -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. |
||||
@ -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] action, 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 |
||||
@ -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 } |
||||
@ -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. |
||||
@ -0,0 +1,55 @@
|
||||
# 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" |
||||
``` |
||||
|
||||
Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other actions/plugins. |
||||
|
||||
## 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. |
||||
@ -0,0 +1,119 @@
|
||||
--- @since 26.1.22 |
||||
|
||||
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 output, err = Command("magick"):arg { |
||||
tostring(job.file.path), |
||||
"-auto-orient", "-strip", |
||||
"-sample", string.format("%dx%d", new_w, new_h), |
||||
"-quality", rt.preview.image_quality, |
||||
string.format("JPG:%s", tmp), |
||||
}:output() |
||||
|
||||
if not output then |
||||
end_(job, Err("Failed to start `magick`, error: %s", err)) |
||||
elseif not output.status.success then |
||||
end_(job, Err("`magick` exited with error code %s: %s", output.status.code, output.stderr)) |
||||
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 = File { url = st.url, cha = Cha { mode = tonumber("100644", 8) } }, |
||||
skip = 0, |
||||
new_level = new, |
||||
old_level = st.level, |
||||
}) |
||||
end |
||||
end |
||||
|
||||
return { peek = peek, entry = entry } |
||||
@ -0,0 +1,3 @@
|
||||
[flavor] |
||||
# dark = "everforest-medium" |
||||
dark = "gruvbox-material" |
||||
@ -0,0 +1,37 @@
|
||||
[[plugin.prepend_fetchers]] |
||||
# id = "git" # Remove if Yazi > v26.1.22 |
||||
url = "*" |
||||
run = "git" |
||||
group = "git" |
||||
|
||||
[[plugin.prepend_fetchers]] |
||||
# id = "git" # Remove if Yazi > v26.1.22 |
||||
url = "*/" |
||||
run = "git" |
||||
group = "git" |
||||
|
||||
# [opener] |
||||
# edit = [ |
||||
# { run = 'nvim "$@"', desc = "Open in Neovim", block = true, for = "unix" }, |
||||
# ] |
||||
|
||||
# [[plugin.prepend_previewers]] |
||||
# url = "*" |
||||
# run = 'piper -- echo "$1"' |
||||
|
||||
[[plugin.prepend_previewers]] |
||||
url = "*.md" |
||||
run = 'piper -- CLICOLOR_FORCE=1 glow -w=$w -s=dark "$1"' |
||||
|
||||
[[plugin.prepend_previewers]] |
||||
url = "*/" |
||||
run = 'piper -- eza -TL=3 --color=always --icons=always --group-directories-first --no-quotes "$1"' |
||||
|
||||
[[plugin.prepend_previewers]] |
||||
mime = "application/sqlite3" |
||||
run = 'piper -- sqlite3 "$1" ".schema --indent"' |
||||
|
||||
[[plugin.append_previewers]] |
||||
url = "*" |
||||
run = 'piper -- hexyl --border=none --terminal-width=$w "$1"' |
||||
|
||||
@ -0,0 +1,25 @@
|
||||
#!/bin/bash |
||||
set -euo pipefail |
||||
|
||||
echo "[1/4] Updating apt and installing prerequisites (curl, build-essential)..." |
||||
sudo apt update -qq |
||||
sudo apt install -y curl build-essential imagemagick |
||||
|
||||
sudo ln -s /usr/bin/convert /usr/bin/magick |
||||
|
||||
echo "[2/4] Downloading and installing rustup (latest rustc/cargo)..." |
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y |
||||
|
||||
echo "[3/4] Sourcing cargo environment..." |
||||
source "$HOME/.cargo/env" |
||||
|
||||
echo " -> Installed: $(rustc --version)" |
||||
echo " -> Installed: $(cargo --version)" |
||||
|
||||
echo "[4/4] Installing tools..." |
||||
cargo install fsel |
||||
cargo install yazi |
||||
cargo install glow |
||||
cargo install resvg |
||||
|
||||
echo "Done!" |
||||
Loading…
Reference in new issue