mirror of
https://github.com/kristoferssolo/solorice.git
synced 2026-02-04 06:32:03 +00:00
Update 2026-01-27
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
# vim:fileencoding=utf-8:foldmethod=marker
|
||||
|
||||
# : App {{{
|
||||
|
||||
[app]
|
||||
overall = { bg = "#faf4ed" }
|
||||
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Manager {{{
|
||||
|
||||
[mgr]
|
||||
cwd = { fg = "#56949f" }
|
||||
|
||||
# Hovered
|
||||
hovered = { reversed = true }
|
||||
preview_hovered = { underline = true }
|
||||
|
||||
# Find
|
||||
find_keyword = { fg = "#ea9d34", bold = true, italic = true, underline = true }
|
||||
find_position = { fg = "#907aa9", bg = "reset", bold = true, italic = true }
|
||||
@@ -17,9 +21,9 @@ find_position = { fg = "#907aa9", bg = "reset", bold = true, italic = true }
|
||||
symlink_target = { italic = true }
|
||||
|
||||
# Marker
|
||||
marker_copied = { fg = "#286983", bg = "#286983" }
|
||||
marker_cut = { fg = "#b4637a", bg = "#b4637a" }
|
||||
marker_marked = { fg = "#d7827e", bg = "#d7827e" }
|
||||
marker_copied = { fg = "#286983", bg = "#286983" }
|
||||
marker_cut = { fg = "#b4637a", bg = "#b4637a" }
|
||||
marker_marked = { fg = "#d7827e", bg = "#d7827e" }
|
||||
marker_selected = { fg = "#ea9d34", bg = "#ea9d34" }
|
||||
|
||||
# Count
|
||||
@@ -67,6 +71,17 @@ unset_alt = { fg = "#faf4ed", bg = "#575279", bold = true }
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Indicator of hovered file {{{
|
||||
|
||||
[indicator]
|
||||
parent = { reversed = true }
|
||||
current = { reversed = true }
|
||||
preview = { underline = true }
|
||||
padding = { open = "", close = "" }
|
||||
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Status bar {{{
|
||||
|
||||
[status]
|
||||
@@ -137,6 +152,10 @@ title_info = { fg = "#286983" }
|
||||
title_warn = { fg = "#ea9d34" }
|
||||
title_error = { fg = "#b4637a" }
|
||||
|
||||
# Icons
|
||||
icon_info = ""
|
||||
icon_warn = ""
|
||||
icon_error = ""
|
||||
|
||||
# : }}}
|
||||
|
||||
@@ -146,7 +165,7 @@ title_error = { fg = "#b4637a" }
|
||||
[pick]
|
||||
border = { fg = "#286983" }
|
||||
active = { fg = "#907aa9", bold = true }
|
||||
inactive = { fg = "#797593"}
|
||||
inactive = { fg = "#797593" }
|
||||
|
||||
# : }}}
|
||||
|
||||
@@ -156,7 +175,7 @@ inactive = { fg = "#797593"}
|
||||
[input]
|
||||
border = { fg = "#286983" }
|
||||
title = { bold = true }
|
||||
value = { fg = "#575279"}
|
||||
value = { fg = "#575279" }
|
||||
selected = { reversed = true }
|
||||
|
||||
# : }}}
|
||||
@@ -169,6 +188,10 @@ border = { fg = "#286983" }
|
||||
active = { bg = "#575279", fg = "#faf4ed", bold = true }
|
||||
inactive = { fg = "#797593" }
|
||||
|
||||
# Icons
|
||||
icon_file = ""
|
||||
icon_folder = ""
|
||||
icon_command = ""
|
||||
|
||||
# : }}}
|
||||
|
||||
@@ -177,7 +200,7 @@ inactive = { fg = "#797593" }
|
||||
|
||||
[tasks]
|
||||
border = { fg = "#286983" }
|
||||
title = { bold = true}
|
||||
title = { bold = true }
|
||||
hovered = { fg = "#907aa9", bold = true, reversed = true }
|
||||
|
||||
# : }}}
|
||||
@@ -188,7 +211,7 @@ hovered = { fg = "#907aa9", bold = true, reversed = true }
|
||||
[help]
|
||||
on = { fg = "#56949f" }
|
||||
run = { fg = "#907aa9" }
|
||||
desc = { fg = "#ea9d34"}
|
||||
desc = { fg = "#ea9d34" }
|
||||
hovered = { reversed = true, bold = true }
|
||||
footer = { fg = "#faf4ed", bg = "#575279" }
|
||||
|
||||
@@ -199,32 +222,32 @@ footer = { fg = "#faf4ed", bg = "#575279" }
|
||||
|
||||
[filetype]
|
||||
rules = [
|
||||
# Images
|
||||
{ mime = "image/*", fg = "#907aa9" },
|
||||
# Images
|
||||
{ mime = "image/*", fg = "#907aa9" },
|
||||
|
||||
# Media
|
||||
{ mime = "{audio,video}/*", fg = "#ea9d34" },
|
||||
# Media
|
||||
{ mime = "{audio,video}/*", fg = "#ea9d34" },
|
||||
|
||||
# Archives
|
||||
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#b4637a" },
|
||||
# Archives
|
||||
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#b4637a" },
|
||||
|
||||
# Documents
|
||||
{ mime = "application/{pdf,doc,rtf}", fg = "#d7827e" },
|
||||
# Documents
|
||||
{ mime = "application/{pdf,doc,rtf}", fg = "#d7827e" },
|
||||
|
||||
# Empty files
|
||||
# { mime = "inode/empty", fg = "#b4637a" },
|
||||
# Empty files
|
||||
# { mime = "inode/empty", fg = "#b4637a" },
|
||||
|
||||
# Special files
|
||||
{ name = "*", is = "orphan", fg = "#faf4ed", bg = "#b4637a" },
|
||||
{ name = "*", is = "exec" , fg = "#56949f" },
|
||||
# Special files
|
||||
{ url = "*", is = "orphan", fg = "#faf4ed", bg = "#b4637a" },
|
||||
{ url = "*", is = "exec", fg = "#56949f" },
|
||||
|
||||
# Dummy files
|
||||
{ name = "*", is = "dummy", fg = "#faf4ed", bg = "#b4637a" },
|
||||
{ name = "*/", is = "dummy", fg = "#faf4ed", bg = "#b4637a" },
|
||||
# Dummy files
|
||||
{ url = "*", is = "dummy", fg = "#faf4ed", bg = "#b4637a" },
|
||||
{ url = "*/", is = "dummy", fg = "#faf4ed", bg = "#b4637a" },
|
||||
|
||||
# Fallback
|
||||
{ name = "*", fg = "#575279" },
|
||||
{ name = "*/", fg = "#286983" }
|
||||
# Fallback
|
||||
{ url = "*", fg = "#575279" },
|
||||
{ url = "*/", fg = "#286983" }
|
||||
]
|
||||
|
||||
# : }}}
|
||||
# : }}}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
# vim:fileencoding=utf-8:foldmethod=marker
|
||||
|
||||
# : App {{{
|
||||
|
||||
[app]
|
||||
overall = { bg = "#232136" }
|
||||
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Manager {{{
|
||||
|
||||
[mgr]
|
||||
cwd = { fg = "#9ccfd8" }
|
||||
|
||||
# Hovered
|
||||
hovered = { reversed = true }
|
||||
preview_hovered = { underline = true }
|
||||
|
||||
# Find
|
||||
find_keyword = { fg = "#f6c177", bold = true, italic = true, underline = true }
|
||||
find_position = { fg = "#c4a7e7", bg = "reset", bold = true, italic = true }
|
||||
@@ -17,9 +21,9 @@ find_position = { fg = "#c4a7e7", bg = "reset", bold = true, italic = true }
|
||||
symlink_target = { italic = true }
|
||||
|
||||
# Marker
|
||||
marker_copied = { fg = "#3e8fb0", bg = "#3e8fb0" }
|
||||
marker_cut = { fg = "#eb6f92", bg = "#eb6f92" }
|
||||
marker_marked = { fg = "#ea9a97", bg = "#ea9a97" }
|
||||
marker_copied = { fg = "#3e8fb0", bg = "#3e8fb0" }
|
||||
marker_cut = { fg = "#eb6f92", bg = "#eb6f92" }
|
||||
marker_marked = { fg = "#ea9a97", bg = "#ea9a97" }
|
||||
marker_selected = { fg = "#f6c177", bg = "#f6c177" }
|
||||
|
||||
# Count
|
||||
@@ -67,6 +71,17 @@ unset_alt = { fg = "#232136", bg = "#e0def4", bold = true }
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Indicator of hovered file {{{
|
||||
|
||||
[indicator]
|
||||
parent = { reversed = true }
|
||||
current = { reversed = true }
|
||||
preview = { underline = true }
|
||||
padding = { open = "", close = "" }
|
||||
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Status bar {{{
|
||||
|
||||
[status]
|
||||
@@ -137,6 +152,10 @@ title_info = { fg = "#3e8fb0" }
|
||||
title_warn = { fg = "#f6c177" }
|
||||
title_error = { fg = "#eb6f92" }
|
||||
|
||||
# Icons
|
||||
icon_info = ""
|
||||
icon_warn = ""
|
||||
icon_error = ""
|
||||
|
||||
# : }}}
|
||||
|
||||
@@ -146,7 +165,7 @@ title_error = { fg = "#eb6f92" }
|
||||
[pick]
|
||||
border = { fg = "#3e8fb0" }
|
||||
active = { fg = "#c4a7e7", bold = true }
|
||||
inactive = { fg = "#908caa"}
|
||||
inactive = { fg = "#908caa" }
|
||||
|
||||
# : }}}
|
||||
|
||||
@@ -156,7 +175,7 @@ inactive = { fg = "#908caa"}
|
||||
[input]
|
||||
border = { fg = "#3e8fb0" }
|
||||
title = { bold = true }
|
||||
value = { fg = "#e0def4"}
|
||||
value = { fg = "#e0def4" }
|
||||
selected = { reversed = true }
|
||||
|
||||
# : }}}
|
||||
@@ -169,6 +188,10 @@ border = { fg = "#3e8fb0" }
|
||||
active = { bg = "#e0def4", fg = "#232136", bold = true }
|
||||
inactive = { fg = "#908caa" }
|
||||
|
||||
# Icons
|
||||
icon_file = ""
|
||||
icon_folder = ""
|
||||
icon_command = ""
|
||||
|
||||
# : }}}
|
||||
|
||||
@@ -177,7 +200,7 @@ inactive = { fg = "#908caa" }
|
||||
|
||||
[tasks]
|
||||
border = { fg = "#3e8fb0" }
|
||||
title = { bold = true}
|
||||
title = { bold = true }
|
||||
hovered = { fg = "#c4a7e7", bold = true, reversed = true }
|
||||
|
||||
# : }}}
|
||||
@@ -188,7 +211,7 @@ hovered = { fg = "#c4a7e7", bold = true, reversed = true }
|
||||
[help]
|
||||
on = { fg = "#9ccfd8" }
|
||||
run = { fg = "#c4a7e7" }
|
||||
desc = { fg = "#f6c177"}
|
||||
desc = { fg = "#f6c177" }
|
||||
hovered = { reversed = true, bold = true }
|
||||
footer = { fg = "#232136", bg = "#e0def4" }
|
||||
|
||||
@@ -199,32 +222,32 @@ footer = { fg = "#232136", bg = "#e0def4" }
|
||||
|
||||
[filetype]
|
||||
rules = [
|
||||
# Images
|
||||
{ mime = "image/*", fg = "#c4a7e7" },
|
||||
# Images
|
||||
{ mime = "image/*", fg = "#c4a7e7" },
|
||||
|
||||
# Media
|
||||
{ mime = "{audio,video}/*", fg = "#f6c177" },
|
||||
# Media
|
||||
{ mime = "{audio,video}/*", fg = "#f6c177" },
|
||||
|
||||
# Archives
|
||||
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#eb6f92" },
|
||||
# Archives
|
||||
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#eb6f92" },
|
||||
|
||||
# Documents
|
||||
{ mime = "application/{pdf,doc,rtf}", fg = "#ea9a97" },
|
||||
# Documents
|
||||
{ mime = "application/{pdf,doc,rtf}", fg = "#ea9a97" },
|
||||
|
||||
# Empty files
|
||||
# { mime = "inode/empty", fg = "#eb6f92" },
|
||||
# Empty files
|
||||
# { mime = "inode/empty", fg = "#eb6f92" },
|
||||
|
||||
# Special files
|
||||
{ name = "*", is = "orphan", fg = "#232136", bg = "#eb6f92" },
|
||||
{ name = "*", is = "exec" , fg = "#9ccfd8" },
|
||||
# Special files
|
||||
{ url = "*", is = "orphan", fg = "#232136", bg = "#eb6f92" },
|
||||
{ url = "*", is = "exec", fg = "#9ccfd8" },
|
||||
|
||||
# Dummy files
|
||||
{ name = "*", is = "dummy", fg = "#232136", bg = "#eb6f92" },
|
||||
{ name = "*/", is = "dummy", fg = "#232136", bg = "#eb6f92" },
|
||||
# Dummy files
|
||||
{ url = "*", is = "dummy", fg = "#232136", bg = "#eb6f92" },
|
||||
{ url = "*/", is = "dummy", fg = "#232136", bg = "#eb6f92" },
|
||||
|
||||
# Fallback
|
||||
{ name = "*", fg = "#e0def4" },
|
||||
{ name = "*/", fg = "#3e8fb0" }
|
||||
# Fallback
|
||||
{ url = "*", fg = "#e0def4" },
|
||||
{ url = "*/", fg = "#3e8fb0" }
|
||||
]
|
||||
|
||||
# : }}}
|
||||
# : }}}
|
||||
|
||||
@@ -10,58 +10,58 @@ hash = "e02a788e5b8ae0fb47fd0193dda589cc"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "hankertrix/augment-command"
|
||||
rev = "120406f"
|
||||
hash = "e83ccc99739cd500fb1b1014dc45824a"
|
||||
rev = "7c12bdf"
|
||||
hash = "f7e6d377e4efee567ec6d1c2355f2ca3"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "kirasok/torrent-preview"
|
||||
rev = "f465282"
|
||||
hash = "b89b46a15035a51d010a254858ba9741"
|
||||
hash = "d849ad596b8a77902e62a42403aeba40"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "ndtoan96/ouch"
|
||||
rev = "0742fff"
|
||||
hash = "2bd799b42b8e42b8ef100310be71f8d1"
|
||||
rev = "594b8a2"
|
||||
hash = "c9e628fc0312d198db22ae2fa74883b"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "pirafrank/what-size"
|
||||
rev = "d896656"
|
||||
hash = "62251f94eb67648d42cdfcfb8be0fad2"
|
||||
rev = "179ebf6"
|
||||
hash = "57056b9728006881d580ccabe8154a9c"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:git"
|
||||
rev = "8f1d971"
|
||||
hash = "63b6c222bf2103b3023389dde5e2ecfe"
|
||||
rev = "e07bf41"
|
||||
hash = "270915fa8282a19908449530ff66f7e2"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:chmod"
|
||||
rev = "8f1d971"
|
||||
hash = "8ad3e90b079190b4905f24a44f4ca06b"
|
||||
rev = "e07bf41"
|
||||
hash = "8da0b15a97b5dfd13941d1ecc617ac7c"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:full-border"
|
||||
rev = "8f1d971"
|
||||
rev = "e07bf41"
|
||||
hash = "3996fc74044bc44144b323686f887e1"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:mount"
|
||||
rev = "8f1d971"
|
||||
hash = "4fccaaf10c36dc2466e2438a386ff017"
|
||||
rev = "e07bf41"
|
||||
hash = "563e4068979d1466d3dfc2e70a296947"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:smart-filter"
|
||||
rev = "8f1d971"
|
||||
hash = "bcda8eeb3f4013bae156aecabfc745e8"
|
||||
rev = "e07bf41"
|
||||
hash = "407d19bc4fb46eff5fa8ff8337644847"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:diff"
|
||||
rev = "8f1d971"
|
||||
hash = "82376c3381ae915fd799d40f4dd4c6cd"
|
||||
rev = "e07bf41"
|
||||
hash = "2f08b8249b57737e7257298a3b2a2edc"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "AnirudhG07/rich-preview"
|
||||
rev = "831234e"
|
||||
hash = "ed3363e256e210593f0f8e4cec3e36a9"
|
||||
rev = "573b275"
|
||||
hash = "c3e2871c9ef244fd181f203791f9b0d2"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "macydnah/office"
|
||||
@@ -70,30 +70,30 @@ hash = "5805affd3ae8adcb3c72b6997d21c0a6"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "boydaihungst/mediainfo"
|
||||
rev = "7543154"
|
||||
hash = "212fe977a845f44da073a397e0170e60"
|
||||
rev = "dc61636"
|
||||
hash = "2fa34959353b6f1a1c33659f50e098fd"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "iynaix/time-travel"
|
||||
rev = "7e0179e"
|
||||
hash = "69967963fba96295a07b68354fc91ea9"
|
||||
rev = "aaec6e2"
|
||||
hash = "3d52b2b88c91a4ece2f7ed68ff39bb7b"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "imsi32/yatline"
|
||||
rev = "88bd1c5"
|
||||
hash = "981acbe9b758b1af3a8154c932bb936d"
|
||||
|
||||
[[flavor.deps]]
|
||||
use = "Mintass/rose-pine"
|
||||
rev = "8343340"
|
||||
hash = "e2336631865c0f90276dcbc553d532fc"
|
||||
rev = "c5d4b48"
|
||||
hash = "e6e98d12b1648d1894c2b560d85eeaa7"
|
||||
|
||||
[[flavor.deps]]
|
||||
use = "Mintass/rose-pine-moon"
|
||||
rev = "aa2d937"
|
||||
hash = "97cd4819252cb1a9d0f55daa14e481e"
|
||||
rev = "94385fe"
|
||||
hash = "edbe0dfb5db8ff37281dba62adc7e750"
|
||||
|
||||
[[flavor.deps]]
|
||||
use = "Mintass/rose-pine-dawn"
|
||||
rev = "5c6d353"
|
||||
hash = "d9d9463d5d43ced18e18986d05439ace"
|
||||
rev = "d82f54f"
|
||||
hash = "15ae5f83d3770b9ffc276af77f642e2b"
|
||||
|
||||
[[flavor.deps]]
|
||||
use = "Mintass/rose-pine"
|
||||
rev = "d91f8f2"
|
||||
hash = "87da3630653b6d272e43658418599188"
|
||||
|
||||
@@ -47,7 +47,7 @@ plugin.
|
||||
|
||||
## Requirements
|
||||
|
||||
- [Yazi][yazi-link] v25.5.31+
|
||||
- [Yazi][yazi-link] v25.12.29+
|
||||
- [`7z` or `7zz` command][7z-link]
|
||||
- [`file` command][file-command-link]
|
||||
|
||||
@@ -191,7 +191,6 @@ then it will operate on the selected items.
|
||||
[this section above][augment-section].
|
||||
|
||||
Videos:
|
||||
|
||||
- When `prompt` is set to `true`:
|
||||
|
||||
[open-prompt-video]
|
||||
@@ -258,7 +257,6 @@ then it will operate on the selected items.
|
||||
[this section above][augment-section].
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[extract-must-have-hovered-item-video]
|
||||
@@ -304,27 +302,19 @@ then it will operate on the selected items.
|
||||
|
||||
[opener]
|
||||
extract = [
|
||||
{ run = 'ya pub augmented-extract --list "$@"', desc = "Extract here", for = "unix" },
|
||||
{ run = 'ya pub augmented-extract --list %*', desc = "Extract here", for = "windows" },
|
||||
{ run = "ya pub augmented-extract --list %s", desc = "Extract here" },
|
||||
]
|
||||
```
|
||||
|
||||
If that exceeds your editor's line length limit,
|
||||
another way to do it is:
|
||||
Alternatively, another way to do it is:
|
||||
|
||||
```toml
|
||||
# ~/.config/yazi/yazi.toml for Linux and macOS
|
||||
# %AppData%\yazi\config\yazi.toml for Windows
|
||||
|
||||
[[opener.extract]]
|
||||
run = 'ya pub augmented-extract --list "$@"'
|
||||
run = "ya pub augmented-extract --list %s"
|
||||
desc = "Extract here"
|
||||
for = "unix"
|
||||
|
||||
[[opener.extract]]
|
||||
run = 'ya pub augmented-extract --list %*'
|
||||
desc = "Extract here"
|
||||
for = "windows"
|
||||
```
|
||||
|
||||
- The `extract` command supports recursively extracting archives,
|
||||
@@ -467,7 +457,6 @@ then it will operate on the selected items.
|
||||
[this section above][augment-section].
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[rename-must-have-hovered-item-video]
|
||||
@@ -490,7 +479,6 @@ then it will operate on the selected items.
|
||||
[this section above][augment-section].
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[remove-must-have-hovered-item-video]
|
||||
@@ -513,7 +501,6 @@ then it will operate on the selected items.
|
||||
[this section above][augment-section].
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[copy-must-have-hovered-item-video]
|
||||
@@ -604,7 +591,6 @@ then it will operate on the selected items.
|
||||
use the default `shell` command provided by Yazi.
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[shell-must-have-hovered-item-video]
|
||||
@@ -623,8 +609,8 @@ then it will operate on the selected items.
|
||||
|
||||
- To use this command, the syntax is exactly the same as the default
|
||||
`shell` command provided by Yazi. You just provide
|
||||
the command you want and provide any Yazi shell variable,
|
||||
which is documented [here][yazi-shell-variables].
|
||||
the command you want and provide any Yazi shell variable that
|
||||
**provides the file path**, which is [documented here][yazi-shell-variables].
|
||||
The plugin will automatically replace the shell variable you give
|
||||
with the file paths for the item group before executing the command.
|
||||
|
||||
@@ -650,7 +636,7 @@ then it will operate on the selected items.
|
||||
|
||||
[[mgr.prepend_keymap]]
|
||||
on = "i"
|
||||
run = "plugin augment-command -- shell '$PAGER $@' --block --exit-if-dir"
|
||||
run = "plugin augment-command -- shell '$PAGER %s' --block --exit-if-dir"
|
||||
desc = "Open the pager"
|
||||
```
|
||||
|
||||
@@ -667,7 +653,7 @@ then it will operate on the selected items.
|
||||
|
||||
[[mgr.prepend_keymap]]
|
||||
on = "o"
|
||||
run = "plugin augment-command -- shell '$EDITOR $@' --block --exit-if-dir"
|
||||
run = "plugin augment-command -- shell '$EDITOR %s' --block --exit-if-dir"
|
||||
desc = "Open the editor"
|
||||
```
|
||||
|
||||
@@ -692,18 +678,10 @@ the shell command arguments, so here are a few ways to do it:
|
||||
# %AppData%\yazi\config\keymap.toml on Windows
|
||||
[[mgr.prepend_keymap]]
|
||||
on = "i"
|
||||
run = "plugin augment-command -- shell --block 'bat -p --pager less $@'"
|
||||
run = "plugin augment-command -- shell --block 'bat -p --pager less %s'"
|
||||
desc = "Open with bat"
|
||||
```
|
||||
|
||||
Even though the `$@` argument above is considered
|
||||
a shell variable in Linux and macOS,
|
||||
the plugin automatically replaces it with the full path
|
||||
of the items in the item group,
|
||||
so it does not need to be quoted with
|
||||
double quotes `"`, as it is expanded by the plugin,
|
||||
and not meant to be expanded by the shell.
|
||||
|
||||
2. If the arguments to the `shell` command have special
|
||||
shell variables on Linux and macOS, like `$SHELL`,
|
||||
or special shell characters like `>`, `|`, or spaces,
|
||||
@@ -765,16 +743,12 @@ the shell command arguments, so here are a few ways to do it:
|
||||
on = "<C-e>"
|
||||
run = '''plugin augment-command --
|
||||
shell --
|
||||
paths=$(for p in $@; do echo "$p"; done | paste -s -d,)
|
||||
paths=$(for p in %s; do echo "$p"; done | paste -s -d,)
|
||||
thunderbird -compose "attachment='$paths'"
|
||||
'''
|
||||
desc = "Email files using Mozilla Thunderbird"
|
||||
```
|
||||
|
||||
Once again, the `$@` variable above does not need to be quoted
|
||||
in double quotes `"` as it is expanded by the plugin
|
||||
instead of the shell.
|
||||
|
||||
If the above few methods to avoid using backslashes
|
||||
within your shell command to escape the quotes are
|
||||
still insufficient for your use case,
|
||||
@@ -1043,7 +1017,6 @@ in your `keymap.toml` file.
|
||||
[this section above][augment-section].
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[archive-must-have-hovered-item-video]
|
||||
@@ -1149,7 +1122,6 @@ in your `keymap.toml` file.
|
||||
[this section above][augment-section].
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[editor-must-have-hovered-item-video]
|
||||
@@ -1181,7 +1153,6 @@ in your `keymap.toml` file.
|
||||
causing a flash and causing Yazi to send a notification.
|
||||
|
||||
Videos:
|
||||
|
||||
- When `must_have_hovered_item` is `true`:
|
||||
|
||||
[pager-must-have-hovered-item-video]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.5.31
|
||||
--- @since 25.12.29
|
||||
|
||||
-- Plugin to make some Yazi commands smarter
|
||||
-- Written in Lua 5.4
|
||||
@@ -24,29 +24,50 @@
|
||||
-- The type for the archiver list items command
|
||||
---@alias Archiver.ListItemsCommand fun(
|
||||
--- self: Archiver,
|
||||
---): output: CommandOutput|nil, error: Error|nil
|
||||
---): output: Output?, error: Error?
|
||||
|
||||
-- The type for the archiver get items function
|
||||
---@alias Archiver.GetItems fun(
|
||||
--- self: Archiver,
|
||||
---): files: string[], directories: string[], error: string|nil
|
||||
---): files: string[], dirs: string[], result: Archiver.Result
|
||||
|
||||
-- The type for the archiver extract function
|
||||
---@alias Archiver.Extract fun(
|
||||
--- self: Archiver,
|
||||
--- has_only_one_file: boolean|nil,
|
||||
--- has_only_one_file: boolean?,
|
||||
---): Archiver.Result
|
||||
|
||||
-- The type for the archiver archive function
|
||||
---@alias Archiver.Archive fun(
|
||||
--- self: Archiver,
|
||||
--- item_paths: string[],
|
||||
--- password: string|nil,
|
||||
--- encrypt_headers: boolean|nil,
|
||||
--- password: string?,
|
||||
--- encrypt_headers: boolean?,
|
||||
---): Archiver.Result
|
||||
|
||||
-- The type for the archiver command function
|
||||
---@alias Archiver.Command fun(): output: CommandOutput|nil, error: Error|nil
|
||||
---@alias Archiver.Command fun(): output: Output?, error: Error?
|
||||
|
||||
-- The type for the Yazi input options
|
||||
---@alias YaziInputOptions {
|
||||
--- title: string,
|
||||
--- value: string?,
|
||||
--- obscure: boolean?,
|
||||
--- pos: AsPos,
|
||||
--- realtime: boolean?,
|
||||
--- debounce: number?,
|
||||
---}
|
||||
|
||||
-- The type for the Yazi notification options
|
||||
---@alias YaziNotificationOptions {
|
||||
--- title: string,
|
||||
--- content: string,
|
||||
--- timeout: number,
|
||||
--- level: "info"|"warn"|"error"|nil,
|
||||
---}
|
||||
|
||||
-- The type for the Yazi confirm options
|
||||
---@alias YaziConfirmOptions { pos: AsPos, title: AsLine, body: AsText }
|
||||
|
||||
-- The type of the function to get the password options
|
||||
---@alias GetPasswordOptions fun(is_confirm_password: boolean): YaziInputOptions
|
||||
@@ -93,13 +114,13 @@
|
||||
-- The type for the archiver function result
|
||||
---@class (exact) Archiver.Result
|
||||
---@field successful boolean Whether the archiver function was successful
|
||||
---@field output string|nil The output of the archiver function
|
||||
---@field cancelled boolean|nil boolean Whether the archiver was cancelled
|
||||
---@field error string|nil The error message
|
||||
---@field archive_path string|nil The path to the archive
|
||||
---@field destination_path string|nil The path to the destination
|
||||
---@field extracted_items_path string|nil The path to the extracted items
|
||||
---@field archiver_name string|nil The name of the archiver
|
||||
---@field output string? The output of the archiver function
|
||||
---@field cancelled boolean? boolean Whether the archiver was cancelled
|
||||
---@field error string? The error message
|
||||
---@field archive_path string? The path to the archive
|
||||
---@field destination_path string? The path to the destination
|
||||
---@field extracted_items_path string? The path to the extracted items
|
||||
---@field archiver_name string? The name of the archiver
|
||||
|
||||
-- The module table
|
||||
---@class AugmentCommandPlugin
|
||||
@@ -168,7 +189,7 @@ local INPUT_AND_CONFIRM_OPTIONS = {
|
||||
"title",
|
||||
"origin",
|
||||
"offset",
|
||||
"content",
|
||||
"body",
|
||||
}
|
||||
|
||||
-- The default configuration for the plugin
|
||||
@@ -221,7 +242,7 @@ local DEFAULT_NOTIFICATION_OPTIONS = {
|
||||
-- The values are just dummy values
|
||||
-- so that I don't have to maintain two
|
||||
-- different types for the same thing.
|
||||
---@type tab.Preference
|
||||
---@type tab__Pref
|
||||
local TAB_PREFERENCE_KEYS = {
|
||||
sort_by = "alphabetical",
|
||||
sort_sensitive = false,
|
||||
@@ -300,7 +321,7 @@ local BASE_ARCHIVER_ERROR = table.concat({
|
||||
-- The base archiver that all archivers inherit from
|
||||
---@class Archiver
|
||||
---@field name string The name of the archiver
|
||||
---@field command string|nil The shell command for the archiver
|
||||
---@field command string? The shell command for the archiver
|
||||
---@field commands string[] The possible archiver commands
|
||||
---
|
||||
--- Whether the archiver supports preserving file permissions
|
||||
@@ -334,7 +355,12 @@ end
|
||||
|
||||
-- The method to get the archive items
|
||||
---@type Archiver.GetItems
|
||||
function Archiver:get_items() return {}, {}, BASE_ARCHIVER_ERROR end
|
||||
function Archiver:get_items()
|
||||
return {}, {}, {
|
||||
successful = false,
|
||||
error = BASE_ARCHIVER_ERROR,
|
||||
}
|
||||
end
|
||||
|
||||
-- The method to extract the archive
|
||||
---@type Archiver.Extract
|
||||
@@ -424,7 +450,7 @@ local get_mime_type_without_prefix_template_pattern =
|
||||
|
||||
-- The pattern to get the shell variables in a command
|
||||
---@type string
|
||||
local shell_variable_pattern = "[%$%%][%*@0]"
|
||||
local shell_variable_pattern = "%%[hs]%d?"
|
||||
|
||||
-- The pattern to match the bat command
|
||||
---@type string
|
||||
@@ -446,8 +472,8 @@ local bat_command_pattern = "%f[%a]bat%f[%A]"
|
||||
-- Pass true as the first parameter to get the function
|
||||
-- to merge the tables recursively.
|
||||
---@param deep_or_target table<any, any>|boolean|nil Recursively merge or not
|
||||
---@param target table<any, any> The target table to merge
|
||||
---@param ... table<any, any>[] The tables to merge
|
||||
---@param target table<any, any>? The target table to merge
|
||||
---@param ... table<any, any>[]? The tables to merge
|
||||
---@return table<any, any> merged_table The merged table
|
||||
local function merge_tables(deep_or_target, target, ...)
|
||||
--
|
||||
@@ -489,6 +515,9 @@ local function merge_tables(deep_or_target, target, ...)
|
||||
args = { target, ... }
|
||||
end
|
||||
|
||||
-- The target table will not be nil after the checks above
|
||||
---@cast target_table table<any, any>
|
||||
|
||||
-- Initialise the index variable
|
||||
local index = #target_table + 1
|
||||
|
||||
@@ -549,7 +578,7 @@ end
|
||||
|
||||
-- Function to split a string into a list
|
||||
---@param given_string string The string to split
|
||||
---@param separator string|nil The character to split the string by
|
||||
---@param separator string? The character to split the string by
|
||||
---@return string[] splitted_strings The list of strings split by the character
|
||||
local function string_split(given_string, separator)
|
||||
--
|
||||
@@ -705,7 +734,7 @@ end
|
||||
|
||||
-- Function to show a warning
|
||||
---@param warning_message any The warning message
|
||||
---@param options YaziNotificationOptions|nil Options for the notification
|
||||
---@param options YaziNotificationOptions? Options for the notification
|
||||
---@return nil
|
||||
local function show_warning(warning_message, options)
|
||||
return ya.notify(
|
||||
@@ -718,7 +747,7 @@ end
|
||||
|
||||
-- Function to show an error
|
||||
---@param error_message any The error message
|
||||
---@param options YaziNotificationOptions|nil Options for the notification
|
||||
---@param options YaziNotificationOptions? Options for the notification
|
||||
---@return nil
|
||||
local function show_error(error_message, options)
|
||||
return ya.notify(
|
||||
@@ -737,7 +766,7 @@ local function throw_error(error_message, ...)
|
||||
end
|
||||
|
||||
-- Function to get the theme from an async function
|
||||
---@type fun(): Th The theme object
|
||||
---@type fun(): th The theme object
|
||||
local get_theme = ya.sync(function(state) return state.theme end)
|
||||
|
||||
-- Function to get the component option string
|
||||
@@ -752,13 +781,13 @@ end
|
||||
---@param component BuiltInComponents|PluginComponents The name of the component
|
||||
---@param defaults {
|
||||
--- prompts: string|string[], -- The default prompts
|
||||
--- content: string|ui.Line|ui.Text|nil, -- The default contents
|
||||
--- origin: string|nil, -- The default origin
|
||||
--- offset: Position|nil, -- The default offset
|
||||
--- body: string|ui.Line|ui.Text|nil, -- The default body
|
||||
--- origin: string?, -- The default origin
|
||||
--- offset: ui.Pos?, -- The default offset
|
||||
---}
|
||||
---@param is_plugin_options boolean|nil Whether the options are plugin specific
|
||||
---@param is_confirm boolean|nil Whether the component is the confirm component
|
||||
---@param title_index integer|nil The index to get the title
|
||||
---@param is_plugin_options boolean? Whether the options are plugin specific
|
||||
---@param is_confirm boolean? Whether the component is the confirm component
|
||||
---@param title_index integer? The index to get the title
|
||||
---@return YaziInputOptions|YaziConfirmOptions options The resolved options
|
||||
local function get_user_input_or_confirm_options(
|
||||
component,
|
||||
@@ -808,7 +837,7 @@ local function get_user_input_or_confirm_options(
|
||||
end
|
||||
|
||||
-- Unpack the options
|
||||
local title_option, origin_option, offset_option, content_option =
|
||||
local title_option, origin_option, offset_option, body_option =
|
||||
table.unpack(option_list)
|
||||
|
||||
-- Get the value of all the options
|
||||
@@ -818,7 +847,7 @@ local function get_user_input_or_confirm_options(
|
||||
or defaults.origin
|
||||
or default_options[1]
|
||||
local offset = theme_config[offset_option or ""] or {}
|
||||
local content = theme_config[content_option or ""] or defaults.content
|
||||
local body = theme_config[body_option or ""] or defaults.body
|
||||
|
||||
-- Get the title
|
||||
local title = type(raw_title) == "string" and raw_title
|
||||
@@ -837,16 +866,16 @@ local function get_user_input_or_confirm_options(
|
||||
-- Return the options
|
||||
return {
|
||||
title = title,
|
||||
[is_confirm and "pos" or "position"] = position,
|
||||
content = content,
|
||||
pos = position,
|
||||
body = body,
|
||||
}
|
||||
end
|
||||
|
||||
-- Function to get a password from the user
|
||||
---@param get_password_options GetPasswordOptions Get password options function
|
||||
---@param want_confirmation boolean|nil Whether to get a confirmation password
|
||||
---@return string|nil password The password or nil if the user cancelled
|
||||
---@return InputEvent|nil event The event for the input function
|
||||
---@param want_confirmation boolean? Whether to get a confirmation password
|
||||
---@return string? password The password or nil if the user cancelled
|
||||
---@return number? event The event for the input function
|
||||
local function get_password(get_password_options, want_confirmation)
|
||||
--
|
||||
|
||||
@@ -920,40 +949,38 @@ local function show_overwrite_prompt(file_path_to_overwrite)
|
||||
ConfigurableComponents.BuiltIn.Overwrite,
|
||||
{
|
||||
prompts = "Overwrite file?",
|
||||
content = ui.Line("Will overwrite the following file:"),
|
||||
body = ui.Line("Will overwrite the following file:"),
|
||||
},
|
||||
false,
|
||||
true
|
||||
)
|
||||
|
||||
-- Get the type of the overwrite content
|
||||
local overwrite_content_type = type(overwrite_confirm_options.content)
|
||||
-- Get the type of the overwrite body
|
||||
---@cast overwrite_confirm_options YaziConfirmOptions
|
||||
local overwrite_body_type = type(overwrite_confirm_options.body)
|
||||
|
||||
-- Initialise the first line of the content
|
||||
-- Initialise the first line of the body
|
||||
local first_line = nil
|
||||
|
||||
-- If the content section is a string
|
||||
if
|
||||
overwrite_content_type == "string"
|
||||
or overwrite_content_type == "table"
|
||||
then
|
||||
-- If the body section is a string
|
||||
if overwrite_body_type == "string" or overwrite_body_type == "table" then
|
||||
--
|
||||
|
||||
-- Wrap the string in a line and align it to the center.
|
||||
first_line = ui.Line(overwrite_confirm_options.content)
|
||||
first_line = ui.Line(overwrite_confirm_options.body)
|
||||
:align(ui.Align.CENTER)
|
||||
|
||||
-- Otherwise, just set the first line to the content given
|
||||
-- Otherwise, just set the first line to the body given
|
||||
else
|
||||
first_line = overwrite_confirm_options.content
|
||||
first_line = overwrite_confirm_options.body
|
||||
end
|
||||
|
||||
-- Create the content for the overwrite prompt
|
||||
-- Create the body for the overwrite prompt
|
||||
---@cast first_line ui.Line|ui.Span
|
||||
overwrite_confirm_options.content = ui.Text({
|
||||
overwrite_confirm_options.body = ui.Text({
|
||||
first_line,
|
||||
ui.Line(string.rep("─", overwrite_confirm_options.pos.w - 2))
|
||||
:style(ui.Style(th.confirm.border))
|
||||
:style(th.confirm.border)
|
||||
:align(ui.Align.LEFT),
|
||||
ui.Line(tostring(file_path_to_overwrite)):align(ui.Align.LEFT),
|
||||
}):wrap(ui.Wrap.TRIM)
|
||||
@@ -967,7 +994,7 @@ local function show_overwrite_prompt(file_path_to_overwrite)
|
||||
end
|
||||
|
||||
-- Function to merge the given configuration table with the default one
|
||||
---@param config UserConfiguration|nil The configuration table to merge
|
||||
---@param config UserConfiguration? The configuration table to merge
|
||||
---@return UserConfiguration merged_config The merged configuration table
|
||||
local function merge_configuration(config)
|
||||
--
|
||||
@@ -1069,7 +1096,7 @@ end
|
||||
|
||||
-- Function to initialise the configuration
|
||||
---@type fun(
|
||||
--- user_config: UserConfiguration|nil, -- The configuration object
|
||||
--- user_config: UserConfiguration?, -- The configuration object
|
||||
---): Configuration The initialised configuration object
|
||||
local initialise_config = ya.sync(function(state, user_config)
|
||||
--
|
||||
@@ -1090,7 +1117,7 @@ local initialise_config = ya.sync(function(state, user_config)
|
||||
end)
|
||||
|
||||
-- Function to initialise the theme configuration
|
||||
---@type fun(): Th
|
||||
---@type fun(): th
|
||||
local initialise_theme = ya.sync(function(state)
|
||||
--
|
||||
|
||||
@@ -1130,9 +1157,9 @@ end)
|
||||
|
||||
-- Function to try if a shell command exists
|
||||
---@param shell_command string The shell command to check
|
||||
---@param args string[]|nil The arguments to the shell command
|
||||
---@param args string[]? The arguments to the shell command
|
||||
---@return boolean shell_command_exists Whether the shell command exists
|
||||
---@return CommandOutput|nil output The output of the shell command
|
||||
---@return Output? output The output of the shell command
|
||||
local function async_shell_command_exists(shell_command, args)
|
||||
--
|
||||
|
||||
@@ -1194,9 +1221,9 @@ local subscribe_to_augmented_extract_event = ya.sync(function(_)
|
||||
end)
|
||||
|
||||
-- Function to initialise the plugin
|
||||
---@param opts UserConfiguration|nil The options given to the plugin
|
||||
---@param opts UserConfiguration? The options given to the plugin
|
||||
---@return Configuration config The initialised configuration object
|
||||
---@return Th theme The saved theme object
|
||||
---@return th theme The saved theme object
|
||||
local function initialise_plugin(opts)
|
||||
--
|
||||
|
||||
@@ -1246,7 +1273,7 @@ local function standardise_mime_type(mime_type)
|
||||
end
|
||||
|
||||
-- Function to check if a given mime type is an archive
|
||||
---@param mime_type string|nil The mime type of the file
|
||||
---@param mime_type string? The mime type of the file
|
||||
---@return boolean is_archive Whether the mime type is an archive
|
||||
local function is_archive_mime_type(mime_type)
|
||||
--
|
||||
@@ -1266,7 +1293,7 @@ end
|
||||
|
||||
-- Function to check if a given file extension
|
||||
-- is an archive file extension
|
||||
---@param file_extension string|nil The file extension of the file
|
||||
---@param file_extension string? The file extension of the file
|
||||
---@return boolean is_archive Whether the file extension is an archive
|
||||
local function is_archive_file_extension(file_extension)
|
||||
--
|
||||
@@ -1335,8 +1362,8 @@ end
|
||||
-- Function to get a temporary directory url
|
||||
-- for the given file path
|
||||
---@param path string The path to the item to create a temporary directory
|
||||
---@param destination_given boolean|nil Whether the destination was given
|
||||
---@return Url|nil url The url of the temporary directory
|
||||
---@param destination_given boolean? Whether the destination was given
|
||||
---@return Url? url The url of the temporary directory
|
||||
local function get_temporary_directory_url(path, destination_given)
|
||||
--
|
||||
|
||||
@@ -1377,8 +1404,8 @@ local get_current_directory = ya.sync(
|
||||
|
||||
-- Function to get the path of the hovered item
|
||||
---@type fun(
|
||||
--- quote: boolean|nil, -- Whether to escape the characters in the path
|
||||
---): string|nil The path of the hovered item
|
||||
--- quote: boolean?, -- Whether to escape the characters in the path
|
||||
---): string? The path of the hovered item
|
||||
local get_path_of_hovered_item = ya.sync(function(_, quote)
|
||||
--
|
||||
|
||||
@@ -1425,8 +1452,8 @@ end)
|
||||
|
||||
-- Function to get the paths of the selected items
|
||||
---@type fun(
|
||||
--- quote: boolean|nil, -- Whether to escape the characters in the path
|
||||
---): string[]|nil The list of paths of the selected items
|
||||
--- quote: boolean?, -- Whether to escape the characters in the path
|
||||
---): string[]? The list of paths of the selected items
|
||||
local get_paths_of_selected_items = ya.sync(function(_, quote)
|
||||
--
|
||||
|
||||
@@ -1463,7 +1490,7 @@ end)
|
||||
local get_number_of_tabs = ya.sync(function() return #cx.tabs end)
|
||||
|
||||
-- Function to get the tab preferences
|
||||
---@type fun(): tab.Preference
|
||||
---@type fun(): tab__Pref
|
||||
local get_tab_preferences = ya.sync(function(_)
|
||||
--
|
||||
|
||||
@@ -1488,7 +1515,7 @@ end)
|
||||
-- ItemGroup.Selected for the selected items,
|
||||
-- and ItemGroup.Prompt to tell the calling function
|
||||
-- to prompt the user.
|
||||
---@type fun(): ItemGroup|nil The desired item group
|
||||
---@type fun(): ItemGroup? The desired item group
|
||||
local get_item_group_from_state = ya.sync(function(state)
|
||||
--
|
||||
|
||||
@@ -1539,7 +1566,7 @@ local get_item_group_from_state = ya.sync(function(state)
|
||||
end)
|
||||
|
||||
-- Function to prompt the user for their desired item group
|
||||
---@return ItemGroup|nil item_group The item group selected by the user
|
||||
---@return ItemGroup? item_group The item group selected by the user
|
||||
local function prompt_for_desired_item_group()
|
||||
--
|
||||
|
||||
@@ -1547,7 +1574,7 @@ local function prompt_for_desired_item_group()
|
||||
local config = get_config()
|
||||
|
||||
-- Get the default item group
|
||||
---@type ItemGroup|nil
|
||||
---@type ItemGroup?
|
||||
local default_item_group = config.default_item_group_for_prompt
|
||||
|
||||
-- Get the input options, which the (h/s) options
|
||||
@@ -1598,7 +1625,7 @@ local function prompt_for_desired_item_group()
|
||||
end
|
||||
|
||||
-- Function to get the item group
|
||||
---@return ItemGroup|nil item_group The desired item group
|
||||
---@return ItemGroup? item_group The desired item group
|
||||
local function get_item_group()
|
||||
--
|
||||
|
||||
@@ -1619,7 +1646,7 @@ end
|
||||
-- Function to get all the items in the given directory
|
||||
---@param directory_path string The path to the directory
|
||||
---@param get_hidden_items boolean Whether to get hidden items
|
||||
---@param directories_only boolean|nil Whether to only get directories
|
||||
---@param directories_only boolean? Whether to only get directories
|
||||
---@return string[] directory_items The list of urls to the directory items
|
||||
local function get_directory_items(
|
||||
directory_path,
|
||||
@@ -1716,8 +1743,8 @@ end
|
||||
-- The function to create a new instance of the archiver
|
||||
---@param archive_path string The path to the archive
|
||||
---@param config Configuration The configuration object
|
||||
---@param destination_path string|nil The path to extract to
|
||||
---@return Archiver|nil instance An instance of the archiver if available
|
||||
---@param destination_path string? The path to extract to
|
||||
---@return Archiver? instance An instance of the archiver if available
|
||||
function Archiver:new(archive_path, config, destination_path)
|
||||
--
|
||||
|
||||
@@ -1774,7 +1801,7 @@ end
|
||||
-- Function to retry the archiver
|
||||
---@private
|
||||
---@param archiver_function Archiver.Command Archiver command to retry
|
||||
---@param clean_up_wanted boolean|nil Whether to clean up the destination path
|
||||
---@param clean_up_wanted boolean? Whether to clean up the destination path
|
||||
---@return Archiver.Result result Result of the archiver function
|
||||
function SevenZip:retry_archiver(archiver_function, clean_up_wanted)
|
||||
--
|
||||
@@ -1898,7 +1925,7 @@ function SevenZip:retry_archiver(archiver_function, clean_up_wanted)
|
||||
|
||||
-- Set the width of the component to the input width
|
||||
---@cast password_input_options YaziInputOptions
|
||||
password_input_options.position.w = input_width
|
||||
password_input_options.pos.w = input_width
|
||||
|
||||
-- Return the password input options
|
||||
return password_input_options
|
||||
@@ -1992,15 +2019,11 @@ function SevenZip:get_items()
|
||||
-- Get the output
|
||||
local output = archiver_result.output
|
||||
|
||||
-- Get the error
|
||||
local error = archiver_result.error
|
||||
|
||||
-- If the archiver command was not successful,
|
||||
-- or the output was nil,
|
||||
-- then return nil the error message,
|
||||
-- and nil as the correct password
|
||||
-- then return the result
|
||||
if not archiver_result.successful or not output then
|
||||
return files, directories, error
|
||||
return files, directories, archiver_result
|
||||
end
|
||||
|
||||
-- Otherwise, split the output at the newline character
|
||||
@@ -2042,16 +2065,15 @@ function SevenZip:get_items()
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- Return the list of files, the list of directories,
|
||||
-- the error message, and the password
|
||||
return files, directories, error
|
||||
-- Return the list of files, the list of directories and the result
|
||||
return files, directories, archiver_result
|
||||
end
|
||||
|
||||
-- Function to extract an archive using the command
|
||||
---@param extract_files_only boolean|nil Extract the files only or not
|
||||
---@param extract_behaviour ExtractBehaviour|nil The extraction behaviour
|
||||
---@return CommandOutput|nil output The output of the command
|
||||
---@return Error|nil error The error if any
|
||||
---@param extract_files_only boolean? Extract the files only or not
|
||||
---@param extract_behaviour ExtractBehaviour? The extraction behaviour
|
||||
---@return Output? output The output of the command
|
||||
---@return Error? error The error if any
|
||||
function SevenZip:extract_command(extract_files_only, extract_behaviour)
|
||||
--
|
||||
|
||||
@@ -2128,10 +2150,10 @@ end
|
||||
|
||||
-- Function to call the command to add items to an archive
|
||||
---@param item_paths string[] The path to the items being added to the archive
|
||||
---@param password string|nil The password to encrypt the archive with
|
||||
---@param encrypt_headers boolean|nil Whether to encrypt the archive headers
|
||||
---@return CommandOutput|nil output The output of the command
|
||||
---@return Error|nil error The error if any
|
||||
---@param password string? The password to encrypt the archive with
|
||||
---@param encrypt_headers boolean? Whether to encrypt the archive headers
|
||||
---@return Output? output The output of the command
|
||||
---@return Error? error The error if any
|
||||
function SevenZip:archive_command(item_paths, password, encrypt_headers)
|
||||
--
|
||||
|
||||
@@ -2241,8 +2263,15 @@ function Tar:get_items()
|
||||
---@type string[]
|
||||
local directories = {}
|
||||
|
||||
-- If there is no output, return the empty lists and the error
|
||||
if not output then return files, directories, tostring(error) end
|
||||
-- If there is no output, return the empty lists and the result
|
||||
if not output then
|
||||
return files,
|
||||
directories,
|
||||
{
|
||||
successful = false,
|
||||
error = tostring(error),
|
||||
}
|
||||
end
|
||||
|
||||
-- Otherwise, split the output into lines and iterate over it
|
||||
for _, line in ipairs(string_split(output.stdout, "\n")) do
|
||||
@@ -2268,11 +2297,16 @@ function Tar:get_items()
|
||||
end
|
||||
|
||||
-- Return the list of files and directories and the error
|
||||
return files, directories, output.stderr
|
||||
return files,
|
||||
directories,
|
||||
{
|
||||
successful = true,
|
||||
error = output.stderr,
|
||||
}
|
||||
end
|
||||
|
||||
-- Function to extract an archive using the command
|
||||
---@param extract_behaviour ExtractBehaviour|nil The extract behaviour to use
|
||||
---@param extract_behaviour ExtractBehaviour? The extract behaviour to use
|
||||
function Tar:extract_command(extract_behaviour)
|
||||
--
|
||||
|
||||
@@ -2437,8 +2471,8 @@ end
|
||||
---@param archive_path string The path to the archive file
|
||||
---@param command SupportedCommands The command the archiver is used for
|
||||
---@param config Configuration The configuration for the plugin
|
||||
---@param destination_path string|nil The path to the destination directory
|
||||
---@return Archiver|nil archiver The archiver for the file type
|
||||
---@param destination_path string? The path to the destination directory
|
||||
---@return Archiver? archiver The archiver for the file type
|
||||
---@return Archiver.Result result The results of getting the archiver
|
||||
local function get_archiver(archive_path, command, config, destination_path)
|
||||
--
|
||||
@@ -2536,7 +2570,7 @@ local function move_extracted_items(archive_url, destination_url)
|
||||
-- The function to clean up the destination directory
|
||||
-- and return the archiver result in the event of an error
|
||||
---@param error string The error message to return
|
||||
---@param empty_dir_only boolean|nil Whether to remove the empty dir only
|
||||
---@param empty_dir_only boolean? Whether to remove the empty dir only
|
||||
---@return Archiver.Result
|
||||
local function fail(error, empty_dir_only)
|
||||
--
|
||||
@@ -2675,7 +2709,7 @@ end
|
||||
---@param archive_path string The path to the archive
|
||||
---@param args Arguments The arguments passed to the plugin
|
||||
---@param config Configuration The configuration object
|
||||
---@param destination_path string|nil The destination path to extract to
|
||||
---@param destination_path string? The destination path to extract to
|
||||
---@return Archiver.Result extraction_result The extraction results
|
||||
local function recursively_extract_archive(
|
||||
archive_path,
|
||||
@@ -2739,21 +2773,13 @@ local function recursively_extract_archive(
|
||||
|
||||
-- Get the list of archive files and directories,
|
||||
-- the error message and the password
|
||||
local archive_files, archive_directories, error = archiver:get_items()
|
||||
local archive_files, archive_directories, archiver_result =
|
||||
archiver:get_items()
|
||||
|
||||
-- If there are no are no archive files and directories
|
||||
-- If there are no are no archive files and directories,
|
||||
-- return the extraction result
|
||||
if #archive_files == 0 and #archive_directories == 0 then
|
||||
--
|
||||
|
||||
-- The extraction result
|
||||
---@type Archiver.Result
|
||||
local extraction_result = {
|
||||
successful = false,
|
||||
error = error or "Archive is empty",
|
||||
}
|
||||
|
||||
-- Return the extraction result
|
||||
return add_additional_info(extraction_result)
|
||||
return add_additional_info(archiver_result)
|
||||
end
|
||||
|
||||
-- Get if the archive has only one file
|
||||
@@ -3274,7 +3300,7 @@ end
|
||||
|
||||
-- Function to enter or open the created file
|
||||
---@param item_url Url The url of the item to create
|
||||
---@param is_directory boolean|nil Whether the item to create is a directory
|
||||
---@param is_directory boolean? Whether the item to create is a directory
|
||||
---@param args Arguments The arguments passed to the plugin
|
||||
---@param config Configuration The configuration object
|
||||
---@return nil
|
||||
@@ -3890,18 +3916,18 @@ local function handle_quit(args, config)
|
||||
local quit_confirm_options =
|
||||
get_user_input_or_confirm_options(ConfigurableComponents.Plugin.Quit, {
|
||||
prompts = "Quit?",
|
||||
content = ui.Text({
|
||||
body = ui.Text({
|
||||
"There are multiple tabs open.",
|
||||
"Are you sure you want to quit?",
|
||||
}):wrap(ui.Wrap.TRIM),
|
||||
}, true, true)
|
||||
|
||||
-- Get the type of the quit content
|
||||
local quit_content_type = type(quit_confirm_options.content)
|
||||
-- Get the type of the quit body
|
||||
local quit_body_type = type(quit_confirm_options.body)
|
||||
|
||||
-- If the type of the quit content is a string or a list of strings
|
||||
if quit_content_type == "string" or quit_content_type == "table" then
|
||||
quit_confirm_options.content = ui.Text(quit_confirm_options.content)
|
||||
-- If the type of the quit body is a string or a list of strings
|
||||
if quit_body_type == "string" or quit_body_type == "table" then
|
||||
quit_confirm_options.body = ui.Text(quit_confirm_options.body)
|
||||
:wrap(ui.Wrap.TRIM)
|
||||
end
|
||||
|
||||
@@ -4641,7 +4667,7 @@ local function handle_editor(args, config)
|
||||
if not editor then return end
|
||||
|
||||
-- Initialise the shell command
|
||||
local shell_command = string.format(editor .. " $@")
|
||||
local shell_command = editor .. " %s"
|
||||
|
||||
-- Get the cha object of the hovered file
|
||||
local hovered_item_cha = fs.cha(
|
||||
@@ -4653,7 +4679,7 @@ local function handle_editor(args, config)
|
||||
-- and sudo edit is supported,
|
||||
-- set the shell command to "sudo -e"
|
||||
if config.sudo_edit_supported and hovered_item_cha.uid == 0 then
|
||||
shell_command = "sudo -e $@"
|
||||
shell_command = "sudo -e %s"
|
||||
end
|
||||
|
||||
-- Call the handle shell function
|
||||
@@ -4683,7 +4709,7 @@ local function handle_pager(args, config)
|
||||
-- with the pager command
|
||||
handle_shell(
|
||||
merge_tables({
|
||||
pager .. " $@",
|
||||
pager .. " %s",
|
||||
block = true,
|
||||
exit_if_dir = true,
|
||||
}, args),
|
||||
@@ -4725,7 +4751,7 @@ local function run_command_func(command, args, config)
|
||||
}
|
||||
|
||||
-- Get the function for the command
|
||||
---@type CommandFunction|nil
|
||||
---@type CommandFunction?
|
||||
local command_func = command_table[command]
|
||||
|
||||
-- If the function isn't found, notify the user and exit the function
|
||||
@@ -4738,7 +4764,7 @@ local function run_command_func(command, args, config)
|
||||
end
|
||||
|
||||
-- The setup function to setup the plugin
|
||||
---@param opts UserConfiguration|nil The options given to the plugin
|
||||
---@param opts UserConfiguration? The options given to the plugin
|
||||
---@return nil
|
||||
function M:setup(opts)
|
||||
--
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.5.31
|
||||
--- @since 25.12.29
|
||||
|
||||
local selected_or_hovered = ya.sync(function()
|
||||
local tab, paths = cx.active, {}
|
||||
@@ -11,6 +11,15 @@ local selected_or_hovered = ya.sync(function()
|
||||
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 })
|
||||
@@ -23,20 +32,16 @@ return {
|
||||
local value, event = ya.input {
|
||||
title = "Chmod:",
|
||||
pos = { "top-center", y = 3, w = 40 },
|
||||
position = { "top-center", y = 3, w = 40 }, -- TODO: remove
|
||||
}
|
||||
if event ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local status, err = Command("chmod"):arg(value):arg(urls):spawn():wait()
|
||||
if not status or not status.success then
|
||||
ya.notify {
|
||||
title = "Chmod",
|
||||
content = string.format("Chmod on selected files failed, error: %s", status and status.code or err),
|
||||
level = "error",
|
||||
timeout = 5,
|
||||
}
|
||||
local output, err = Command("chmod"):arg(value):arg(urls):stderr(Command.PIPED):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,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.2.7
|
||||
--- @since 26.1.22
|
||||
|
||||
local function info(content)
|
||||
return ya.notify {
|
||||
@@ -8,20 +8,20 @@ local function info(content)
|
||||
}
|
||||
end
|
||||
|
||||
local selected_url = ya.sync(function()
|
||||
local selected_path = ya.sync(function()
|
||||
for _, u in pairs(cx.active.selected) do
|
||||
return u
|
||||
return u.cache or u
|
||||
end
|
||||
end)
|
||||
|
||||
local hovered_url = ya.sync(function()
|
||||
local hovered_path = ya.sync(function()
|
||||
local h = cx.active.current.hovered
|
||||
return h and h.url
|
||||
return h and h.path
|
||||
end)
|
||||
|
||||
return {
|
||||
entry = function()
|
||||
local a, b = selected_url(), hovered_url()
|
||||
local a, b = selected_path(), hovered_path()
|
||||
if not a then
|
||||
return info("No file selected")
|
||||
elseif not b then
|
||||
|
||||
@@ -15,36 +15,41 @@ ya pkg add yazi-rs/plugins:git
|
||||
Add the following to your `~/.config/yazi/init.lua`:
|
||||
|
||||
```lua
|
||||
require("git"):setup()
|
||||
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"
|
||||
name = "*"
|
||||
run = "git"
|
||||
id = "git"
|
||||
url = "*"
|
||||
run = "git"
|
||||
|
||||
[[plugin.prepend_fetchers]]
|
||||
id = "git"
|
||||
name = "*/"
|
||||
run = "git"
|
||||
id = "git"
|
||||
url = "*/"
|
||||
run = "git"
|
||||
```
|
||||
|
||||
## Advanced
|
||||
|
||||
> [!NOTE]
|
||||
> [!NOTE]
|
||||
> The following configuration must be put before `require("git"):setup()`
|
||||
|
||||
You can customize the [Style](https://yazi-rs.github.io/docs/plugins/layout#style) of the status sign with:
|
||||
|
||||
- `th.git.modified`
|
||||
- `th.git.added`
|
||||
- `th.git.untracked`
|
||||
- `th.git.ignored`
|
||||
- `th.git.deleted`
|
||||
- `th.git.updated`
|
||||
- `th.git.unknown` - status cannot/not yet determined
|
||||
- `th.git.modified` - modified file
|
||||
- `th.git.added` - added file
|
||||
- `th.git.untracked` - untracked file
|
||||
- `th.git.ignored` - ignored file
|
||||
- `th.git.deleted` - deleted file
|
||||
- `th.git.updated` - updated file
|
||||
- `th.git.clean` - clean file
|
||||
|
||||
For example:
|
||||
|
||||
@@ -57,20 +62,24 @@ th.git.deleted = ui.Style():fg("red"):bold()
|
||||
|
||||
You can also customize the text of the status sign with:
|
||||
|
||||
- `th.git.modified_sign`
|
||||
- `th.git.added_sign`
|
||||
- `th.git.untracked_sign`
|
||||
- `th.git.ignored_sign`
|
||||
- `th.git.deleted_sign`
|
||||
- `th.git.updated_sign`
|
||||
- `th.git.unknown_sign` - status cannot/not yet determined
|
||||
- `th.git.modified_sign` - modified file
|
||||
- `th.git.added_sign` - added file
|
||||
- `th.git.untracked_sign` - untracked file
|
||||
- `th.git.ignored_sign` - ignored file
|
||||
- `th.git.deleted_sign` - deleted file
|
||||
- `th.git.updated_sign` - updated file
|
||||
- `th.git.clean_sign` - clean file
|
||||
|
||||
For example:
|
||||
|
||||
```lua
|
||||
-- ~/.config/yazi/init.lua
|
||||
th.git = th.git or {}
|
||||
th.git.unknown_sign = " "
|
||||
th.git.modified_sign = "M"
|
||||
th.git.deleted_sign = "D"
|
||||
th.git.clean_sign = "✔"
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.5.31
|
||||
--- @since 25.12.29
|
||||
|
||||
local WINDOWS = ya.target_family() == "windows"
|
||||
|
||||
@@ -7,14 +7,15 @@ local WINDOWS = ya.target_family() == "windows"
|
||||
-- see `bubble_up`
|
||||
---@enum CODES
|
||||
local CODES = {
|
||||
excluded = 100, -- ignored directory
|
||||
unknown = 100, -- status cannot/not yet determined
|
||||
excluded = 99, -- ignored directory
|
||||
ignored = 6, -- ignored file
|
||||
untracked = 5,
|
||||
modified = 4,
|
||||
added = 3,
|
||||
deleted = 2,
|
||||
updated = 1,
|
||||
unknown = 0,
|
||||
clean = 0,
|
||||
}
|
||||
|
||||
local PATTERNS = {
|
||||
@@ -79,7 +80,7 @@ local function bubble_up(changed)
|
||||
local url = Url(path).parent
|
||||
while url and url ~= empty do
|
||||
local s = tostring(url)
|
||||
new[s] = (new[s] or CODES.unknown) > code and new[s] or code
|
||||
new[s] = (new[s] or CODES.clean) > code and new[s] or code
|
||||
url = url.parent
|
||||
end
|
||||
end
|
||||
@@ -116,7 +117,7 @@ local add = ya.sync(function(st, cwd, repo, changed)
|
||||
st.dirs[cwd] = repo
|
||||
st.repos[repo] = st.repos[repo] or {}
|
||||
for path, code in pairs(changed) do
|
||||
if code == CODES.unknown then
|
||||
if code == CODES.clean then
|
||||
st.repos[repo][path] = nil
|
||||
elseif code == CODES.excluded then
|
||||
-- Mark the directory with a special value `excluded` so that it can be distinguished during UI rendering
|
||||
@@ -125,12 +126,7 @@ local add = ya.sync(function(st, cwd, repo, changed)
|
||||
st.repos[repo][path] = code
|
||||
end
|
||||
end
|
||||
-- TODO: remove this
|
||||
if ui.render then
|
||||
ui.render()
|
||||
else
|
||||
ya.render()
|
||||
end
|
||||
ui.render()
|
||||
end)
|
||||
|
||||
---@param cwd string
|
||||
@@ -142,12 +138,7 @@ local remove = ya.sync(function(st, cwd)
|
||||
return
|
||||
end
|
||||
|
||||
-- TODO: remove this
|
||||
if ui.render then
|
||||
ui.render()
|
||||
else
|
||||
ya.render()
|
||||
end
|
||||
ui.render()
|
||||
st.dirs[cwd] = nil
|
||||
if not st.repos[repo] then
|
||||
return
|
||||
@@ -172,31 +163,39 @@ local function setup(st, opts)
|
||||
|
||||
local t = th.git or {}
|
||||
local styles = {
|
||||
[CODES.ignored] = t.ignored and ui.Style(t.ignored) or ui.Style():fg("darkgray"),
|
||||
[CODES.untracked] = t.untracked and ui.Style(t.untracked) or ui.Style():fg("magenta"),
|
||||
[CODES.modified] = t.modified and ui.Style(t.modified) or ui.Style():fg("yellow"),
|
||||
[CODES.added] = t.added and ui.Style(t.added) or ui.Style():fg("green"),
|
||||
[CODES.deleted] = t.deleted and ui.Style(t.deleted) or ui.Style():fg("red"),
|
||||
[CODES.updated] = t.updated and ui.Style(t.updated) or ui.Style():fg("yellow"),
|
||||
[CODES.unknown] = t.unknown or ui.Style(),
|
||||
[CODES.ignored] = t.ignored or ui.Style():fg("darkgray"),
|
||||
[CODES.untracked] = t.untracked or ui.Style():fg("magenta"),
|
||||
[CODES.modified] = t.modified or ui.Style():fg("yellow"),
|
||||
[CODES.added] = t.added or ui.Style():fg("green"),
|
||||
[CODES.deleted] = t.deleted or ui.Style():fg("red"),
|
||||
[CODES.updated] = t.updated or ui.Style():fg("yellow"),
|
||||
[CODES.clean] = t.clean or ui.Style(),
|
||||
}
|
||||
local signs = {
|
||||
[CODES.ignored] = t.ignored_sign or "",
|
||||
[CODES.untracked] = t.untracked_sign or "?",
|
||||
[CODES.modified] = t.modified_sign or "",
|
||||
[CODES.added] = t.added_sign or "",
|
||||
[CODES.deleted] = t.deleted_sign or "",
|
||||
[CODES.updated] = t.updated_sign or "",
|
||||
[CODES.unknown] = t.unknown_sign or "",
|
||||
[CODES.ignored] = t.ignored_sign or " ",
|
||||
[CODES.untracked] = t.untracked_sign or "? ",
|
||||
[CODES.modified] = t.modified_sign or " ",
|
||||
[CODES.added] = t.added_sign or " ",
|
||||
[CODES.deleted] = t.deleted_sign or " ",
|
||||
[CODES.updated] = t.updated_sign or " ",
|
||||
[CODES.clean] = t.clean_sign or "",
|
||||
}
|
||||
|
||||
Linemode:children_add(function(self)
|
||||
local url = self._file.url
|
||||
local repo = st.dirs[tostring(url.base)]
|
||||
local code
|
||||
if repo then
|
||||
code = repo == CODES.excluded and CODES.ignored or st.repos[repo][tostring(url):sub(#repo + 2)]
|
||||
if not self._file.in_current then
|
||||
return ""
|
||||
end
|
||||
|
||||
if not code or signs[code] == "" then
|
||||
local url = self._file.url
|
||||
local repo = st.dirs[tostring(url.base or url.parent)]
|
||||
local code = CODES.unknown
|
||||
if repo then
|
||||
code = repo == CODES.excluded and CODES.ignored or st.repos[repo][tostring(url):sub(#repo + 2)] or CODES.clean
|
||||
end
|
||||
|
||||
if signs[code] == "" then
|
||||
return ""
|
||||
elseif self._file.is_hovered then
|
||||
return ui.Line { " ", signs[code] }
|
||||
@@ -208,7 +207,7 @@ end
|
||||
|
||||
---@type UnstableFetcher
|
||||
local function fetch(_, job)
|
||||
local cwd = job.files[1].url.base
|
||||
local cwd = job.files[1].url.base or job.files[1].url.parent
|
||||
local repo = root(cwd)
|
||||
if not repo then
|
||||
remove(tostring(cwd))
|
||||
@@ -246,11 +245,11 @@ local function fetch(_, job)
|
||||
end
|
||||
ya.dict_merge(changed, propagate_down(excluded, cwd, Url(repo)))
|
||||
|
||||
-- Reset the status of any files that don't appear in the output of `git status` to `unknown`,
|
||||
-- Reset the status of any files that don't appear in the output of `git status` to `clean`,
|
||||
-- so that cleaning up outdated statuses from `st.repos`
|
||||
for _, path in ipairs(paths) do
|
||||
local s = path:sub(#repo + 2)
|
||||
changed[s] = changed[s] or CODES.unknown
|
||||
changed[s] = changed[s] or CODES.clean
|
||||
end
|
||||
|
||||
add(tostring(cwd), repo, changed)
|
||||
|
||||
12
config/yazi/plugins/git.yazi/types.lua
Normal file
12
config/yazi/plugins/git.yazi/types.lua
Normal 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>
|
||||
@@ -40,7 +40,6 @@ using `ffmpeg` if available and media metadata using `mediainfo`.
|
||||
## Installation
|
||||
|
||||
- Install mediainfo CLI:
|
||||
|
||||
- [https://mediaarea.net/en/MediaInfo/Download](https://mediaarea.net/en/MediaInfo/Download)
|
||||
- Run this command in terminal to check if it's installed correctly:
|
||||
|
||||
@@ -52,7 +51,6 @@ using `ffmpeg` if available and media metadata using `mediainfo`.
|
||||
|
||||
- Install ImageMagick (for linux, you can use your distro package manager to install):
|
||||
https://imagemagick.org/script/download.php
|
||||
|
||||
- Install this plugin:
|
||||
|
||||
```bash
|
||||
@@ -74,7 +72,7 @@ Create `.../yazi/yazi.toml` and add:
|
||||
|
||||
> [!IMPORTANT]
|
||||
>
|
||||
> For yazi nightly replace `name` with `url`
|
||||
> For yazi (>=v25.12.29) replace `name` with `url`
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
@@ -120,3 +118,18 @@ title = { fg = "green" }
|
||||
# Example: `Format: FLAC` with blue color in preview images above
|
||||
tbl_col = { fg = "blue" }
|
||||
```
|
||||
|
||||
## (Optional) Keymaps to hide metadata and to preview images in full screen
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Use any key you want, but make sure there is no conflicts with [default Keybindings](https://github.com/sxyazi/yazi/blob/main/yazi-config/preset/keymap-default.toml).
|
||||
|
||||
Since Yazi prioritizes the first matching key, `prepend_keymap` takes precedence over defaults.
|
||||
Or you can use `keymap` to replace all other keys
|
||||
|
||||
```toml
|
||||
[mgr]
|
||||
prepend_keymap = [
|
||||
{ on = "<F9>", run = "plugin mediainfo -- toggle-metadata", desc = "Toggle media preview metadata" },
|
||||
]
|
||||
```
|
||||
|
||||
@@ -10,6 +10,16 @@ local skip_labels = {
|
||||
["MD5 of the unencoded content"] = true,
|
||||
}
|
||||
|
||||
local ENTRY_ACTION = {
|
||||
toggle_metadata = "toggle-metadata",
|
||||
}
|
||||
|
||||
local STATE_KEY = {
|
||||
units = "units",
|
||||
hide_metadata = "hide_metadata",
|
||||
prev_metadata_area = "prev_metadata_area",
|
||||
}
|
||||
|
||||
local magick_image_mimes = {
|
||||
avif = true,
|
||||
hei = true,
|
||||
@@ -18,8 +28,10 @@ local magick_image_mimes = {
|
||||
["heif-sequence"] = true,
|
||||
["heic-sequence"] = true,
|
||||
jxl = true,
|
||||
tiff = true,
|
||||
xml = true,
|
||||
["svg+xml"] = true,
|
||||
["canon-cr2"] = true,
|
||||
}
|
||||
|
||||
local seekable_mimes = {
|
||||
@@ -76,7 +88,9 @@ local function image_layer_count(job)
|
||||
if layer_count then
|
||||
return layer_count
|
||||
end
|
||||
local output, err = Command("identify"):arg({ tostring(job.file.url) }):output()
|
||||
local output, err = Command("identify")
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
if err then
|
||||
return 0
|
||||
end
|
||||
@@ -110,73 +124,85 @@ function M:peek(job)
|
||||
return
|
||||
end
|
||||
ya.sleep(math.max(0, rt.preview.image_delay / 1000 + start - os.clock()))
|
||||
local cache_mediainfo_path = tostring(cache_img_url_no_skip) .. suffix
|
||||
local output = read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
|
||||
local hide_metadata = get_state(STATE_KEY.hide_metadata)
|
||||
local mediainfo_height = 0
|
||||
local lines = {}
|
||||
local limit = job.area.h
|
||||
local last_line = 0
|
||||
local is_wrap = rt.preview.wrap == "yes"
|
||||
|
||||
if output then
|
||||
local max_width = math.max(1, job.area.w)
|
||||
if output:match("^Error:") then
|
||||
job.args.force_reload_mediainfo = true
|
||||
preload_status, preload_err = self:preload(job)
|
||||
if not preload_status or preload_err then
|
||||
return
|
||||
end
|
||||
output = read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
end
|
||||
|
||||
for str in output:gsub("\n+$", ""):gmatch("[^\n]*") do
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
if label then
|
||||
if not skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):style(ui.Style():fg("reset"):bold()),
|
||||
ui.Span(value):style(th.spot.tbl_col or ui.Style():fg("blue")),
|
||||
})
|
||||
if not hide_metadata then
|
||||
local cache_mediainfo_path = tostring(cache_img_url_no_skip) .. suffix
|
||||
local output = read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
if output then
|
||||
local max_width = math.max(1, job.area.w)
|
||||
if output:match("^Error:") then
|
||||
job.args.force_reload_mediainfo = true
|
||||
preload_status, preload_err = self:preload(job)
|
||||
if not preload_status or preload_err then
|
||||
return
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):style(th.spot.title or ui.Style():fg("green")) })
|
||||
output = read_mediainfo_cached_file(cache_mediainfo_path)
|
||||
end
|
||||
|
||||
if line then
|
||||
local line_height = math.max(1, is_wrap and math.ceil(ui.width(line) / max_width) or 1)
|
||||
if (last_line + line_height) > job.skip then
|
||||
table.insert(lines, line)
|
||||
for str in output:gsub("\n+$", ""):gmatch("[^\n]*") do
|
||||
local label, value = str:match("(.*[^ ]) +: (.*)")
|
||||
local line
|
||||
if label then
|
||||
if not skip_labels[label] then
|
||||
line = ui.Line({
|
||||
ui.Span(label .. ": "):style(ui.Style():fg("reset"):bold()),
|
||||
ui.Span(value):style(th.spot.tbl_col or ui.Style():fg("blue")),
|
||||
})
|
||||
end
|
||||
elseif str ~= "General" then
|
||||
line = ui.Line({ ui.Span(str):style(th.spot.title or ui.Style():fg("green")) })
|
||||
end
|
||||
if (last_line + line_height) >= job.skip + limit then
|
||||
last_line = job.skip + limit
|
||||
break
|
||||
|
||||
if line then
|
||||
local line_height = math.max(1, is_wrap and math.ceil(ui.width(line) / max_width) or 1)
|
||||
if (last_line + line_height) > job.skip then
|
||||
table.insert(lines, line)
|
||||
end
|
||||
if (last_line + line_height) >= job.skip + limit then
|
||||
last_line = job.skip + limit
|
||||
break
|
||||
end
|
||||
last_line = last_line + line_height
|
||||
end
|
||||
last_line = last_line + line_height
|
||||
end
|
||||
end
|
||||
mediainfo_height = math.min(limit, last_line)
|
||||
end
|
||||
local mediainfo_height = math.min(limit, last_line)
|
||||
|
||||
if
|
||||
(job.skip > 0 and #lines == 0)
|
||||
(job.skip > 0 and #lines == 0 and not hide_metadata)
|
||||
and (
|
||||
not is_seekable
|
||||
or (is_video and job.skip >= 90)
|
||||
or (
|
||||
(job.mime == "image/adobe.photoshop" or job.mime == "application/postscript")
|
||||
and image_layer_count(job)
|
||||
< (1 + math.floor(math.max(0, get_state("units") and (job.skip / get_state("units")) or 0)))
|
||||
< (1 + math.floor(
|
||||
math.max(0, get_state(STATE_KEY.units) and (job.skip / get_state(STATE_KEY.units)) or 0)
|
||||
))
|
||||
)
|
||||
)
|
||||
then
|
||||
ya.emit(
|
||||
"peek",
|
||||
{ math.max(0, job.skip - (get_state("units") or limit)), only_if = job.file.url, upper_bound = true }
|
||||
)
|
||||
ya.emit("peek", {
|
||||
math.max(0, job.skip - (get_state(STATE_KEY.units) or limit)),
|
||||
only_if = job.file.url,
|
||||
upper_bound = true,
|
||||
})
|
||||
return
|
||||
end
|
||||
force_render()
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
if hide_metadata and get_state(STATE_KEY.prev_metadata_area) then
|
||||
ya.preview_widget(job, {
|
||||
ui.Clear(ui.Rect(get_state(STATE_KEY.prev_metadata_area))),
|
||||
})
|
||||
ya.sleep(0.1)
|
||||
end
|
||||
local rendered_img_rect = cache_img_url
|
||||
and fs.cha(cache_img_url)
|
||||
and ya.image_show(
|
||||
@@ -189,7 +215,6 @@ function M:peek(job)
|
||||
})
|
||||
)
|
||||
or nil
|
||||
|
||||
local image_height = rendered_img_rect and rendered_img_rect.h or 0
|
||||
|
||||
-- NOTE: Workaround case audio has no cover image. Prevent regenerate preview image
|
||||
@@ -220,12 +245,19 @@ function M:peek(job)
|
||||
}))
|
||||
:wrap(is_wrap and ui.Wrap.YES or ui.Wrap.NO),
|
||||
})
|
||||
-- NOTE: Hacky way to prevent image overlap with old metadata area
|
||||
set_state(STATE_KEY.prev_metadata_area, not hide_metadata and {
|
||||
x = job.area.x,
|
||||
y = job.area.y + image_height,
|
||||
w = job.area.w,
|
||||
h = job.area.h - image_height,
|
||||
} or nil)
|
||||
end
|
||||
|
||||
function M:seek(job)
|
||||
local h = cx.active.current.hovered
|
||||
if h and h.url == job.file.url then
|
||||
set_state("units", job.units)
|
||||
set_state(STATE_KEY.units, job.units)
|
||||
ya.emit("peek", {
|
||||
math.max(0, cx.active.preview.skip + job.units),
|
||||
only_if = job.file.url,
|
||||
@@ -242,7 +274,7 @@ function M:preload(job)
|
||||
cache_img_url = seekable_mimes[job.mime] and ya.file_cache(job) or cache_img_url
|
||||
local cache_img_url_cha = cache_img_url and fs.cha(cache_img_url)
|
||||
local err_msg = ""
|
||||
local is_valid_utf8_path = is_valid_utf8(tostring(job.file.url))
|
||||
local is_valid_utf8_path = is_valid_utf8(tostring(job.file.path or job.file.cache or job.file.url))
|
||||
-- video mimetype
|
||||
if job.mime then
|
||||
if string.find(job.mime, "^video/") then
|
||||
@@ -261,15 +293,11 @@ function M:preload(job)
|
||||
"error",
|
||||
"-threads",
|
||||
1,
|
||||
"-hwaccel",
|
||||
"auto",
|
||||
"-skip_frame",
|
||||
"nokey",
|
||||
"-an",
|
||||
"-sn",
|
||||
"-dn",
|
||||
"-i",
|
||||
tostring(job.file.url),
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url),
|
||||
"-vframes",
|
||||
1,
|
||||
"-q:v",
|
||||
@@ -283,13 +311,17 @@ function M:preload(job)
|
||||
}):output()
|
||||
-- NOTE: Some audio types doesn't have cover image -> error ""
|
||||
if
|
||||
(audio_preload_output and audio_preload_output.stderr ~= nil and audio_preload_output.stderr ~= "")
|
||||
or audio_preload_err
|
||||
(
|
||||
audio_preload_output
|
||||
and audio_preload_output.stderr ~= nil
|
||||
and audio_preload_output.stderr ~= ""
|
||||
and not audio_preload_output.stderr:find("Output file does not contain any stream")
|
||||
) or audio_preload_err
|
||||
then
|
||||
err_msg = err_msg
|
||||
.. string.format("Failed to start `%s`, Do you have `%s` installed?\n", "ffmpeg", "ffmpeg")
|
||||
else
|
||||
cache_img_url_cha = fs.cha(cache_img_url)
|
||||
cache_img_url_cha, _ = fs.cha(cache_img_url)
|
||||
if not cache_img_url_cha then
|
||||
-- NOTE: Workaround case audio has no cover image. Prevent regenerate preview image
|
||||
audio_preload_output, audio_preload_err = require("magick")
|
||||
@@ -317,18 +349,18 @@ function M:preload(job)
|
||||
-- image
|
||||
elseif string.find(job.mime, "^image/") or job.mime == "application/postscript" then
|
||||
local svg_plugin_ok, svg_plugin = pcall(require, "svg")
|
||||
local _, magick_plugin = pcall(require, "magick")
|
||||
local magick_plugin_ok, magick_plugin = pcall(require, "magick")
|
||||
local mime = job.mime:match(".*/(.*)$")
|
||||
|
||||
local image_plugin = magick_image_mimes[mime]
|
||||
and ((mime == "svg+xml" and svg_plugin_ok) and svg_plugin or magick_plugin)
|
||||
and ((mime == "svg+xml" and svg_plugin_ok) and svg_plugin or (magick_plugin_ok and magick_plugin))
|
||||
or require("image")
|
||||
|
||||
local cache_img_status, image_preload_err
|
||||
-- psd, ai, eps
|
||||
if mime == "adobe.photoshop" or job.mime == "application/postscript" then
|
||||
local layer_index = 0
|
||||
local units = get_state("units")
|
||||
local units = get_state(STATE_KEY.units)
|
||||
if units ~= nil then
|
||||
local max_layer = image_layer_count(job)
|
||||
layer_index = math.floor(math.max(0, job.skip / units))
|
||||
@@ -346,7 +378,10 @@ function M:preload(job)
|
||||
:arg({
|
||||
"-background",
|
||||
"none",
|
||||
tostring(job.file.url) .. "[" .. tostring(layer_index) .. "]",
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url)
|
||||
.. "["
|
||||
.. tostring(layer_index)
|
||||
.. "]",
|
||||
"-auto-orient",
|
||||
"-strip",
|
||||
"-resize",
|
||||
@@ -371,7 +406,7 @@ function M:preload(job)
|
||||
:arg({
|
||||
"-background",
|
||||
"none",
|
||||
tostring(job.file.url),
|
||||
tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url),
|
||||
"-auto-orient",
|
||||
"-strip",
|
||||
"-flatten",
|
||||
@@ -403,15 +438,20 @@ function M:preload(job)
|
||||
local cmd = "mediainfo"
|
||||
local output, err
|
||||
if is_valid_utf8_path then
|
||||
output, err = Command(cmd):arg({ tostring(job.file.url) }):output()
|
||||
output, err = Command(cmd)
|
||||
:arg({ tostring(job.file.path or job.file.cache or job.file.url.path or job.file.url) })
|
||||
:output()
|
||||
else
|
||||
cmd = "cd "
|
||||
.. path_quote(job.file.url.parent)
|
||||
.. path_quote(job.file.path or job.file.cache or (job.file.url.path or job.file.url).parent)
|
||||
.. " && "
|
||||
.. cmd
|
||||
.. " "
|
||||
.. path_quote(tostring(job.file.url.name))
|
||||
output, err = Command(SHELL):arg({ "-c", cmd }):arg({ tostring(job.file.url) }):output()
|
||||
.. path_quote(tostring(job.file.path or job.file.cache or job.file.url.name))
|
||||
output, err = Command(SHELL)
|
||||
:arg({ "-c", cmd })
|
||||
:arg({ tostring(job.file.path or job.file.cache or (job.file.url.path or job.file.url)) })
|
||||
:output()
|
||||
end
|
||||
if err then
|
||||
err_msg = err_msg .. string.format("Failed to start `%s`, Do you have `%s` installed?\n", cmd, cmd)
|
||||
@@ -422,4 +462,15 @@ function M:preload(job)
|
||||
)
|
||||
end
|
||||
|
||||
function M:entry(job)
|
||||
local action = job.args[1]
|
||||
|
||||
if action == ENTRY_ACTION.toggle_metadata then
|
||||
set_state(STATE_KEY.hide_metadata, not get_state(STATE_KEY.hide_metadata))
|
||||
ya.emit("peek", {
|
||||
force = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.5.31
|
||||
--- @since 25.12.29
|
||||
|
||||
local toggle_ui = ya.sync(function(self)
|
||||
if self.children then
|
||||
@@ -7,12 +7,7 @@ local toggle_ui = ya.sync(function(self)
|
||||
else
|
||||
self.children = Modal:children_add(self, 10)
|
||||
end
|
||||
-- TODO: remove this
|
||||
if ui.render then
|
||||
ui.render()
|
||||
else
|
||||
ya.render()
|
||||
end
|
||||
ui.render()
|
||||
end)
|
||||
|
||||
local subscribe = ya.sync(function(self)
|
||||
@@ -23,12 +18,7 @@ end)
|
||||
local update_partitions = ya.sync(function(self, partitions)
|
||||
self.partitions = partitions
|
||||
self.cursor = math.max(0, math.min(self.cursor or 0, #self.partitions - 1))
|
||||
-- TODO: remove this
|
||||
if ui.render then
|
||||
ui.render()
|
||||
else
|
||||
ya.render()
|
||||
end
|
||||
ui.render()
|
||||
end)
|
||||
|
||||
local active_partition = ya.sync(function(self) return self.partitions[self.cursor + 1] end)
|
||||
@@ -39,12 +29,7 @@ local update_cursor = ya.sync(function(self, cursor)
|
||||
else
|
||||
self.cursor = ya.clamp(0, self.cursor + cursor, #self.partitions - 1)
|
||||
end
|
||||
-- TODO: remove this
|
||||
if ui.render then
|
||||
ui.render()
|
||||
else
|
||||
ya.render()
|
||||
end
|
||||
ui.render()
|
||||
end)
|
||||
|
||||
local M = {
|
||||
@@ -272,24 +257,33 @@ function M.operate(type)
|
||||
return -- TODO: mount/unmount main disk
|
||||
end
|
||||
|
||||
local output, err
|
||||
local cmd
|
||||
if ya.target_os() == "macos" then
|
||||
output, err = Command("diskutil"):arg({ type, active.src }):output()
|
||||
cmd = Command("diskutil"):arg { type, active.src }
|
||||
end
|
||||
if ya.target_os() == "linux" then
|
||||
if type == "eject" and active.src:match("^/dev/sr%d+") then
|
||||
Command("udisksctl"):arg({ "unmount", "-b", active.src }):status()
|
||||
output, err = Command("eject"):arg({ "--traytoggle", active.src }):output()
|
||||
cmd = Command("eject"):arg { "--traytoggle", active.src }
|
||||
elseif type == "eject" then
|
||||
Command("udisksctl"):arg({ "unmount", "-b", active.src }):status()
|
||||
output, err = Command("udisksctl"):arg({ "power-off", "-b", active.src }):output()
|
||||
cmd = Command("udisksctl"):arg { "power-off", "-b", active.src }
|
||||
else
|
||||
output, err = Command("udisksctl"):arg({ type, "-b", active.src }):output()
|
||||
cmd = Command("udisksctl"):arg { type, "-b", active.src }
|
||||
end
|
||||
end
|
||||
|
||||
if not cmd then
|
||||
return M.fail("mount.yazi is not currently supported on your platform")
|
||||
end
|
||||
|
||||
local output, err = cmd:output()
|
||||
if not output then
|
||||
M.fail("Failed to %s `%s`: %s", type, active.src, err)
|
||||
if cmd.program then
|
||||
M.fail("Failed to spawn `%s`: %s", cmd.program, err)
|
||||
else
|
||||
M.fail("Failed to spawn `udisksctl`: %s", err) -- TODO: remove
|
||||
end
|
||||
elseif not output.status.success then
|
||||
M.fail("Failed to %s `%s`: %s", type, active.src, output.stderr)
|
||||
end
|
||||
|
||||
@@ -35,29 +35,38 @@ Make sure you have [ouch](https://github.com/ouch-org/ouch) installed and in you
|
||||
For archive preview, add this to your `yazi.toml`:
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
prepend_previewers = [
|
||||
# Archive previewer
|
||||
{ mime = "application/*zip", run = "ouch" },
|
||||
{ mime = "application/x-tar", run = "ouch" },
|
||||
{ mime = "application/x-bzip2", run = "ouch" },
|
||||
{ mime = "application/x-7z-compressed", run = "ouch" },
|
||||
{ mime = "application/x-rar", run = "ouch" },
|
||||
{ mime = "application/vnd.rar", run = "ouch" },
|
||||
{ mime = "application/x-xz", run = "ouch" },
|
||||
{ mime = "application/xz", run = "ouch" },
|
||||
{ mime = "application/x-zstd", run = "ouch" },
|
||||
{ mime = "application/zstd", run = "ouch" },
|
||||
{ mime = "application/java-archive", run = "ouch" },
|
||||
]
|
||||
[[plugin.prepend_previewers]]
|
||||
mime = "application/{*zip,tar,bzip2,7z*,rar,xz,zstd,java-archive}"
|
||||
run = "ouch"
|
||||
```
|
||||
|
||||
Now go to an archive on Yazi, you should see the archive's content in the preview pane. You can use `J` and `K` to roll up and down the preview.
|
||||
|
||||
If you want to change the icon or the style of text, you can modify the `peek` function in `init.lua` file (all of them are stored in the `lines` variable).
|
||||
#### Customization
|
||||
|
||||
Previews can be customized by adding extra arguments in the `run` string:
|
||||
|
||||
```toml
|
||||
[plugin]
|
||||
prepend_previewers = [
|
||||
# Change the top-level archive icon
|
||||
{ ..., run = "ouch --archive-icon='🗄️ '" },
|
||||
# Or remove it by setting it to ''
|
||||
{ ..., run = "ouch --archive-icon=''" },
|
||||
|
||||
# Enable file icons
|
||||
{ ..., run = "ouch --show-file-icons" },
|
||||
|
||||
# Disable tree view
|
||||
{ ..., run = "ouch --list-view" },
|
||||
|
||||
# These can be combined
|
||||
{ ..., run = "ouch --archive-icon='🗄️ ' --show-file-icons --list-view" },
|
||||
]
|
||||
```
|
||||
|
||||
### Compression
|
||||
For compession, add this to your `keymap.toml`:
|
||||
For compression, add this to your `keymap.toml`:
|
||||
|
||||
```toml
|
||||
[[mgr.prepend_keymap]]
|
||||
|
||||
@@ -1,15 +1,50 @@
|
||||
local M = {}
|
||||
|
||||
-- Extract the tree prefix (if any) from a line
|
||||
local function get_tree_prefix(line)
|
||||
local _, prefix_len = line:find("─ ", 1, true)
|
||||
if prefix_len then
|
||||
return line:sub(1, prefix_len)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
-- Add a filetype icon to a line
|
||||
local function line_with_icon(line)
|
||||
line = line:gsub("[\r\n]+$", "") -- Trailing newlines mess with filetype detection
|
||||
|
||||
local tree_prefix = get_tree_prefix(line)
|
||||
local url = line:sub(#tree_prefix + 1)
|
||||
local icon = File({
|
||||
url = Url(url),
|
||||
cha = Cha {
|
||||
mode = tonumber(url:sub(-1) == "/" and "40700" or "100644", 8),
|
||||
kind = url:sub(-1) == "/" and 1 or 0, -- For Yazi <25.9.x compatibility
|
||||
}
|
||||
}):icon()
|
||||
|
||||
if icon then
|
||||
line = ui.Line { tree_prefix, ui.Span(icon.text .. " "):style(icon.style), url }
|
||||
end
|
||||
|
||||
return line
|
||||
end
|
||||
|
||||
function M:peek(job)
|
||||
local child = Command("ouch")
|
||||
:arg({ "l", "-t", "-y", tostring(job.file.url) })
|
||||
local cmd = Command("ouch"):arg("l")
|
||||
if not job.args.list_view then
|
||||
cmd:arg("-t")
|
||||
end
|
||||
cmd:arg({ "-y", tostring(job.file.url) })
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
:spawn()
|
||||
|
||||
local child = cmd:spawn()
|
||||
local limit = job.area.h
|
||||
local archive_icon = job.args.archive_icon or "\u{1f4c1} "
|
||||
local file_name = string.match(tostring(job.file.url), ".*[/\\](.*)")
|
||||
local lines = string.format("\u{1f4c1} %s\n", file_name)
|
||||
local num_lines = 1
|
||||
local lines = { string.format(" %s%s", archive_icon, file_name) }
|
||||
local num_skip = 0
|
||||
repeat
|
||||
local line, event = child:read_line()
|
||||
@@ -21,19 +56,29 @@ function M:peek(job)
|
||||
|
||||
if line:find('Archive', 1, true) ~= 1 and line:find('[INFO]', 1, true) ~= 1 then
|
||||
if num_skip >= job.skip then
|
||||
lines = lines .. line
|
||||
num_lines = num_lines + 1
|
||||
if job.args.show_file_icons then
|
||||
if line:find ('[ERROR]', 1, true) == 1 then
|
||||
-- On error, disable file icons for the rest of the output
|
||||
job.args.show_file_icons = false
|
||||
elseif line:find ('[WARNING]', 1, true) ~= 1 then
|
||||
-- Show icons for non-warning lines only
|
||||
line = line_with_icon(line)
|
||||
end
|
||||
end
|
||||
|
||||
line = ui.Line { " ", line } -- One space padding
|
||||
table.insert(lines, line)
|
||||
else
|
||||
num_skip = num_skip + 1
|
||||
end
|
||||
end
|
||||
until num_lines >= limit
|
||||
until #lines >= limit
|
||||
|
||||
child:start_kill()
|
||||
if job.skip > 0 and num_lines < limit then
|
||||
if job.skip > 0 and #lines < limit then
|
||||
ya.emit(
|
||||
"peek",
|
||||
{ tostring(math.max(0, job.skip - (limit - num_lines))), only_if = tostring(job.file.url), upper_bound = "" }
|
||||
{ tostring(math.max(0, job.skip - (limit - #lines))), only_if = tostring(job.file.url), upper_bound = "" }
|
||||
)
|
||||
else
|
||||
ya.preview_widget(job, { ui.Text(lines):area(job.area) })
|
||||
@@ -125,7 +170,6 @@ function M:entry(job)
|
||||
local output_name, name_event = ya.input({
|
||||
title = "Create archive:",
|
||||
value = default_name .. "." .. default_fmt,
|
||||
position = { "top-center", y = 3, w = 40 },
|
||||
pos = { "top-center", y = 3, w = 40 },
|
||||
})
|
||||
if name_event ~= 1 then
|
||||
@@ -136,7 +180,6 @@ function M:entry(job)
|
||||
if file_exists(output_name) then
|
||||
local confirm, confirm_event = ya.input({
|
||||
title = "Overwrite " .. output_name .. "? (y/N)",
|
||||
position = { "top-center", y = 3, w = 40 },
|
||||
pos = { "top-center", y = 3, w = 40 },
|
||||
})
|
||||
if not (confirm_event == 1 and confirm:lower() == "y") then
|
||||
|
||||
@@ -40,12 +40,12 @@ Add the below to your `yazi.toml` file to allow the respective file to previewed
|
||||
[plugin]
|
||||
|
||||
prepend_previewers = [
|
||||
{ name = "*.csv", run = "rich-preview"}, # for csv files
|
||||
{ name = "*.md", run = "rich-preview" }, # for markdown (.md) files
|
||||
{ name = "*.rst", run = "rich-preview"}, # for restructured text (.rst) files
|
||||
{ name = "*.ipynb", run = "rich-preview"}, # for jupyter notebooks (.ipynb)
|
||||
{ name = "*.json", run = "rich-preview"}, # for json (.json) files
|
||||
# { name = "*.lang_type", run = "rich-preview"} # for particular language files eg. .py, .go., .lua, etc.
|
||||
{ url = "*.csv", run = "rich-preview"}, # for csv files
|
||||
{ url = "*.md", run = "rich-preview" }, # for markdown (.md) files
|
||||
{ url = "*.rst", run = "rich-preview"}, # for restructured text (.rst) files
|
||||
{ url = "*.ipynb", run = "rich-preview"}, # for jupyter notebooks (.ipynb)
|
||||
{ url = "*.json", run = "rich-preview"}, # for json (.json) files
|
||||
# { url = "*.lang_type", run = "rich-preview"} # for particular language files eg. .py, .go., .lua, etc.
|
||||
]
|
||||
```
|
||||
|
||||
@@ -78,7 +78,7 @@ To use `rich` with piper, you can add this in your `yazi.toml` file:
|
||||
|
||||
```toml
|
||||
[[plugin.prepend_previewers]]
|
||||
name = "*.md"
|
||||
url = "*.md"
|
||||
run = 'piper -- rich -j --left --panel=rounded --guides --line-numbers --force-terminal "$1"'
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- @since 25.5.31
|
||||
--- @since 25.12.29
|
||||
|
||||
local hovered = ya.sync(function()
|
||||
local h = cx.active.current.hovered
|
||||
@@ -17,7 +17,6 @@ local function prompt()
|
||||
return ya.input {
|
||||
title = "Smart filter:",
|
||||
pos = { "center", w = 50 },
|
||||
position = { "center", w = 50 }, -- TODO: remove
|
||||
realtime = true,
|
||||
debounce = 0.1,
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ https://github.com/user-attachments/assets/6d2fc9e7-f86e-4444-aab6-4e11e51e8b34
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
ya pack -a iynaix/time-travel
|
||||
ya pkg add iynaix/time-travel
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The minimum required yazi version is 25.2.7.
|
||||
> The minimum required yazi version is 25.12.29.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -21,17 +21,17 @@ Add keymaps similar to the following to your `~/.config/yazi/keymap.toml`:
|
||||
```toml
|
||||
[[manager.prepend_keymap]]
|
||||
on = ["z", "h"]
|
||||
run = "plugin time-travel --args=prev"
|
||||
run = "plugin time-travel prev"
|
||||
desc = "Go to previous snapshot"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = ["z", "l"]
|
||||
run = "plugin time-travel --args=next"
|
||||
run = "plugin time-travel next"
|
||||
desc = "Go to next snapshot"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = ["z", "e"]
|
||||
run = "plugin time-travel --args=exit"
|
||||
run = "plugin time-travel exit"
|
||||
desc = "Exit browsing snapshots"
|
||||
```
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ end
|
||||
--- Verify if `sudo` is already authenticated
|
||||
--- @return boolean
|
||||
local function sudo_already()
|
||||
local status = Command("sudo"):args({ "--validate", "--non-interactive" }):status()
|
||||
local status = Command("sudo"):arg({ "--validate", "--non-interactive" }):status()
|
||||
assert(status, "Failed to run `sudo --validate --non-interactive`")
|
||||
return status.success
|
||||
end
|
||||
@@ -36,12 +36,12 @@ end
|
||||
--- nil: no error
|
||||
--- 1: sudo failed
|
||||
local function run_with_sudo(program, args)
|
||||
local cmd = Command("sudo"):args({ program, table.unpack(args) }):stdout(Command.PIPED):stderr(Command.PIPED)
|
||||
local cmd = Command("sudo"):arg({ program, table.unpack(args) }):stdout(Command.PIPED):stderr(Command.PIPED)
|
||||
if sudo_already() then
|
||||
return cmd:output()
|
||||
end
|
||||
|
||||
local permit = ya.hide()
|
||||
local permit = ui.hide()
|
||||
print(string.format("Sudo password required to run: `%s %s`", program, table.concat(args, " ")))
|
||||
local output = cmd:output()
|
||||
permit:drop()
|
||||
@@ -67,7 +67,7 @@ end
|
||||
---@param cwd string
|
||||
---@return string|nil
|
||||
local get_filesystem_type = function(cwd)
|
||||
local stat, _ = Command("stat"):args({ "-f", "-c", "%T", cwd }):output()
|
||||
local stat, _ = Command("stat"):arg({ "-f", "-c", "%T", cwd }):output()
|
||||
if not stat.status.success then
|
||||
return nil
|
||||
end
|
||||
@@ -77,7 +77,7 @@ end
|
||||
---@param cwd string
|
||||
---@return string|nil
|
||||
local zfs_dataset = function(cwd)
|
||||
local df, _ = Command("df"):args({ "--output=source", cwd }):output()
|
||||
local df, _ = Command("df"):arg({ "--output=source", cwd }):output()
|
||||
local dataset = nil
|
||||
for line in df.stdout:gmatch("[^\r\n]+") do
|
||||
-- dataset is last line in output
|
||||
@@ -89,7 +89,7 @@ end
|
||||
---@param dataset string
|
||||
---@return string|nil
|
||||
local zfs_mountpoint = function(dataset)
|
||||
local zfs, _ = Command("zfs"):args({ "get", "-H", "-o", "value", "mountpoint", dataset }):output()
|
||||
local zfs, _ = Command("zfs"):arg({ "get", "-H", "-o", "value", "mountpoint", dataset }):output()
|
||||
|
||||
-- not a dataset!
|
||||
if not zfs.status.success then
|
||||
@@ -157,7 +157,7 @@ end
|
||||
---@return Snapshot[]
|
||||
local zfs_snapshots = function(dataset, mountpoint, relative)
|
||||
-- -S is for reverse order
|
||||
local zfs_snapshots, _ = Command("zfs"):args({ "list", "-H", "-t", "snapshot", "-o", "name", "-S", "creation",
|
||||
local zfs_snapshots, _ = Command("zfs"):arg({ "list", "-H", "-t", "snapshot", "-o", "name", "-S", "creation",
|
||||
dataset })
|
||||
:output()
|
||||
|
||||
@@ -183,7 +183,7 @@ end
|
||||
---@param cwd string
|
||||
---@return string|nil
|
||||
local function btrfs_mountpoint(cwd)
|
||||
local cmd, _ = Command("findmnt"):args({ "-no", "TARGET", "-T", cwd }):output()
|
||||
local cmd, _ = Command("findmnt"):arg({ "-no", "TARGET", "-T", cwd }):output()
|
||||
if not cmd.status.success then
|
||||
return nil
|
||||
end
|
||||
@@ -329,7 +329,7 @@ return {
|
||||
end
|
||||
|
||||
if action == "exit" then
|
||||
ya.manager_emit("cd", { latest_path })
|
||||
ya.emit("cd", { latest_path })
|
||||
return
|
||||
end
|
||||
|
||||
@@ -343,7 +343,7 @@ return {
|
||||
local find_and_goto_snapshot = function(start_idx, end_idx, step)
|
||||
if start_idx == 0 then
|
||||
-- going from newest snapshot to current state
|
||||
return ya.manager_emit("cd", { latest_path })
|
||||
return ya.emit("cd", { latest_path })
|
||||
elseif start_idx < 0 then
|
||||
return notify_warn("No earlier snapshots found.")
|
||||
elseif start_idx > #snapshots then
|
||||
@@ -353,7 +353,7 @@ return {
|
||||
for i = start_idx, end_idx, step do
|
||||
local snapshot_dir = snapshots[i].path
|
||||
if io.open(snapshot_dir, "r") then
|
||||
return ya.manager_emit("cd", { snapshot_dir })
|
||||
return ya.emit("cd", { snapshot_dir })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
43
config/yazi/plugins/torrent-preview.yazi/init.lua
Normal file
43
config/yazi/plugins/torrent-preview.yazi/init.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
local M = {}
|
||||
|
||||
function M:peek(job)
|
||||
local child = Command("transmission-show")
|
||||
:arg(tostring(job.file.url))
|
||||
:stdout(Command.PIPED)
|
||||
:stderr(Command.PIPED)
|
||||
:spawn()
|
||||
|
||||
if not child then
|
||||
return require("code"):peek(job)
|
||||
end
|
||||
|
||||
local limit = job.area.h
|
||||
local i, lines = 0, ""
|
||||
repeat
|
||||
local next, event = child:read_line()
|
||||
if event == 1 then
|
||||
return require("code"):peek(job)
|
||||
elseif event ~= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
if i > job.skip then
|
||||
lines = lines .. next
|
||||
end
|
||||
until i >= job.skip + limit
|
||||
|
||||
child:start_kill()
|
||||
if job.skip > 0 and i < job.skip + limit then
|
||||
ya.manager_emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true })
|
||||
else
|
||||
lines = lines:gsub("\t", string.rep(" ", rt.preview.tab_size))
|
||||
ya.preview_widget(job, { ui.Text.parse(lines):area(job.area) })
|
||||
end
|
||||
end
|
||||
|
||||
function M:seek(job)
|
||||
require("code"):seek(job)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -14,31 +14,46 @@ what-size supports Yazi on Linux, macOS, and Windows.
|
||||
|
||||
### Yazi
|
||||
|
||||
- yazi `25.5.28` and onwards since commit `c5c939b` ([link](https://github.com/pirafrank/what-size.yazi/commit/c5c939bb37ec1d132c942cf5724d4e847acc2977))
|
||||
- yazi `25.x`-`25.4.8` since commit `fce1778` ([link](https://github.com/pirafrank/what-size.yazi/commit/fce1778d911621dc57796cdfdf11dcda3c2e28de))
|
||||
- yazi `0.4.x` since commit `2780de5` ([link](https://github.com/pirafrank/what-size.yazi/commit/2780de5aeef1ed16d1973dd6e0cd4d630c900d56))
|
||||
- yazi `0.3.x` up to commit `f08f7f2` ([link](https://github.com/pirafrank/what-size.yazi/commit/f08f7f2d5c94958ac4cb66c51a7c24b4319c6c93))
|
||||
In an effort to make things easy, I keep `compatibility/yazi-x.y.z` branches with each pointing to the most up-to-date commit compatible with yazi release `x.y.z`. Full table below.
|
||||
|
||||
|Yazi releases|what-size branch name|
|
||||
|---|---|
|
||||
|*[latest stable](https://github.com/sxyazi/yazi/releases/latest)*|`main`|
|
||||
|`25.5.28`|`compatibility/yazi-25.5.28`|
|
||||
|`25.x`-`25.4.8`|`compatibility/yazi-25.4.8`|
|
||||
|`0.4.x`|`compatibility/yazi-0.4.x`|
|
||||
|`0.3.x`|`compatibility/yazi-0.3.x`|
|
||||
|
||||
Please notice that `nightly` releses may work but are not explicitly supported.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Before Yazi's version 25.5.28
|
||||
|
||||
- Use this commit: [Old version](https://github.com/pirafrank/what-size.yazi/commit/d8966568f2a80394bf1f9a1ace6708ddd4cc8154)
|
||||
- `du` on Linux and macOS
|
||||
- PowerShell on Windows
|
||||
|
||||
### On Yazi's version 25.5.28 or newer
|
||||
|
||||
- No requirement
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
ya pkg add pirafrank/what-size
|
||||
```
|
||||
|
||||
or
|
||||
or (**DEPRECATED** - use only for yazi `25.4.8` and older):
|
||||
|
||||
**DEPRECATED**
|
||||
```sh
|
||||
ya pack -a 'pirafrank/what-size'
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Keymap
|
||||
|
||||
Add this to your `~/.config/yazi/keymap.toml`:
|
||||
|
||||
```toml
|
||||
@@ -54,18 +69,30 @@ If you want to copy the result to clipboard, you can add `--clipboard` or `-c` a
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ ".", "s" ]
|
||||
run = "plugin what-size -- '--clipboard'"
|
||||
desc = "Calc size of selection or cwd"
|
||||
desc = "Calc size of sel/cwd + paste to clipboard"
|
||||
```
|
||||
|
||||
```toml
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ ".", "s" ]
|
||||
run = "plugin what-size -- '-c'"
|
||||
desc = "Calc size of selection or cwd"
|
||||
desc = "Calc size of sel/cwd + paste to clipboard"
|
||||
```
|
||||
|
||||
Change to whatever keybinding you like.
|
||||
|
||||
### User interface (optional)
|
||||
|
||||
If you want to place the size value exactly where you want, modify the priority value. Also changing two strings `LEFT` and `RIGHT` will add them to the left and right side of the value. Remember to add to and change these lines inside your `init.lua` file if you want to customize, or the plugin will use this configuration by default:
|
||||
|
||||
```lua
|
||||
require("what-size"):setup({
|
||||
priority = 400,
|
||||
LEFT = "",
|
||||
RIGHT = " ",
|
||||
})
|
||||
```
|
||||
|
||||
## Feedback
|
||||
|
||||
If you have any feedback, suggestions, or ideas please let me know by opening an issue.
|
||||
@@ -82,6 +109,16 @@ YAZI_LOG=debug yazi
|
||||
|
||||
Logs will be saved to `~.local/state/yazi/yazi.log` file.
|
||||
|
||||
### Plugin definition
|
||||
|
||||
The repo already has a `.luarc.json` file. You only need to run the following to add the `types` plugin dependency:
|
||||
|
||||
```sh
|
||||
ya pkg add yazi-rs/plugins:types
|
||||
```
|
||||
|
||||
as per the [docs](https://github.com/yazi-rs/plugins/tree/main/types.yazi).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome. Please fork the repository and submit a PR.
|
||||
|
||||
@@ -1,102 +1,238 @@
|
||||
--- @since 25.5.28
|
||||
-- This plugin is now only supporting Yazi's version 25.5.28 or newer
|
||||
-- since commit https://github.com/sxyazi/yazi/pull/2695
|
||||
|
||||
-- function to get paths of selected elements or current directory
|
||||
-- if no elements are selected
|
||||
local get_paths = ya.sync(function()
|
||||
local paths = {}
|
||||
-- get selected files
|
||||
for _, u in pairs(cx.active.selected) do
|
||||
paths[#paths + 1] = tostring(u)
|
||||
end
|
||||
-- if no files are selected, get current directory
|
||||
if #paths == 0 then
|
||||
if cx.active.current.cwd then
|
||||
paths[1] = tostring(cx.active.current.cwd)
|
||||
else
|
||||
ya.err("what-size would return nil paths")
|
||||
-- TODO: Asynchronous calculating and dynamic displaying in statusline,
|
||||
-- perhaps by using this:
|
||||
-- https://yazi-rs.github.io/docs/plugins/utils/#ps.sub
|
||||
-- and by using ui.render() method
|
||||
-- See also:
|
||||
-- https://github.com/sxyazi/yazi/pull/1903
|
||||
-- https://yazi-rs.github.io/docs/dds/#kinds
|
||||
-- https://github.com/sxyazi/yazi/pull/2210
|
||||
-- https://github.com/imsi32/yatline.yazi
|
||||
-- TODO: Add options to choose displaying in popup box or in statusline
|
||||
-- TODO: Add spotter and previewer widget to support simpler displaying
|
||||
-- TODO: Remove note [1] and [2] after add them to the setup
|
||||
-- configuration
|
||||
|
||||
-- Get selected paths {{{1
|
||||
local get_selected_paths = ya.sync(function(state)
|
||||
local result = {}
|
||||
if cx and cx.active and cx.active.selected then
|
||||
for _, url in pairs(cx.active.selected) do
|
||||
result[#result + 1] = url
|
||||
end
|
||||
end
|
||||
end
|
||||
return paths
|
||||
return result
|
||||
end)
|
||||
|
||||
-- Function to get total size from output
|
||||
-- Unix use `du`, Windows use PowerShell
|
||||
-- }}}1
|
||||
-- Get current working directory in sync context {{{1
|
||||
local get_cwd = ya.sync(function(state)
|
||||
if cx and cx.active and cx.active.current and cx.active.current.cwd then
|
||||
return cx.active.current.cwd
|
||||
end
|
||||
return nil
|
||||
end)
|
||||
-- }}}1
|
||||
-- Function to get paths of selected files or current directory {{{1
|
||||
--- @param selected table Table of selected urls
|
||||
--- @return paths table Table of selected urls
|
||||
local function get_paths(selected)
|
||||
-- If no files are selected, get current directory
|
||||
if #selected == 0 then
|
||||
local paths = {}
|
||||
-- Try fs.cwd() first (async, optimized for slow devices)
|
||||
local cwd, err = fs.cwd()
|
||||
if cwd then
|
||||
paths[1] = cwd
|
||||
else
|
||||
-- Fallback to cx.active.current.cwd (via sync block)
|
||||
local sync_cwd = get_cwd()
|
||||
if sync_cwd then
|
||||
paths[1] = sync_cwd
|
||||
else
|
||||
ya.notify {
|
||||
title = "What size",
|
||||
content = "Cannot get current working directory: " .. (err or "unknown error"),
|
||||
timeout = 5,
|
||||
level = "error",
|
||||
}
|
||||
end
|
||||
end
|
||||
return paths
|
||||
else
|
||||
-- This variable is a table of urls already
|
||||
return selected
|
||||
end
|
||||
end
|
||||
-- }}}1
|
||||
-- Function to get total size using Yazi's fs.calc_size API {{{1
|
||||
-- See: https://github.com/sxyazi/yazi/pull/2695
|
||||
-- See: https://github.com/sxyazi/yazi/blob/main/yazi-plugin/preset/plugins/folder.lua
|
||||
local function get_total_size(items)
|
||||
local is_windows = package.config:sub(1,1) == '\\'
|
||||
|
||||
if is_windows then
|
||||
local total = 0
|
||||
for _, path in ipairs(items) do
|
||||
path = path:gsub('"', '\\"')
|
||||
local ps_cmd = string.format(
|
||||
[[powershell -Command "& { $p = '%s'; if (Test-Path $p) { if ((Get-ChildItem -Path $p -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum) { (Get-ChildItem -Path $p -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum } else { (Get-Item $p).Length } } }"]],
|
||||
path
|
||||
)
|
||||
local pipe = io.popen(ps_cmd)
|
||||
local result = pipe:read("*a")
|
||||
-- Debug
|
||||
-- ya.notify {
|
||||
-- title = "Debug Output",
|
||||
-- content = result,
|
||||
-- timeout = 5,
|
||||
-- }
|
||||
pipe:close()
|
||||
local num = tonumber(result)
|
||||
if num then total = total + num end
|
||||
for _, url in ipairs(items) do
|
||||
local it = fs.calc_size(url)
|
||||
while true do
|
||||
local next = it:recv()
|
||||
if next then
|
||||
total = total + next
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return total
|
||||
else
|
||||
local arg = ya.target_os() == "macos" and "-scA" or "-scb"
|
||||
-- pass args as string
|
||||
local cmd = Command("du"):arg(arg)
|
||||
for _, path in ipairs(items) do
|
||||
cmd = cmd:arg(path)
|
||||
end
|
||||
local output, err = cmd:output()
|
||||
|
||||
if not output then
|
||||
ya.err("Failed to run du: " .. err)
|
||||
end
|
||||
local lines = {}
|
||||
for line in output.stdout:gmatch("[^\n]+") do
|
||||
lines[#lines + 1] = line
|
||||
end
|
||||
local last_line = lines[#lines]
|
||||
local size = tonumber(last_line:match("^(%d+)"))
|
||||
return ya.target_os() == "macos" and size * 512 or size
|
||||
end
|
||||
end
|
||||
|
||||
-- Function to format file size
|
||||
-- }}}1
|
||||
-- Function to format files/folders size {{{1
|
||||
local function format_size(size)
|
||||
local units = { "B", "KB", "MB", "GB", "TB" }
|
||||
local unit_index = 1
|
||||
while size > 1024 and unit_index < #units do
|
||||
size = size / 1024
|
||||
unit_index = unit_index + 1
|
||||
end
|
||||
return string.format("%.2f %s", size, units[unit_index])
|
||||
end
|
||||
|
||||
return {
|
||||
-- as per doc: https://yazi-rs.github.io/docs/plugins/overview#functional-plugin
|
||||
entry = function(_, job)
|
||||
-- defaults not to use clipboard, use it only if required by the user
|
||||
local clipboard = job.args.clipboard == true or job.args[1] == "--clipboard" or job.args[1] == "-c"
|
||||
local items = get_paths()
|
||||
|
||||
local total_size = get_total_size(items)
|
||||
local formatted_size = format_size(total_size)
|
||||
|
||||
local notification_content = "Total size: " .. formatted_size
|
||||
if clipboard then
|
||||
ya.clipboard(formatted_size)
|
||||
notification_content = notification_content .. "\nCopied to clipboard."
|
||||
local units = { "B", "KB", "MB", "GB", "TB" }
|
||||
local unit_index = 1
|
||||
while size > 1024 and unit_index < #units do
|
||||
size = size / 1024
|
||||
unit_index = unit_index + 1
|
||||
end
|
||||
return string.format("%.2f %s", size, units[unit_index])
|
||||
end
|
||||
-- }}}1
|
||||
-- Generic setter for any state field {{{1
|
||||
local set_state = ya.sync(function(state, field, value)
|
||||
state[field] = value
|
||||
end)
|
||||
-- }}}1
|
||||
-- Generic getter for any state field {{{1
|
||||
local get_state = ya.sync(function(state, field)
|
||||
return state[field] or nil
|
||||
end)
|
||||
-- }}}1
|
||||
-- Get selecting state {{{1
|
||||
local get_selected = ya.sync(function()
|
||||
return (not cx.active.mode.is_visual) and (#cx.active.selected ~= 0)
|
||||
end)
|
||||
-- }}}1
|
||||
-- Set separators {{{1
|
||||
local set_separator = ya.sync(function(state, table)
|
||||
if table and table.LEFT and table.RIGHT then
|
||||
state.LEFT = table.LEFT
|
||||
state.RIGHT = table.RIGHT
|
||||
else
|
||||
state.LEFT = " "
|
||||
state.RIGHT = " "
|
||||
end
|
||||
end)
|
||||
-- }}}1
|
||||
-- Get separators {{{1
|
||||
local get_separator = ya.sync(function(state)
|
||||
return {state.LEFT, state.RIGHT}
|
||||
end)
|
||||
-- }}}1
|
||||
-- Redraw statusline {{{1
|
||||
local redraw_statusline = ya.sync(function(state)
|
||||
ui.render()
|
||||
end)
|
||||
-- }}}1
|
||||
-- Set ui line in statusline for size, clean up when no selection exists {{{1
|
||||
-- @return of get_state("renewed_state") number or nil Returning -1
|
||||
-- means never show the size - suitable for setup function;
|
||||
-- returning 0 means the size will be shown after triggering the
|
||||
-- calculation, but without unselect the selections, or it will be
|
||||
-- hidden after nothing is selected; returning 1 means hidden when
|
||||
-- nothing is selected as said.
|
||||
local set_ui_line = function(state)
|
||||
local sep_left, sep_right = table.unpack(get_separator())
|
||||
|
||||
ya.notify {
|
||||
title = "What size",
|
||||
content = notification_content,
|
||||
timeout = 4,
|
||||
}
|
||||
end,
|
||||
}
|
||||
if get_state("renewed_state") == -1 then
|
||||
return ""
|
||||
else
|
||||
if not get_selected() then
|
||||
if not get_state("is_held") then
|
||||
set_state("renewed_state", 1)
|
||||
return ""
|
||||
end
|
||||
-- NOTE [1]: Set this line if DON'T want to clear the value
|
||||
-- in the statusline when move the cursor, after calculating
|
||||
-- with NO selection(s). Or just return ""
|
||||
return ui.Span(sep_left .. get_state("size") .. sep_right)
|
||||
end
|
||||
if get_state("renewed_state") == 0 then
|
||||
return ui.Span(sep_left .. get_state("size") .. sep_right)
|
||||
else
|
||||
-- NOTE [2]: Set this line if want to clear the value in the
|
||||
-- statusline when move the cursor, after calculating WITH
|
||||
-- selection: return ui.Span(sep_left .. get_state("size") .. sep_right)
|
||||
-- or just remove after the unselection like below
|
||||
return ""
|
||||
end
|
||||
end
|
||||
end
|
||||
-- }}}1
|
||||
|
||||
--- @since 25.12.29
|
||||
return {
|
||||
entry = function(self, job)
|
||||
local clipboard = job.args.clipboard or job.args[1] == '-c'
|
||||
|
||||
local selected = get_selected_paths()
|
||||
local prepend_msg
|
||||
-- Keep showing the size after CWD calculation (no selections)
|
||||
if #selected == 0 then
|
||||
set_state("is_held", true)
|
||||
prepend_msg = "Current Dir: "
|
||||
else
|
||||
set_state("is_held", false)
|
||||
prepend_msg = "Selected: "
|
||||
end
|
||||
|
||||
local items = get_paths(selected)
|
||||
if not items or #items == 0 then
|
||||
ya.notify {
|
||||
title = "What size",
|
||||
content = "Failed to get paths",
|
||||
timeout = 5,
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
local total_size = get_total_size(items)
|
||||
if not total_size then
|
||||
ya.notify {
|
||||
title = "What size",
|
||||
content = "Failed to calculate size",
|
||||
timeout = 5,
|
||||
}
|
||||
return
|
||||
end
|
||||
|
||||
local formatted_size = format_size(total_size)
|
||||
|
||||
local notification_content = prepend_msg .. formatted_size
|
||||
if clipboard then
|
||||
ya.clipboard(formatted_size)
|
||||
notification_content = notification_content .. "\nCopied to clipboard."
|
||||
end
|
||||
ya.notify {
|
||||
title = "What size",
|
||||
content = notification_content,
|
||||
timeout = 4,
|
||||
}
|
||||
|
||||
set_state("size", formatted_size)
|
||||
set_state("renewed_state", 0)
|
||||
redraw_statusline()
|
||||
end,
|
||||
|
||||
setup = function(state, opts)
|
||||
opts = opts or {}
|
||||
local priority = opts.priority or 400
|
||||
set_separator(opts)
|
||||
set_state("renewed_state", -1)
|
||||
|
||||
if Status and type(Status.children_add) == "function" then
|
||||
Status:children_add(set_ui_line, priority, Status.RIGHT)
|
||||
else
|
||||
ya.err("Failed to initialize status bar: Status or children_add not available")
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# yatline.yazi
|
||||
|
||||
The first Yazi plugin for customizing both header-line and status-line.
|
||||
|
||||

|
||||
@@ -7,13 +8,16 @@ The first Yazi plugin for customizing both header-line and status-line.
|
||||
> Check out [wiki](https://github.com/imsi32/yatline.yazi/wiki) for installation steps, configuration and further information.
|
||||
|
||||
## Features
|
||||
|
||||
- Lualine-like Design
|
||||
- Flexible
|
||||
- Simple
|
||||
- Automatic Configuration
|
||||
- Support for Yazi Plugins
|
||||
- Themes (See: [yatline-themes](https://github.com/imsi32/yatline-themes))
|
||||
- Add-ons (See: [yatline-addons](https://github.com/imsi32/yatline-addons))
|
||||
|
||||
## Credits
|
||||
|
||||
- [Lualine](https://github.com/nvim-lualine/lualine.nvim)
|
||||
- [Yazi](https://github.com/sxyazi/yazi)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@ play = [
|
||||
[open]
|
||||
rules = [
|
||||
# Folder
|
||||
{name = "*/", use = [ "edit", "open", "reveal" ]},
|
||||
{url = "*/", use = [ "edit", "open", "reveal" ]},
|
||||
# Text
|
||||
{mime = "text/*", use = [ "edit", "reveal" ]},
|
||||
# Image
|
||||
@@ -71,7 +71,7 @@ rules = [
|
||||
# Empty file
|
||||
{mime = "inode/empty", use = [ "edit", "reveal" ]},
|
||||
# Fallback
|
||||
{name = "*", use = [ "open", "reveal" ]},
|
||||
{url = "*", use = [ "open", "reveal" ]},
|
||||
]
|
||||
|
||||
[tasks]
|
||||
@@ -89,14 +89,14 @@ preloaders = [
|
||||
{mime = "application/pdf", run = "pdf"},
|
||||
]
|
||||
previewers = [
|
||||
{name = "*/", run = "folder", sync = true},
|
||||
{url = "*/", run = "folder", sync = true},
|
||||
{mime = "text/*", run = "code"},
|
||||
{mime = "*/{xml,javascript,x-wine-extension-ini}", run = "code"},
|
||||
{mime = "application/json", run = "code"},
|
||||
{mime = "image/vnd.djvu", run = "noop"},
|
||||
{mime = "application/pdf", run = "pdf"},
|
||||
# Fallback
|
||||
{name = "*", run = "file"},
|
||||
{url = "*", run = "file"},
|
||||
]
|
||||
prepend_previewers = [
|
||||
{mime = "text/csv", run = "miller"},
|
||||
@@ -104,11 +104,11 @@ prepend_previewers = [
|
||||
{mime = "{audio,video,image}/*", run = "mediainfo"},
|
||||
{mime = "application/subrip", run = "mediainfo"},
|
||||
# rich preview
|
||||
{name = "*.csv", run = "rich-preview"}, # for csv files
|
||||
{name = "*.{md,mdx,markdown}", run = "rich-preview"}, # for markdown (.md) files
|
||||
{name = "*.rst", run = "rich-preview"}, # for restructured text (.rst) files
|
||||
{name = "*.ipynb", run = "rich-preview"}, # for jupyter notebooks (.ipynb)
|
||||
{name = "*.json", run = "rich-preview"}, # for json (.json) files
|
||||
{url = "*.csv", run = "rich-preview"}, # for csv files
|
||||
{url = "*.{md,mdx,markdown}", run = "rich-preview"}, # for markdown (.md) files
|
||||
{url = "*.rst", run = "rich-preview"}, # for restructured text (.rst) files
|
||||
{url = "*.ipynb", run = "rich-preview"}, # for jupyter notebooks (.ipynb)
|
||||
{url = "*.json", run = "rich-preview"}, # for json (.json) files
|
||||
{mime = "application/bittorrent", run = "torrent-preview"},
|
||||
# Archive previewer
|
||||
{mime = "application/*zip", run = "ouch"},
|
||||
@@ -127,16 +127,16 @@ prepend_previewers = [
|
||||
{mime = "application/oasis.opendocument.*", run = "office"},
|
||||
{mime = "application/ms-*", run = "office"},
|
||||
{mime = "application/msword", run = "office"},
|
||||
{name = "*.docx", run = "office"},
|
||||
{url = "*.docx", run = "office"},
|
||||
]
|
||||
|
||||
append_previewers = [
|
||||
{name = "*", run = "hexyl"},
|
||||
{url = "*", run = "hexyl"},
|
||||
]
|
||||
|
||||
prepend_fetchers = [
|
||||
{id = "git", name = "*", run = "git"},
|
||||
{id = "git", name = "*/", run = "git"},
|
||||
{id = "git", url = "*", run = "git"},
|
||||
{id = "git", url = "*/", run = "git"},
|
||||
]
|
||||
|
||||
[input]
|
||||
|
||||
Reference in New Issue
Block a user