mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Merge branch 'sxyazi:main' into add-create-function-to-fs-module
This commit is contained in:
commit
560b7752a8
160 changed files with 2022 additions and 1273 deletions
54
.github/DISCUSSION_TEMPLATE/1-q-a.yml
vendored
Normal file
54
.github/DISCUSSION_TEMPLATE/1-q-a.yml
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
body:
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: What system are you running Yazi on?
|
||||
options:
|
||||
- Linux X11
|
||||
- Linux Wayland
|
||||
- macOS
|
||||
- Windows
|
||||
- Windows WSL
|
||||
- FreeBSD X11
|
||||
- FreeBSD Wayland
|
||||
- Android
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: terminal
|
||||
attributes:
|
||||
label: What terminal are you running Yazi in?
|
||||
placeholder: "ex: kitty v0.32.2"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: debug
|
||||
attributes:
|
||||
label: "`yazi --debug` output"
|
||||
description: Please run `yazi --debug` and paste the debug information here.
|
||||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the question
|
||||
description: A clear and concise description of what the question is
|
||||
placeholder: Tell us what you want to know
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: other
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: |
|
||||
Add any other context about the problem here. You can attach screenshots by clicking
|
||||
this area to highlight it and then drag the files in.
|
||||
- type: checkboxes
|
||||
id: validations
|
||||
attributes:
|
||||
label: Validations
|
||||
description: Before submitting the post, please make sure you have completed the following
|
||||
options:
|
||||
- label: I have searched the existing discussions/issues
|
||||
required: true
|
||||
21
.github/ISSUE_TEMPLATE/bug.yml
vendored
21
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
|
@ -15,6 +15,7 @@ body:
|
|||
- Windows WSL
|
||||
- FreeBSD X11
|
||||
- FreeBSD Wayland
|
||||
- Android
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
|
@ -32,15 +33,6 @@ body:
|
|||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: tried_main
|
||||
attributes:
|
||||
label: Did you try the latest nightly build to see if the problem got fixed?
|
||||
options:
|
||||
- Yes, and I updated the debug information above (`yazi --debug`) to the nightly that I tried
|
||||
- No, and I'll explain why below
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
|
@ -65,3 +57,14 @@ body:
|
|||
description: |
|
||||
Add any other context about the problem here. You can attach screenshots by clicking
|
||||
this area to highlight it and then drag the files in.
|
||||
- type: checkboxes
|
||||
id: validations
|
||||
attributes:
|
||||
label: Validations
|
||||
description: Before submitting the issue, please make sure you have completed the following
|
||||
options:
|
||||
- label: I tried the [latest nightly build](https://yazi-rs.github.io/docs/installation#official-binaries), and the issue is still reproducible
|
||||
required: true
|
||||
- label: I updated the debug information (`yazi --debug`) input box to the nightly that I tried
|
||||
required: true
|
||||
- label: I can reproduce it after disabling all custom configs/plugins (`mv ~/.config/yazi ~/.config/yazi-backup`)
|
||||
|
|
|
|||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -3,9 +3,9 @@ contact_links:
|
|||
- name: 📝 Documentation Improvement
|
||||
url: https://github.com/yazi-rs/yazi-rs.github.io
|
||||
about: If you'd like to help improve the documentation
|
||||
- name: 💬 GitHub Discussions
|
||||
url: https://github.com/sxyazi/yazi/discussions/new?category=1-q-a
|
||||
about: When you have questions that are not bug reports or feature requests
|
||||
- name: 🌐 Discord Server / Telegram Group
|
||||
url: https://github.com/sxyazi/yazi#discussion
|
||||
about: If you'd prefer more realtime conversation with the community
|
||||
- name: 💬 GitHub Discussions
|
||||
url: https://github.com/sxyazi/yazi/discussions
|
||||
about: When you have questions that are not bug reports or feature requests
|
||||
|
|
|
|||
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
|
|
@ -45,5 +45,5 @@ body:
|
|||
options:
|
||||
- label: I have searched the existing issues/discussions
|
||||
required: true
|
||||
- label: The latest nightly build of Yazi doesn't already have this feature
|
||||
- label: The [latest nightly build](https://yazi-rs.github.io/docs/installation/#official-binaries) doesn't already have this feature
|
||||
required: true
|
||||
|
|
|
|||
6
.github/workflows/check.yml
vendored
6
.github/workflows/check.yml
vendored
|
|
@ -19,6 +19,9 @@ jobs:
|
|||
|
||||
- name: Setup Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: rust
|
||||
shared-key: ubuntu-latest@debug
|
||||
|
||||
- name: Clippy
|
||||
run: cargo clippy --all
|
||||
|
|
@ -36,6 +39,9 @@ jobs:
|
|||
|
||||
- name: Setup Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: rust
|
||||
shared-key: ubuntu-latest@debug
|
||||
|
||||
- name: Rustfmt
|
||||
run: cargo +nightly fmt --all -- --check
|
||||
|
|
|
|||
37
.github/workflows/draft.yml
vendored
37
.github/workflows/draft.yml
vendored
|
|
@ -32,6 +32,15 @@ jobs:
|
|||
echo "JEMALLOC_SYS_WITH_LG_PAGE=16" >> $GITHUB_ENV
|
||||
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
|
||||
|
||||
- name: Setup Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: rust
|
||||
shared-key: ${{ matrix.target }}@release
|
||||
|
||||
- name: Build
|
||||
run: ./scripts/build.sh ${{ matrix.target }}
|
||||
|
||||
|
|
@ -54,10 +63,13 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
run: rustup toolchain install stable --profile minimal
|
||||
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
|
||||
|
||||
- name: Add target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
- name: Setup Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: rust
|
||||
shared-key: ${{ matrix.target }}@release
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
|
|
@ -98,6 +110,15 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Add musl target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
|
||||
- name: Setup Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: rust
|
||||
shared-key: ${{ matrix.target }}@release
|
||||
|
||||
- name: Build
|
||||
run: ./scripts/build.sh ${{ matrix.target }}
|
||||
|
||||
|
|
@ -117,10 +138,16 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build snap
|
||||
uses: snapcore/action-build@v1
|
||||
- name: Setup Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: rust
|
||||
shared-key: ${{ matrix.target }}@release
|
||||
|
||||
- name: Build
|
||||
uses: snapcore/action-build@v1
|
||||
|
||||
- name: Rename snap
|
||||
run: mv yazi_*.snap yazi-${{ matrix.target }}.snap
|
||||
|
||||
- name: Upload artifact
|
||||
|
|
|
|||
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
I'm going to lock this issue because it has been closed for _30 days_. ⏳
|
||||
|
||||
This helps our maintainers find and focus on the active issues.
|
||||
If you have found a problem that seems similar to this, please open a new
|
||||
If you have found a problem that seems similar to this, please file a new
|
||||
issue and complete the issue template so we can capture all the details
|
||||
necessary to investigate further.
|
||||
pr-inactive-days: "30"
|
||||
|
|
|
|||
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
|
|
@ -23,6 +23,9 @@ jobs:
|
|||
|
||||
- name: Setup Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: rust
|
||||
shared-key: ${{ matrix.os }}@debug
|
||||
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
|
|
|||
287
Cargo.lock
generated
287
Cargo.lock
generated
|
|
@ -61,9 +61,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ansi-to-tui"
|
||||
version = "6.0.0"
|
||||
version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00c4af0bef1b514c9b6a32a773caf604c1390fa7913f4eaa23bfe76f251d6a42"
|
||||
checksum = "67555e1f1ece39d737e28c8a017721287753af3f93225e4a445b29ccb0f5912c"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"ratatui",
|
||||
|
|
@ -74,9 +74,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.15"
|
||||
version = "0.6.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
|
|
@ -89,43 +89,43 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.4"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.89"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
|
|
@ -133,12 +133,6 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||
|
||||
[[package]]
|
||||
name = "arg_enum_proc_macro"
|
||||
version = "0.3.4"
|
||||
|
|
@ -147,7 +141,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -187,9 +181,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "avif-serialize"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2"
|
||||
checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
|
@ -288,9 +282,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4"
|
||||
checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
|
|
@ -300,9 +294,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
|
||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
|
@ -318,9 +312,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.2"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
|
|
@ -339,9 +333,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.30"
|
||||
version = "1.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||
checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
|
@ -400,9 +394,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.33"
|
||||
version = "4.5.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9646e2e245bf62f45d39a0f3f36f1171ad1ea0d6967fd114bca72cb02a8fcdfb"
|
||||
checksum = "07a13ab5b8cb13dbe35e68b83f6c12f9293b2f601797b71bc9f23befdb329feb"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
|
@ -436,7 +430,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -462,9 +456,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
|||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
|
|
@ -561,7 +555,7 @@ dependencies = [
|
|||
"filedescriptor",
|
||||
"futures-core",
|
||||
"libc",
|
||||
"mio 1.0.2",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"signal-hook",
|
||||
|
|
@ -615,7 +609,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -626,7 +620,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
|||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -656,7 +650,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -666,7 +660,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -773,9 +767,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab"
|
||||
checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
|
@ -825,9 +819,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
|
@ -918,7 +912,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1084,9 +1078,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.2"
|
||||
version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10"
|
||||
checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
|
|
@ -1106,9 +1100,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "image-webp"
|
||||
version = "0.1.3"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904"
|
||||
checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
|
||||
dependencies = [
|
||||
"byteorder-lite",
|
||||
"quick-error",
|
||||
|
|
@ -1122,9 +1116,9 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
|
|||
|
||||
[[package]]
|
||||
name = "imgref"
|
||||
version = "1.10.1"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
|
||||
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
|
|
@ -1137,6 +1131,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.10.2"
|
||||
|
|
@ -1164,7 +1164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1184,7 +1184,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1235,15 +1235,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kamadak-exif"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef4fc70d0ab7e5b6bafa30216a6b48705ea964cdfc29c050f2412295eba58077"
|
||||
dependencies = [
|
||||
"mutate_once",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
|
|
@ -1278,9 +1269,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.159"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
|
|
@ -1413,18 +1404,6 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.2"
|
||||
|
|
@ -1458,9 +1437,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebe026d6bd1583a9cf9080e189030ddaea7e6f5f0deb366a8e26f8a26c4135b8"
|
||||
checksum = "e9eebac25c35a13285456c88ee2fde93d9aee8bcfdaf03f9d6d12be3391351ec"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
|
|
@ -1481,15 +1460,9 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mutate_once"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
|
|
@ -1513,10 +1486,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
|
||||
[[package]]
|
||||
name = "notify-fork"
|
||||
version = "6.1.1"
|
||||
name = "notify"
|
||||
version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f610737468f7a95610d2b320b5d5e246b3cd1c3603782680c0f454ac5825b64b"
|
||||
checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"filetime",
|
||||
|
|
@ -1525,17 +1498,17 @@ dependencies = [
|
|||
"kqueue",
|
||||
"libc",
|
||||
"log",
|
||||
"mio 0.8.11",
|
||||
"notify-types-fork",
|
||||
"mio",
|
||||
"notify-types",
|
||||
"walkdir",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-types-fork"
|
||||
name = "notify-types"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc01866a421a4ce1eddc764da8a95b30b82aa0d9d7f33508265c196d4863b18"
|
||||
checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
|
@ -1574,7 +1547,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1750,9 +1723,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
|||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
|
@ -1833,30 +1806,30 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.87"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling"
|
||||
version = "1.0.15"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
|
||||
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
|
||||
dependencies = [
|
||||
"profiling-procmacros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling-procmacros"
|
||||
version = "1.0.15"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
||||
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1924,23 +1897,23 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.28.1"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d"
|
||||
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"crossterm",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools 0.13.0",
|
||||
"lru",
|
||||
"paste",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"unicode-segmentation",
|
||||
"unicode-truncate",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1980,9 +1953,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ravif"
|
||||
version = "0.11.10"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd"
|
||||
checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6"
|
||||
dependencies = [
|
||||
"avif-serialize",
|
||||
"imgref",
|
||||
|
|
@ -2034,9 +2007,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
@ -2066,9 +2039,6 @@ name = "rgb"
|
|||
version = "0.8.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
|
|
@ -2084,9 +2054,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.37"
|
||||
version = "0.38.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
|
|
@ -2097,9 +2067,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
|
|
@ -2124,9 +2094,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
version = "1.0.213"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
|
@ -2143,20 +2113,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
version = "1.0.213"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.128"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
|
@ -2211,7 +2181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio 1.0.2",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
|
|
@ -2322,7 +2292,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2337,9 +2307,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.79"
|
||||
version = "2.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2388,22 +2358,22 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.64"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.64"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2486,14 +2456,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.40.0"
|
||||
version = "1.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio 1.0.2",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
|
|
@ -2510,7 +2480,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2603,7 +2573,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2643,9 +2613,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "trash"
|
||||
version = "5.1.1"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33caf2a9be1812a263a4bfce74d2de225fcde12ee7b77001361abd2b34ffdcc4"
|
||||
checksum = "defe1fdd4232e407b312377885a2c5396764972bddad87baf304753374a1bfc8"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"libc",
|
||||
|
|
@ -2705,7 +2675,7 @@ checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
|||
dependencies = [
|
||||
"itertools 0.13.0",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2714,6 +2684,12 @@ version = "0.1.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
|
|
@ -2785,7 +2761,7 @@ dependencies = [
|
|||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2882,7 +2858,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
|
@ -2904,7 +2880,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
|
@ -3003,7 +2979,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3014,7 +2990,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3195,14 +3171,12 @@ version = "0.3.3"
|
|||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"base64",
|
||||
"color_quant",
|
||||
"crossterm",
|
||||
"futures",
|
||||
"image",
|
||||
"imagesize",
|
||||
"kamadak-exif",
|
||||
"ratatui",
|
||||
"scopeguard",
|
||||
"tokio",
|
||||
|
|
@ -3255,7 +3229,7 @@ name = "yazi-codegen"
|
|||
version = "0.3.3"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3263,7 +3237,6 @@ name = "yazi-config"
|
|||
version = "0.3.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"bitflags 2.6.0",
|
||||
"crossterm",
|
||||
"globset",
|
||||
|
|
@ -3287,7 +3260,7 @@ dependencies = [
|
|||
"dirs",
|
||||
"futures",
|
||||
"libc",
|
||||
"notify-fork",
|
||||
"notify",
|
||||
"parking_lot",
|
||||
"ratatui",
|
||||
"scopeguard",
|
||||
|
|
@ -3296,7 +3269,7 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.0",
|
||||
"yazi-adapter",
|
||||
"yazi-boot",
|
||||
"yazi-codegen",
|
||||
|
|
@ -3403,7 +3376,7 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"unicode-width",
|
||||
"unicode-width 0.2.0",
|
||||
"uzers",
|
||||
"yazi-adapter",
|
||||
"yazi-boot",
|
||||
|
|
@ -3495,7 +3468,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.79",
|
||||
"syn 2.0.85",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
19
Cargo.toml
19
Cargo.toml
|
|
@ -10,9 +10,8 @@ panic = "abort"
|
|||
strip = true
|
||||
|
||||
[workspace.dependencies]
|
||||
ansi-to-tui = "6.0.0"
|
||||
anyhow = "1.0.89"
|
||||
arc-swap = "1.7.1"
|
||||
ansi-to-tui = "7.0.0"
|
||||
anyhow = "1.0.91"
|
||||
base64 = "0.22.1"
|
||||
bitflags = "2.6.0"
|
||||
clap = { version = "4.5.20", features = [ "derive" ] }
|
||||
|
|
@ -20,19 +19,19 @@ crossterm = { version = "0.28.1", features = [ "event-stream" ] }
|
|||
dirs = "5.0.1"
|
||||
futures = "0.3.31"
|
||||
globset = "0.4.15"
|
||||
libc = "0.2.159"
|
||||
libc = "0.2.161"
|
||||
md-5 = "0.10.6"
|
||||
mlua = { version = "0.9.9", features = [ "lua54", "serialize", "macros", "async" ] }
|
||||
parking_lot = "0.12.3"
|
||||
ratatui = { version = "0.28.1", features = [ "unstable-rendered-line-info" ] }
|
||||
regex = "1.11.0"
|
||||
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] }
|
||||
regex = "1.11.1"
|
||||
scopeguard = "1.2.0"
|
||||
serde = { version = "1.0.210", features = [ "derive" ] }
|
||||
serde_json = "1.0.128"
|
||||
serde = { version = "1.0.213", features = [ "derive" ] }
|
||||
serde_json = "1.0.132"
|
||||
shell-words = "1.1.0"
|
||||
tokio = { version = "1.40.0", features = [ "full" ] }
|
||||
tokio = { version = "1.41.0", features = [ "full" ] }
|
||||
tokio-stream = "0.1.16"
|
||||
tokio-util = "0.7.12"
|
||||
tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] }
|
||||
unicode-width = "0.1.14"
|
||||
unicode-width = "0.2.0"
|
||||
uzers = "0.12.1"
|
||||
|
|
|
|||
48
README.md
48
README.md
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
For breaking changes, see [Migrating to Yazi v0.4.0](https://github.com/sxyazi/yazi/issues/1772).
|
||||
|
||||
<br><br>
|
||||
|
||||
<div align="center">
|
||||
<img src="assets/logo.png" alt="Yazi logo" width="20%">
|
||||
</div>
|
||||
|
|
@ -43,25 +45,39 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265
|
|||
## Image Preview
|
||||
|
||||
| Platform | Protocol | Support |
|
||||
| --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
| [kitty](https://github.com/kovidgoyal/kitty) | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in |
|
||||
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kgp_old.rs) | ✅ Built-in |
|
||||
| [iTerm2](https://iterm2.com) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| [Mintty](https://github.com/mintty/mintty) (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
|
||||
| [Ghostty](https://mitchellh.com/ghostty) | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in |
|
||||
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.2702.0) | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
|
||||
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in |
|
||||
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in |
|
||||
| X11 / Wayland | Window system protocol | ☑️ [Überzug++](https://github.com/jstkdng/ueberzugpp) required |
|
||||
| Fallback | [ASCII art (Unicode block)](https://en.wikipedia.org/wiki/ASCII_art) | ☑️ [Chafa](https://hpjansson.org/chafa/) required |
|
||||
| --------------------------------------------------------------------------- | -------------------------------------- | --------------------------------------------- |
|
||||
| [kitty](https://github.com/kovidgoyal/kitty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||
| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in |
|
||||
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Ghostty](https://mitchellh.com/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
|
||||
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.2702.0) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio doesn't correctly clear images (#1786) |
|
||||
| [Mintty](https://github.com/mintty/mintty) (Git Bash) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
|
||||
| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol][iip] | ✅ Built-in |
|
||||
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
|
||||
| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required |
|
||||
|
||||
See https://yazi-rs.github.io/docs/image-preview for details.
|
||||
|
||||
<!-- Protocols -->
|
||||
|
||||
[kgp]: https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders
|
||||
[kgp-old]: https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kgp_old.rs
|
||||
[iip]: https://iterm2.com/documentation-images.html
|
||||
[sixel]: https://www.vt100.net/docs/vt3xx-gp/chapter14.html
|
||||
[ascii-art]: https://en.wikipedia.org/wiki/ASCII_art
|
||||
|
||||
<!-- Dependencies -->
|
||||
|
||||
[ueberzug]: https://github.com/jstkdng/ueberzugpp
|
||||
[chafa]: https://hpjansson.org/chafa/
|
||||
|
||||
## License
|
||||
|
||||
Yazi is MIT-licensed. For more information check the [LICENSE](LICENSE) file.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname"],"flagWords":[],"version":"0.2","language":"en"}
|
||||
{"flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod"],"version":"0.2","language":"en"}
|
||||
23
flake.lock
generated
23
flake.lock
generated
|
|
@ -5,11 +5,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1722415718,
|
||||
"narHash": "sha256-5US0/pgxbMksF92k1+eOa8arJTJiPvsdZj9Dl+vJkM4=",
|
||||
"lastModified": 1729265718,
|
||||
"narHash": "sha256-4HQI+6LsO3kpWTYuVGIzhJs1cetFcwT7quWCk/6rqeo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c3392ad349a5227f4a3464dce87bcc5046692fce",
|
||||
"rev": "ccc0c2126893dd20963580b6478d1a10a4512185",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -43,22 +43,19 @@
|
|||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
"nixpkgs": ["nixpkgs"]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1721441897,
|
||||
"narHash": "sha256-gYGX9/22tPNeF7dR6bWN5rsrpU4d06GnQNNgZ6ZiXz0=",
|
||||
"lastModified": 1729391507,
|
||||
"narHash": "sha256-as0I9xieJUHf7kiK2a9znDsVZQTFWhM1pLivII43Gi0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "b7996075da11a2d441cfbf4e77c2939ce51506fd",
|
||||
"rev": "784981a9feeba406de38c1c9a3decf966d853cca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "b7996075da11a2d441cfbf4e77c2939ce51506fd",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay/b7996075da11a2d441cfbf4e77c2939ce51506fd"; # FIX: pin to a specific commit until cargo-c is updated
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@ export ARTIFACT_NAME="yazi-$1"
|
|||
export YAZI_GEN_COMPLETIONS=1
|
||||
export MACOSX_DEPLOYMENT_TARGET="10.11"
|
||||
|
||||
# Setup Rust toolchain
|
||||
if [[ "$1" == *-musl ]]; then
|
||||
rustup target add "$1"
|
||||
else
|
||||
rustup toolchain install stable --profile minimal --target "$1"
|
||||
fi
|
||||
|
||||
# Build for the target
|
||||
cargo build --release --locked --target "$1"
|
||||
|
||||
|
|
|
|||
|
|
@ -16,14 +16,12 @@ yazi-shared = { path = "../yazi-shared", version = "0.3.3" }
|
|||
# External dependencies
|
||||
ansi-to-tui = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
arc-swap = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
color_quant = "1.1.0"
|
||||
crossterm = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
image = { version = "0.25.2", default-features = false, features = [ "rayon", "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "webp" ] }
|
||||
image = { version = "0.25.4", default-features = false, features = [ "rayon", "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "webp" ] }
|
||||
imagesize = "0.13.0"
|
||||
kamadak-exif = "0.5.5"
|
||||
ratatui = { workspace = true }
|
||||
scopeguard = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{env, fmt::Display, path::Path, sync::Arc};
|
||||
use std::{env, fmt::Display, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use ratatui::layout::Rect;
|
||||
|
|
@ -52,7 +52,7 @@ impl Adapter {
|
|||
}
|
||||
|
||||
pub fn image_hide(self) -> Result<()> {
|
||||
if let Some(area) = SHOWN.swap(None) { self.image_erase(*area) } else { Ok(()) }
|
||||
if let Some(area) = SHOWN.replace(None) { self.image_erase(area) } else { Ok(()) }
|
||||
}
|
||||
|
||||
pub fn image_erase(self, area: Rect) -> Result<()> {
|
||||
|
|
@ -67,10 +67,10 @@ impl Adapter {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shown_load(self) -> Option<Rect> { SHOWN.load_full().map(|r| *r) }
|
||||
pub fn shown_load(self) -> Option<Rect> { SHOWN.get() }
|
||||
|
||||
#[inline]
|
||||
pub(super) fn shown_store(area: Rect) { SHOWN.store(Some(Arc::new(area))); }
|
||||
pub(super) fn shown_store(area: Rect) { SHOWN.set(Some(area)); }
|
||||
|
||||
pub(super) fn start(self) { Ueberzug::start(self); }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use std::{fs::File, io::BufReader, path::{Path, PathBuf}};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use exif::{In, Tag};
|
||||
use image::{DynamicImage, ExtendedColorType, ImageEncoder, ImageError, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::{self, FilterType}};
|
||||
use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation};
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_config::{PREVIEW, TASKS};
|
||||
|
||||
|
|
@ -12,38 +11,27 @@ pub struct Image;
|
|||
|
||||
impl Image {
|
||||
pub async fn precache(path: &Path, cache: PathBuf) -> Result<()> {
|
||||
let orientation = Self::orientation(path).await?;
|
||||
|
||||
let path = path.to_owned();
|
||||
let mut img = tokio::task::spawn_blocking(move || {
|
||||
Self::set_limits(image::ImageReader::open(path)?.with_guessed_format()?).decode()
|
||||
})
|
||||
.await??;
|
||||
|
||||
let (mut w, mut h) = (PREVIEW.max_width, PREVIEW.max_height);
|
||||
if (5..=8).contains(&orientation) {
|
||||
(w, h) = (h, w);
|
||||
}
|
||||
let (mut img, orientation, icc) = Self::decode_from(path).await?;
|
||||
let (w, h) = Self::flip_size(orientation, (PREVIEW.max_width, PREVIEW.max_height));
|
||||
|
||||
let buf = tokio::task::spawn_blocking(move || {
|
||||
if img.width() > w || img.height() > h {
|
||||
img = img.resize(w, h, Self::filter());
|
||||
}
|
||||
if orientation != Orientation::NoTransforms {
|
||||
img.apply_orientation(orientation);
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
img = Self::rotate(img, orientation);
|
||||
|
||||
if img.color().has_alpha() {
|
||||
let rgba = img.into_rgba8();
|
||||
PngEncoder::new(&mut buf).write_image(
|
||||
&rgba,
|
||||
rgba.width(),
|
||||
rgba.height(),
|
||||
ExtendedColorType::Rgba8,
|
||||
)?;
|
||||
let mut encoder = PngEncoder::new(&mut buf);
|
||||
icc.map(|b| encoder.set_icc_profile(b));
|
||||
encoder.write_image(&rgba, rgba.width(), rgba.height(), ExtendedColorType::Rgba8)?;
|
||||
} else {
|
||||
JpegEncoder::new_with_quality(&mut buf, PREVIEW.image_quality)
|
||||
.encode_image(&img.into_rgb8())?;
|
||||
let mut encoder = JpegEncoder::new_with_quality(&mut buf, PREVIEW.image_quality);
|
||||
icc.map(|b| encoder.set_icc_profile(b));
|
||||
encoder.encode_image(&img.into_rgb8())?;
|
||||
}
|
||||
|
||||
Ok::<_, ImageError>(buf)
|
||||
|
|
@ -54,31 +42,26 @@ impl Image {
|
|||
}
|
||||
|
||||
pub(super) async fn downscale(path: &Path, rect: Rect) -> Result<DynamicImage> {
|
||||
let orientation = Self::orientation(path).await?;
|
||||
|
||||
let path = path.to_owned();
|
||||
let mut img = tokio::task::spawn_blocking(move || {
|
||||
Self::set_limits(image::ImageReader::open(path)?.with_guessed_format()?).decode()
|
||||
})
|
||||
.await??;
|
||||
|
||||
let (mut w, mut h) = Self::max_pixel(rect);
|
||||
if (5..=8).contains(&orientation) {
|
||||
(w, h) = (h, w);
|
||||
}
|
||||
let (mut img, orientation, _) = Self::decode_from(path).await?;
|
||||
let (w, h) = Self::flip_size(orientation, Self::max_pixel(rect));
|
||||
|
||||
// Fast path.
|
||||
if img.width() <= w && img.height() <= h && orientation <= 1 {
|
||||
if img.width() <= w && img.height() <= h && orientation == Orientation::NoTransforms {
|
||||
return Ok(img);
|
||||
}
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let img = tokio::task::spawn_blocking(move || {
|
||||
if img.width() > w || img.height() > h {
|
||||
img = img.resize(w, h, Self::filter())
|
||||
}
|
||||
Ok(Self::rotate(img, orientation))
|
||||
if orientation != Orientation::NoTransforms {
|
||||
img.apply_orientation(orientation);
|
||||
}
|
||||
img
|
||||
})
|
||||
.await?
|
||||
.await?;
|
||||
|
||||
Ok(img)
|
||||
}
|
||||
|
||||
pub(super) fn max_pixel(rect: Rect) -> (u32, u32) {
|
||||
|
|
@ -113,53 +96,7 @@ impl Image {
|
|||
}
|
||||
}
|
||||
|
||||
async fn orientation(path: &Path) -> Result<u8> {
|
||||
// We don't want to read the orientation of the cached image that has been
|
||||
// rotated in the `Self::precache()` step.
|
||||
if path.parent() == Some(&PREVIEW.cache_dir) {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let path = path.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let file = std::fs::File::open(path)?;
|
||||
|
||||
let mut reader = std::io::BufReader::new(&file);
|
||||
let Ok(exif) = exif::Reader::new().read_from_container(&mut reader) else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
Ok(match exif.get_field(Tag::Orientation, In::PRIMARY) {
|
||||
Some(orientation) => match orientation.value.get_uint(0) {
|
||||
Some(v @ 1..=8) => v as u8,
|
||||
_ => 1,
|
||||
},
|
||||
None => 1,
|
||||
})
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
// https://magnushoff.com/articles/jpeg-orientation/
|
||||
fn rotate(mut img: DynamicImage, orientation: u8) -> DynamicImage {
|
||||
let alpha = img.color().has_alpha();
|
||||
img = match orientation {
|
||||
2 => DynamicImage::ImageRgba8(imageops::flip_horizontal(&img)),
|
||||
3 => DynamicImage::ImageRgba8(imageops::rotate180(&img)),
|
||||
4 => DynamicImage::ImageRgba8(imageops::flip_vertical(&img)),
|
||||
5 => DynamicImage::ImageRgba8(imageops::flip_horizontal(&imageops::rotate90(&img))),
|
||||
6 => DynamicImage::ImageRgba8(imageops::rotate90(&img)),
|
||||
7 => DynamicImage::ImageRgba8(imageops::flip_horizontal(&imageops::rotate270(&img))),
|
||||
8 => DynamicImage::ImageRgba8(imageops::rotate270(&img)),
|
||||
_ => img,
|
||||
};
|
||||
if !alpha {
|
||||
img = DynamicImage::ImageRgb8(img.into_rgb8());
|
||||
}
|
||||
img
|
||||
}
|
||||
|
||||
fn set_limits(mut r: image::ImageReader<BufReader<File>>) -> image::ImageReader<BufReader<File>> {
|
||||
async fn decode_from(path: &Path) -> ImageResult<(DynamicImage, Orientation, Option<Vec<u8>>)> {
|
||||
let mut limits = Limits::no_limits();
|
||||
if TASKS.image_alloc > 0 {
|
||||
limits.max_alloc = Some(TASKS.image_alloc as u64);
|
||||
|
|
@ -170,7 +107,27 @@ impl Image {
|
|||
if TASKS.image_bound[1] > 0 {
|
||||
limits.max_image_height = Some(TASKS.image_bound[1] as u32);
|
||||
}
|
||||
r.limits(limits);
|
||||
r
|
||||
|
||||
let path = path.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut reader = ImageReader::open(path)?;
|
||||
reader.limits(limits);
|
||||
|
||||
let mut decoder = reader.with_guessed_format()?.into_decoder()?;
|
||||
let orientation = decoder.orientation().unwrap_or(Orientation::NoTransforms);
|
||||
let icc = decoder.icc_profile().unwrap_or_default();
|
||||
|
||||
Ok((DynamicImage::from_decoder(decoder)?, orientation, icc))
|
||||
})
|
||||
.await
|
||||
.map_err(|e| ImageError::IoError(e.into()))?
|
||||
}
|
||||
|
||||
fn flip_size(orientation: Orientation, (w, h): (u32, u32)) -> (u32, u32) {
|
||||
use image::metadata::Orientation::{Rotate90, Rotate90FlipH, Rotate270, Rotate270FlipH};
|
||||
match orientation {
|
||||
Rotate90 | Rotate270 | Rotate90FlipH | Rotate270FlipH => (h, w),
|
||||
_ => (w, h),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ yazi_macro::mod_flat!(
|
|||
adapter chafa dimension emulator iip image kgp kgp_old mux sixel ueberzug
|
||||
);
|
||||
|
||||
use yazi_shared::{RoCell, env_exists, in_wsl};
|
||||
use yazi_shared::{RoCell, SyncCell, env_exists, in_wsl};
|
||||
pub static ADAPTOR: RoCell<Adapter> = RoCell::new();
|
||||
|
||||
// Tmux support
|
||||
|
|
@ -17,7 +17,7 @@ static CLOSE: RoCell<&'static str> = RoCell::new();
|
|||
pub static WSL: RoCell<bool> = RoCell::new();
|
||||
|
||||
// Image state
|
||||
static SHOWN: RoCell<arc_swap::ArcSwapOption<ratatui::layout::Rect>> = RoCell::new();
|
||||
static SHOWN: SyncCell<Option<ratatui::layout::Rect>> = SyncCell::new(None);
|
||||
|
||||
pub fn init() {
|
||||
// Tmux support
|
||||
|
|
@ -38,9 +38,6 @@ pub fn init() {
|
|||
// WSL support
|
||||
WSL.init(in_wsl());
|
||||
|
||||
// Image state
|
||||
SHOWN.with(<_>::default);
|
||||
|
||||
ADAPTOR.init(Adapter::matches());
|
||||
ADAPTOR.start();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ serde = { workspace = true }
|
|||
|
||||
[build-dependencies]
|
||||
clap = { workspace = true }
|
||||
clap_complete = "4.5.33"
|
||||
clap_complete = "4.5.35"
|
||||
clap_complete_fig = "4.5.2"
|
||||
clap_complete_nushell = "4.5.4"
|
||||
vergen-gitcl = { version = "1.0.1", features = [ "build" ] }
|
||||
|
|
|
|||
|
|
@ -22,11 +22,10 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
let out = "completions";
|
||||
|
||||
std::fs::create_dir_all(out)?;
|
||||
generate_to(Shell::Bash, cmd, bin, out)?;
|
||||
generate_to(Shell::Fish, cmd, bin, out)?;
|
||||
generate_to(Shell::Zsh, cmd, bin, out)?;
|
||||
generate_to(Shell::Elvish, cmd, bin, out)?;
|
||||
generate_to(Shell::PowerShell, cmd, bin, out)?;
|
||||
for sh in [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] {
|
||||
generate_to(sh, cmd, bin, out)?;
|
||||
}
|
||||
|
||||
generate_to(clap_complete_nushell::Nushell, cmd, bin, out)?;
|
||||
generate_to(clap_complete_fig::Fig, cmd, bin, out)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ toml_edit = "0.22.22"
|
|||
# External build dependencies
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
clap_complete = "4.5.33"
|
||||
clap_complete = "4.5.35"
|
||||
clap_complete_fig = "4.5.2"
|
||||
clap_complete_nushell = "4.5.4"
|
||||
serde_json = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -22,11 +22,10 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
let out = "completions";
|
||||
|
||||
std::fs::create_dir_all(out)?;
|
||||
generate_to(Shell::Bash, cmd, bin, out)?;
|
||||
generate_to(Shell::Fish, cmd, bin, out)?;
|
||||
generate_to(Shell::Zsh, cmd, bin, out)?;
|
||||
generate_to(Shell::Elvish, cmd, bin, out)?;
|
||||
generate_to(Shell::PowerShell, cmd, bin, out)?;
|
||||
for sh in [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] {
|
||||
generate_to(sh, cmd, bin, out)?;
|
||||
}
|
||||
|
||||
generate_to(clap_complete_nushell::Nushell, cmd, bin, out)?;
|
||||
generate_to(clap_complete_fig::Fig, cmd, bin, out)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ async fn main() -> anyhow::Result<()> {
|
|||
|
||||
Command::Pack(cmd) => {
|
||||
package::init()?;
|
||||
package::Package::migrate().await?;
|
||||
if cmd.install {
|
||||
package::Package::install_from_config("plugin", false).await?;
|
||||
package::Package::install_from_config("flavor", false).await?;
|
||||
|
|
@ -57,15 +56,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
} else if cmd.upgrade {
|
||||
package::Package::install_from_config("plugin", true).await?;
|
||||
package::Package::install_from_config("flavor", true).await?;
|
||||
} else if let Some(mut repo) = cmd.add {
|
||||
// TODO: remove this in the future
|
||||
if repo.contains("#") {
|
||||
repo = repo.replace("#", ":");
|
||||
println!(
|
||||
"WARNING: `#` has been deprecated in Yazi 0.3.1, please use `:` instead. See https://github.com/sxyazi/yazi/issues/1471 for more information."
|
||||
);
|
||||
}
|
||||
|
||||
} else if let Some(repo) = cmd.add {
|
||||
package::Package::add_to_config(&repo).await?;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,33 +6,6 @@ use yazi_shared::Xdg;
|
|||
use super::Package;
|
||||
|
||||
impl Package {
|
||||
// TODO: remove this in the future
|
||||
pub(crate) async fn migrate() -> Result<()> {
|
||||
let path = Xdg::config_dir().join("package.toml");
|
||||
let mut doc = Self::ensure_config(&fs::read_to_string(&path).await.unwrap_or_default())?;
|
||||
|
||||
fn impl_(deps: &mut Array) -> Result<()> {
|
||||
for dep in deps.iter_mut() {
|
||||
let dep = dep.as_inline_table_mut().context("Dependency must be an inline table")?;
|
||||
let use_ = dep.get("use").and_then(|d| d.as_str()).context("Missing `use` field")?;
|
||||
if use_.contains("#") {
|
||||
dep["use"] = use_.replace("#", ":").into();
|
||||
}
|
||||
if let Some(commit) = dep.get("commit").map(ToOwned::to_owned) {
|
||||
dep.remove("commit");
|
||||
dep.insert("rev", commit);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl_(doc["plugin"]["deps"].as_array_mut().unwrap())?;
|
||||
impl_(doc["flavor"]["deps"].as_array_mut().unwrap())?;
|
||||
|
||||
fs::write(path, doc.to_string()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn add_to_config(use_: &str) -> Result<()> {
|
||||
let mut package = Self::new(use_, None);
|
||||
let Some(name) = package.name() else { bail!("Invalid package `use`") };
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
# External dependencies
|
||||
syn = "2.0.79"
|
||||
syn = "2.0.85"
|
||||
quote = "1.0.37"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ yazi-shared = { path = "../yazi-shared", version = "0.3.3" }
|
|||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
arc-swap = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -98,6 +98,20 @@ selected = { reversed = true }
|
|||
# : }}}
|
||||
|
||||
|
||||
# : Confirm {{{
|
||||
|
||||
[confirm]
|
||||
border = { fg = "blue" }
|
||||
title = { fg = "blue" }
|
||||
content = {}
|
||||
list = {}
|
||||
btn_yes = { reversed = true }
|
||||
btn_no = {}
|
||||
btn_labels = [ " [Y]es ", " (N)o " ]
|
||||
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Completion {{{
|
||||
|
||||
[completion]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_shared::Layer;
|
||||
|
|
@ -20,7 +21,7 @@ pub struct Keymap {
|
|||
|
||||
impl Keymap {
|
||||
#[inline]
|
||||
pub fn get(&self, layer: Layer) -> &Vec<Chord> {
|
||||
pub fn get(&self, layer: Layer) -> &[Chord] {
|
||||
match layer {
|
||||
Layer::App => unreachable!(),
|
||||
Layer::Manager => &self.manager,
|
||||
|
|
@ -36,9 +37,11 @@ impl Keymap {
|
|||
}
|
||||
|
||||
impl FromStr for Keymap {
|
||||
type Err = toml::de::Error;
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s).context("Failed to parse your keymap.toml")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Keymap {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
use ratatui::layout::Rect;
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct Layout {
|
||||
pub current: Rect,
|
||||
pub preview: Rect,
|
||||
pub progress: Rect,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub const fn default() -> Self {
|
||||
Self { current: Rect::ZERO, preview: Rect::ZERO, progress: Rect::ZERO }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ yazi_macro::mod_flat!(layout pattern preset priority);
|
|||
|
||||
use std::str::FromStr;
|
||||
|
||||
use yazi_shared::{RoCell, Xdg};
|
||||
|
||||
pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();
|
||||
use yazi_shared::{RoCell, SyncCell, Xdg};
|
||||
|
||||
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
|
||||
pub static LOG: RoCell<log::Log> = RoCell::new();
|
||||
|
|
@ -23,26 +21,28 @@ pub static CONFIRM: RoCell<popup::Confirm> = RoCell::new();
|
|||
pub static PICK: RoCell<popup::Pick> = RoCell::new();
|
||||
pub static WHICH: RoCell<which::Which> = RoCell::new();
|
||||
|
||||
pub static LAYOUT: SyncCell<Layout> = SyncCell::new(Layout::default());
|
||||
|
||||
pub fn init() -> anyhow::Result<()> {
|
||||
let config_dir = Xdg::config_dir();
|
||||
let yazi_toml = &Preset::yazi(&config_dir)?;
|
||||
let keymap_toml = &Preset::keymap(&config_dir)?;
|
||||
let theme_toml = &Preset::theme(&config_dir)?;
|
||||
if let Err(e) = try_init(true) {
|
||||
eprintln!("{e}");
|
||||
if let Some(src) = e.source() {
|
||||
eprintln!("\nCaused by:\n{src}");
|
||||
}
|
||||
|
||||
LAYOUT.with(<_>::default);
|
||||
use crossterm::style::{Attribute, Print, SetAttributes};
|
||||
crossterm::execute!(
|
||||
std::io::stderr(),
|
||||
SetAttributes(Attribute::Reverse.into()),
|
||||
SetAttributes(Attribute::Bold.into()),
|
||||
Print("Press <Enter> to continue with preset settings..."),
|
||||
SetAttributes(Attribute::Reset.into()),
|
||||
Print("\n"),
|
||||
)?;
|
||||
|
||||
KEYMAP.init(<_>::from_str(keymap_toml)?);
|
||||
LOG.init(<_>::from_str(yazi_toml)?);
|
||||
MANAGER.init(<_>::from_str(yazi_toml)?);
|
||||
OPEN.init(<_>::from_str(yazi_toml)?);
|
||||
PLUGIN.init(<_>::from_str(yazi_toml)?);
|
||||
PREVIEW.init(<_>::from_str(yazi_toml)?);
|
||||
TASKS.init(<_>::from_str(yazi_toml)?);
|
||||
THEME.init(<_>::from_str(theme_toml)?);
|
||||
INPUT.init(<_>::from_str(yazi_toml)?);
|
||||
CONFIRM.init(<_>::from_str(yazi_toml)?);
|
||||
PICK.init(<_>::from_str(yazi_toml)?);
|
||||
WHICH.init(<_>::from_str(yazi_toml)?);
|
||||
std::io::stdin().read_line(&mut String::new())?;
|
||||
try_init(false)?;
|
||||
}
|
||||
|
||||
// TODO: Remove in v0.3.2
|
||||
for c in &KEYMAP.manager {
|
||||
|
|
@ -78,3 +78,41 @@ Please change `create_title = "Create:"` to `create_title = ["Create:", "Create
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_init(merge: bool) -> anyhow::Result<()> {
|
||||
let (yazi_toml, keymap_toml, theme_toml) = if merge {
|
||||
let p = Xdg::config_dir();
|
||||
(Preset::yazi(&p)?, Preset::keymap(&p)?, Preset::theme(&p)?)
|
||||
} else {
|
||||
use yazi_macro::config_preset as preset;
|
||||
(preset!("yazi"), preset!("keymap"), preset!("theme"))
|
||||
};
|
||||
|
||||
let keymap = <_>::from_str(&keymap_toml)?;
|
||||
let log = <_>::from_str(&yazi_toml)?;
|
||||
let manager = <_>::from_str(&yazi_toml)?;
|
||||
let open = <_>::from_str(&yazi_toml)?;
|
||||
let plugin = <_>::from_str(&yazi_toml)?;
|
||||
let preview = <_>::from_str(&yazi_toml)?;
|
||||
let tasks = <_>::from_str(&yazi_toml)?;
|
||||
let theme = <_>::from_str(&theme_toml)?;
|
||||
let input = <_>::from_str(&yazi_toml)?;
|
||||
let confirm = <_>::from_str(&yazi_toml)?;
|
||||
let pick = <_>::from_str(&yazi_toml)?;
|
||||
let which = <_>::from_str(&yazi_toml)?;
|
||||
|
||||
KEYMAP.init(keymap);
|
||||
LOG.init(log);
|
||||
MANAGER.init(manager);
|
||||
OPEN.init(open);
|
||||
PLUGIN.init(plugin);
|
||||
PREVIEW.init(preview);
|
||||
TASKS.init(tasks);
|
||||
THEME.init(theme);
|
||||
INPUT.init(input);
|
||||
CONFIRM.init(confirm);
|
||||
PICK.init(pick);
|
||||
WHICH.init(which);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -8,9 +9,11 @@ pub struct Log {
|
|||
}
|
||||
|
||||
impl FromStr for Log {
|
||||
type Err = toml::de::Error;
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s).context("Failed to parse the [log] section in your yazi.toml")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Log {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
|
|
@ -35,9 +36,10 @@ impl FromStr for Manager {
|
|||
manager: Manager,
|
||||
}
|
||||
|
||||
let manager = toml::from_str::<Outer>(s)?.manager;
|
||||
manager.validate()?;
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [manager] section in your yazi.toml")?;
|
||||
outer.manager.validate()?;
|
||||
|
||||
Ok(manager)
|
||||
Ok(outer.manager)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::{collections::HashMap, path::Path, str::FromStr};
|
||||
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexSet;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use yazi_shared::MIME_DIR;
|
||||
|
|
@ -55,9 +56,11 @@ impl Open {
|
|||
}
|
||||
|
||||
impl FromStr for Open {
|
||||
type Err = toml::de::Error;
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s).context("Failed to parse the [open] or [opener] section in your yazi.toml")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Open {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::Path;
|
||||
use std::{path::Path, str::FromStr};
|
||||
|
||||
use globset::GlobBuilder;
|
||||
use serde::Deserialize;
|
||||
|
|
@ -9,6 +9,8 @@ pub struct Pattern {
|
|||
inner: globset::GlobMatcher,
|
||||
is_dir: bool,
|
||||
is_star: bool,
|
||||
#[cfg(windows)]
|
||||
sep_lit: bool,
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
|
|
@ -19,7 +21,20 @@ impl Pattern {
|
|||
|
||||
#[inline]
|
||||
pub fn match_path(&self, path: impl AsRef<Path>, is_dir: bool) -> bool {
|
||||
is_dir == self.is_dir && (self.is_star || self.inner.is_match(path))
|
||||
if is_dir != self.is_dir {
|
||||
return false;
|
||||
} else if self.is_star {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
let path = if self.sep_lit {
|
||||
yazi_shared::fs::backslash_to_slash(path.as_ref())
|
||||
} else {
|
||||
std::borrow::Cow::Borrowed(path.as_ref())
|
||||
};
|
||||
|
||||
self.inner.is_match(path)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -29,27 +44,115 @@ impl Pattern {
|
|||
pub fn any_dir(&self) -> bool { self.is_star && self.is_dir }
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Pattern {
|
||||
type Error = anyhow::Error;
|
||||
impl FromStr for Pattern {
|
||||
type Err = globset::Error;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let a = s.trim_start_matches("\\s");
|
||||
let b = a.trim_end_matches('/');
|
||||
let sep_lit = b.contains('/');
|
||||
|
||||
let inner = GlobBuilder::new(b)
|
||||
.case_insensitive(a.len() == s.len())
|
||||
.literal_separator(false)
|
||||
.literal_separator(sep_lit)
|
||||
.backslash_escape(false)
|
||||
.empty_alternates(true)
|
||||
.build()?
|
||||
.compile_matcher();
|
||||
|
||||
Ok(Self { inner, is_dir: b.len() < a.len(), is_star: b == "*" })
|
||||
Ok(Self {
|
||||
inner,
|
||||
is_dir: b.len() < a.len(),
|
||||
is_star: b == "*",
|
||||
#[cfg(windows)]
|
||||
sep_lit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Pattern {
|
||||
type Error = anyhow::Error;
|
||||
type Error = globset::Error;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> { Self::try_from(s.as_str()) }
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(s.as_str()) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn matches(glob: &str, path: &str) -> bool {
|
||||
Pattern::from_str(glob).unwrap().match_path(path, false)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_unix() {
|
||||
// Wildcard
|
||||
assert!(matches("*", "/foo"));
|
||||
assert!(matches("*", "/foo/bar"));
|
||||
assert!(matches("**", "foo"));
|
||||
assert!(matches("**", "/foo"));
|
||||
assert!(matches("**", "/foo/bar"));
|
||||
|
||||
// Filename
|
||||
assert!(matches("*.md", "foo.md"));
|
||||
assert!(matches("*.md", "/foo.md"));
|
||||
assert!(matches("*.md", "/foo/bar.md"));
|
||||
|
||||
// 1-star
|
||||
assert!(matches("/*", "/foo"));
|
||||
assert!(matches("/*/*.md", "/foo/bar.md"));
|
||||
|
||||
// 2-star
|
||||
assert!(matches("/**", "/foo"));
|
||||
assert!(matches("/**", "/foo/bar"));
|
||||
assert!(matches("**/**", "/foo"));
|
||||
assert!(matches("**/**", "/foo/bar"));
|
||||
assert!(matches("/**/*", "/foo"));
|
||||
assert!(matches("/**/*", "/foo/bar"));
|
||||
|
||||
// Failures
|
||||
assert!(!matches("/*/*", "/foo"));
|
||||
assert!(!matches("/*/*.md", "/foo.md"));
|
||||
assert!(!matches("/*", "/foo/bar"));
|
||||
assert!(!matches("/*.md", "/foo/bar.md"));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn test_windows() {
|
||||
// Wildcard
|
||||
assert!(matches("*", r#"C:\foo"#));
|
||||
assert!(matches("*", r#"C:\foo\bar"#));
|
||||
assert!(matches("**", r#"foo"#));
|
||||
assert!(matches("**", r#"C:\foo"#));
|
||||
assert!(matches("**", r#"C:\foo\bar"#));
|
||||
|
||||
// Filename
|
||||
assert!(matches("*.md", r#"foo.md"#));
|
||||
assert!(matches("*.md", r#"C:\foo.md"#));
|
||||
assert!(matches("*.md", r#"C:\foo\bar.md"#));
|
||||
|
||||
// 1-star
|
||||
assert!(matches(r#"C:/*"#, r#"C:\foo"#));
|
||||
assert!(matches(r#"C:/*/*.md"#, r#"C:\foo\bar.md"#));
|
||||
|
||||
// 2-star
|
||||
assert!(matches(r#"C:/**"#, r#"C:\foo"#));
|
||||
assert!(matches(r#"C:/**"#, r#"C:\foo\bar"#));
|
||||
assert!(matches(r#"**/**"#, r#"C:\foo"#));
|
||||
assert!(matches(r#"**/**"#, r#"C:\foo\bar"#));
|
||||
assert!(matches(r#"C:/**/*"#, r#"C:\foo"#));
|
||||
assert!(matches(r#"C:/**/*"#, r#"C:\foo\bar"#));
|
||||
|
||||
// Drive letter
|
||||
assert!(matches(r#"*:/*"#, r#"C:\foo"#));
|
||||
assert!(matches(r#"*:/**/*.md"#, r#"C:\foo\bar.md"#));
|
||||
|
||||
// Failures
|
||||
assert!(!matches(r#"C:/*/*"#, r#"C:\foo"#));
|
||||
assert!(!matches(r#"C:/*/*.md"#, r#"C:\foo.md"#));
|
||||
assert!(!matches(r#"C:/*"#, r#"C:\foo\bar"#));
|
||||
assert!(!matches(r#"C:/*.md"#, r#"C:\foo\bar.md"#));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::{collections::HashSet, path::Path, str::FromStr};
|
||||
|
||||
use serde::Deserialize;
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use super::{Fetcher, Preloader, Previewer};
|
||||
use crate::{Preset, plugin::MAX_PREWORKERS};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Plugin {
|
||||
pub fetchers: Vec<Fetcher>,
|
||||
pub preloaders: Vec<Preloader>,
|
||||
|
|
@ -55,9 +55,18 @@ impl Plugin {
|
|||
}
|
||||
|
||||
impl FromStr for Plugin {
|
||||
type Err = toml::de::Error;
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s).context("Failed to parse the [plugin] section in your yazi.toml")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Plugin {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
plugin: Shadow,
|
||||
|
|
@ -84,7 +93,7 @@ impl FromStr for Plugin {
|
|||
append_previewers: Vec<Previewer>,
|
||||
}
|
||||
|
||||
let mut shadow = toml::from_str::<Outer>(s)?.plugin;
|
||||
let mut shadow = Outer::deserialize(deserializer)?.plugin;
|
||||
if shadow.append_previewers.iter().any(|r| r.any_file()) {
|
||||
shadow.previewers.retain(|r| !r.any_file());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
|
|
@ -30,7 +31,7 @@ pub struct Confirm {
|
|||
}
|
||||
|
||||
impl FromStr for Confirm {
|
||||
type Err = toml::de::Error;
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -38,7 +39,10 @@ impl FromStr for Confirm {
|
|||
confirm: Confirm,
|
||||
}
|
||||
|
||||
Ok(toml::from_str::<Outer>(s)?.confirm)
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [confirm] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.confirm)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
|
|
@ -49,7 +50,7 @@ impl Input {
|
|||
}
|
||||
|
||||
impl FromStr for Input {
|
||||
type Err = toml::de::Error;
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -57,7 +58,10 @@ impl FromStr for Input {
|
|||
input: Input,
|
||||
}
|
||||
|
||||
Ok(toml::from_str::<Outer>(s)?.input)
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [input] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.input)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{Offset, Origin};
|
||||
|
|
@ -17,7 +18,7 @@ impl Pick {
|
|||
}
|
||||
|
||||
impl FromStr for Pick {
|
||||
type Err = toml::de::Error;
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
|
|
@ -25,6 +26,9 @@ impl FromStr for Pick {
|
|||
pick: Pick,
|
||||
}
|
||||
|
||||
Ok(toml::from_str::<Outer>(s)?.pick)
|
||||
let outer =
|
||||
toml::from_str::<Outer>(s).context("Failed to parse the [pick] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.pick)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ use crate::theme::Flavor;
|
|||
pub(crate) struct Preset;
|
||||
|
||||
impl Preset {
|
||||
pub(crate) fn yazi(p: &Path) -> Result<Cow<str>> {
|
||||
pub(crate) fn yazi(p: &Path) -> Result<Cow<'static, str>> {
|
||||
Self::merge_path(p.join("yazi.toml"), preset!("yazi"))
|
||||
}
|
||||
|
||||
pub(crate) fn keymap(p: &Path) -> Result<Cow<str>> {
|
||||
pub(crate) fn keymap(p: &Path) -> Result<Cow<'static, str>> {
|
||||
Self::merge_path(p.join("keymap.toml"), preset!("keymap"))
|
||||
}
|
||||
|
||||
pub(crate) fn theme(p: &Path) -> Result<Cow<str>> {
|
||||
pub(crate) fn theme(p: &Path) -> Result<Cow<'static, str>> {
|
||||
let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else {
|
||||
return Ok(preset!("theme"));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::{borrow::Cow, path::PathBuf, str::FromStr, time::{SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_shared::fs::expand_path;
|
||||
|
||||
|
|
@ -49,6 +49,20 @@ impl FromStr for Preview {
|
|||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let preview: Self =
|
||||
toml::from_str(s).context("Failed to parse the [preview] section in your yazi.toml")?;
|
||||
|
||||
std::fs::create_dir_all(&preview.cache_dir).context("Failed to create cache directory")?;
|
||||
|
||||
Ok(preview)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Preview {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
preview: Shadow,
|
||||
|
|
@ -74,12 +88,8 @@ impl FromStr for Preview {
|
|||
ueberzug_offset: (f32, f32, f32, f32),
|
||||
}
|
||||
|
||||
let preview = toml::from_str::<Outer>(s)?.preview;
|
||||
preview.validate()?;
|
||||
|
||||
let cache_dir =
|
||||
preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, expand_path);
|
||||
std::fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?;
|
||||
let preview = Outer::deserialize(deserializer)?.preview;
|
||||
preview.validate().map_err(serde::de::Error::custom)?;
|
||||
|
||||
Ok(Preview {
|
||||
wrap: preview.wrap,
|
||||
|
|
@ -87,7 +97,10 @@ impl FromStr for Preview {
|
|||
max_width: preview.max_width,
|
||||
max_height: preview.max_height,
|
||||
|
||||
cache_dir,
|
||||
cache_dir: preview
|
||||
.cache_dir
|
||||
.filter(|p| !p.is_empty())
|
||||
.map_or_else(Xdg::cache_dir, expand_path),
|
||||
|
||||
image_delay: preview.image_delay,
|
||||
image_filter: preview.image_filter,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
use validator::Validate;
|
||||
|
||||
|
|
@ -27,9 +28,10 @@ impl FromStr for Tasks {
|
|||
tasks: Tasks,
|
||||
}
|
||||
|
||||
let tasks = toml::from_str::<Outer>(s)?.tasks;
|
||||
tasks.validate()?;
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [tasks] section in your yazi.toml")?;
|
||||
outer.tasks.validate()?;
|
||||
|
||||
Ok(tasks)
|
||||
Ok(outer.tasks)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_shared::{Xdg, fs::expand_path, theme::Style};
|
||||
|
|
@ -12,6 +13,7 @@ pub struct Theme {
|
|||
pub manager: Manager,
|
||||
status: Status,
|
||||
pub input: Input,
|
||||
pub confirm: Confirm,
|
||||
pub pick: Pick,
|
||||
pub completion: Completion,
|
||||
pub tasks: Tasks,
|
||||
|
|
@ -30,7 +32,7 @@ impl FromStr for Theme {
|
|||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut theme: Self = toml::from_str(s)?;
|
||||
let mut theme: Self = toml::from_str(s).context("Failed to parse your yazi.toml")?;
|
||||
theme.manager.validate()?;
|
||||
theme.which.validate()?;
|
||||
|
||||
|
|
@ -114,6 +116,17 @@ pub struct Input {
|
|||
pub selected: Style,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Confirm {
|
||||
pub border: Style,
|
||||
pub title: Style,
|
||||
pub content: Style,
|
||||
pub list: Style,
|
||||
pub btn_yes: Style,
|
||||
pub btn_no: Style,
|
||||
pub btn_labels: [String; 2],
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Pick {
|
||||
pub border: Style,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
|
||||
|
|
@ -23,6 +24,9 @@ impl FromStr for Which {
|
|||
which: Which,
|
||||
}
|
||||
|
||||
Ok(toml::from_str::<Outer>(s)?.which)
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [which] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.which)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ bitflags = { workspace = true }
|
|||
crossterm = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
notify = { package = "notify-fork", version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] }
|
||||
notify = { version = "7.0.0", default-features = false, features = [ "macos_fsevent" ] }
|
||||
parking_lot = { workspace = true }
|
||||
ratatui = { workspace = true }
|
||||
scopeguard = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ impl Manager {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let todo: Vec<_> = old.into_iter().zip(new).filter(|(o, n)| o != n).collect();
|
||||
let (old, new) = old.into_iter().zip(new).filter(|(o, n)| o != n).unzip();
|
||||
let todo = Self::prioritized_paths(old, new);
|
||||
if todo.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -117,4 +118,90 @@ impl Manager {
|
|||
stdin().read_exact(&mut [0]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prioritized_paths(old: Vec<PathBuf>, new: Vec<PathBuf>) -> Vec<(PathBuf, PathBuf)> {
|
||||
let orders: HashMap<_, _> = old.iter().enumerate().map(|(i, p)| (p, i)).collect();
|
||||
let mut incomes: HashMap<_, _> = old.iter().map(|p| (p, false)).collect();
|
||||
let mut todos: HashMap<_, _> = old
|
||||
.iter()
|
||||
.zip(new)
|
||||
.map(|(o, n)| {
|
||||
incomes.get_mut(&n).map(|b| *b = true);
|
||||
(o, n)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut sorted = Vec::with_capacity(old.len());
|
||||
while !todos.is_empty() {
|
||||
// Paths that are non-incomes and don't need to be prioritized in this round
|
||||
let mut outcomes: Vec<_> = incomes.iter().filter(|(_, &b)| !b).map(|(&p, _)| p).collect();
|
||||
outcomes.sort_unstable_by(|a, b| orders[b].cmp(&orders[a]));
|
||||
|
||||
// If there're no outcomes, it means there are cycles in the renaming
|
||||
if outcomes.is_empty() {
|
||||
let mut remain: Vec<_> = todos.into_iter().map(|(o, n)| (o.clone(), n)).collect();
|
||||
remain.sort_unstable_by(|(a, _), (b, _)| orders[a].cmp(&orders[b]));
|
||||
sorted.reverse();
|
||||
sorted.extend(remain);
|
||||
return sorted;
|
||||
}
|
||||
|
||||
for old in outcomes {
|
||||
let Some(new) = todos.remove(old) else { unreachable!() };
|
||||
incomes.remove(&old);
|
||||
incomes.get_mut(&new).map(|b| *b = false);
|
||||
sorted.push((old.clone(), new));
|
||||
}
|
||||
}
|
||||
sorted.reverse();
|
||||
sorted
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sort() {
|
||||
fn cmp(input: &[(&str, &str)], expected: &[(&str, &str)]) {
|
||||
let sorted = Manager::prioritized_paths(
|
||||
input.iter().map(|&(o, _)| o.into()).collect(),
|
||||
input.iter().map(|&(_, n)| n.into()).collect(),
|
||||
);
|
||||
let sorted: Vec<_> =
|
||||
sorted.iter().map(|(o, n)| (o.to_str().unwrap(), n.to_str().unwrap())).collect();
|
||||
assert_eq!(sorted, expected);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("2", "3"), ("1", "2"), ("3", "4")],
|
||||
&[("3", "4"), ("2", "3"), ("1", "2")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("1", "3"), ("2", "3"), ("3", "4")],
|
||||
&[("3", "4"), ("1", "3"), ("2", "3")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("2", "1"), ("1", "2")],
|
||||
&[("2", "1"), ("1", "2")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("3", "2"), ("2", "1"), ("1", "3"), ("a", "b"), ("b", "c")],
|
||||
&[("b", "c"), ("a", "b"), ("3", "2"), ("2", "1"), ("1", "3")]
|
||||
);
|
||||
|
||||
#[rustfmt::skip]
|
||||
cmp(
|
||||
&[("b", "b_"), ("a", "a_"), ("c", "c_")],
|
||||
&[("b", "b_"), ("a", "a_"), ("c", "c_")],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,21 +2,18 @@ use std::{collections::HashSet, path::PathBuf};
|
|||
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_macro::render;
|
||||
use yazi_shared::{event::{Cmd, Data}, fs::{Url, Urn}};
|
||||
use yazi_shared::{Id, event::{Cmd, Data}, fs::{Url, Urn}};
|
||||
|
||||
use crate::manager::Manager;
|
||||
|
||||
struct Opt {
|
||||
url: Option<Url>,
|
||||
tab: Option<usize>,
|
||||
tab: Option<Id>,
|
||||
}
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(mut c: Cmd) -> Self {
|
||||
Self {
|
||||
url: c.take_first().and_then(Data::into_url),
|
||||
tab: c.get("tab").and_then(Data::as_usize),
|
||||
}
|
||||
Self { url: c.take_first().and_then(Data::into_url), tab: c.get("tab").and_then(Data::as_id) }
|
||||
}
|
||||
}
|
||||
impl From<Option<Url>> for Opt {
|
||||
|
|
@ -49,10 +46,10 @@ impl Manager {
|
|||
self.watcher.watch(to_watch);
|
||||
|
||||
// Publish through DDS
|
||||
Pubsub::pub_from_hover(self.active().idx, self.hovered().map(|h| &h.url));
|
||||
Pubsub::pub_from_hover(self.active().id, self.hovered().map(|h| &h.url));
|
||||
}
|
||||
|
||||
fn hover_do(&mut self, url: Url, tab: Option<usize>) {
|
||||
fn hover_do(&mut self, url: Url, tab: Option<Id>) {
|
||||
// Hover on the file
|
||||
if let Ok(p) = url.strip_prefix(&self.current_or(tab).url).map(PathBuf::from) {
|
||||
render!(self.current_or_mut(tab).repos(Some(Urn::new(&p))));
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use yazi_proxy::HIDER;
|
||||
use yazi_shared::{event::{Cmd, Data}, fs::Url};
|
||||
|
||||
use crate::manager::Manager;
|
||||
|
|
@ -30,6 +31,9 @@ impl Manager {
|
|||
let Some(hovered) = self.hovered().cloned() else {
|
||||
return self.active_mut().preview.reset();
|
||||
};
|
||||
if HIDER.try_acquire().is_err() {
|
||||
return self.active_mut().preview.reset_image();
|
||||
}
|
||||
|
||||
let mime = self.mimetype.get_owned(&hovered.url).unwrap_or_default();
|
||||
let folder = self.active().hovered_folder().map(|f| (f.offset, f.cha));
|
||||
|
|
|
|||
|
|
@ -24,11 +24,12 @@ impl Tabs {
|
|||
}
|
||||
|
||||
self.items.remove(opt.idx).shutdown();
|
||||
if opt.idx <= self.cursor {
|
||||
if opt.idx > self.cursor {
|
||||
self.set_idx(self.cursor);
|
||||
} else {
|
||||
self.set_idx(self.absolute(1));
|
||||
}
|
||||
|
||||
self.reorder();
|
||||
render!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ impl Tabs {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut tab = Tab { idx: self.cursor + 1, ..Default::default() };
|
||||
|
||||
let mut tab = Tab::default();
|
||||
if !opt.current {
|
||||
tab.cd(opt.url);
|
||||
} else if let Some(h) = self.active().hovered() {
|
||||
|
|
@ -52,7 +51,6 @@ impl Tabs {
|
|||
|
||||
self.items.insert(self.cursor + 1, tab);
|
||||
self.set_idx(self.cursor + 1);
|
||||
self.reorder();
|
||||
render!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ impl Tabs {
|
|||
|
||||
self.items.swap(self.cursor, idx);
|
||||
self.set_idx(idx);
|
||||
self.reorder();
|
||||
render!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ impl Manager {
|
|||
return;
|
||||
}
|
||||
|
||||
ManagerProxy::hover(None, tab.idx); // Re-hover in next loop
|
||||
ManagerProxy::hover(None, tab.id); // Re-hover in next loop
|
||||
ManagerProxy::update_paged(); // Update for paged files in next loop
|
||||
if calc {
|
||||
tasks.prework_sorted(&tab.current.files);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use ratatui::layout::Rect;
|
|||
use yazi_adapter::Dimension;
|
||||
use yazi_config::popup::{Origin, Position};
|
||||
use yazi_fs::Folder;
|
||||
use yazi_shared::fs::{File, Url};
|
||||
use yazi_shared::{Id, fs::{File, Url}};
|
||||
|
||||
use super::{Mimetype, Tabs, Watcher, Yanked};
|
||||
use crate::tab::Tab;
|
||||
|
|
@ -48,10 +48,10 @@ impl Manager {
|
|||
pub fn active_mut(&mut self) -> &mut Tab { self.tabs.active_mut() }
|
||||
|
||||
#[inline]
|
||||
pub fn active_or(&self, idx: Option<usize>) -> &Tab { self.tabs.active_or(idx) }
|
||||
pub fn active_or(&self, id: Option<Id>) -> &Tab { self.tabs.active_or(id) }
|
||||
|
||||
#[inline]
|
||||
pub fn active_or_mut(&mut self, idx: Option<usize>) -> &mut Tab { self.tabs.active_or_mut(idx) }
|
||||
pub fn active_or_mut(&mut self, id: Option<Id>) -> &mut Tab { self.tabs.active_or_mut(id) }
|
||||
|
||||
#[inline]
|
||||
pub fn current(&self) -> &Folder { &self.active().current }
|
||||
|
|
@ -60,10 +60,10 @@ impl Manager {
|
|||
pub fn current_mut(&mut self) -> &mut Folder { &mut self.active_mut().current }
|
||||
|
||||
#[inline]
|
||||
pub fn current_or(&self, idx: Option<usize>) -> &Folder { &self.active_or(idx).current }
|
||||
pub fn current_or(&self, idx: Option<Id>) -> &Folder { &self.active_or(idx).current }
|
||||
|
||||
#[inline]
|
||||
pub fn current_or_mut(&mut self, idx: Option<usize>) -> &mut Folder {
|
||||
pub fn current_or_mut(&mut self, idx: Option<Id>) -> &mut Folder {
|
||||
&mut self.active_or_mut(idx).current
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut};
|
|||
use yazi_boot::BOOT;
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_proxy::ManagerProxy;
|
||||
use yazi_shared::fs::Url;
|
||||
use yazi_shared::{Id, fs::Url};
|
||||
|
||||
use crate::tab::Tab;
|
||||
|
||||
|
|
@ -16,7 +16,6 @@ impl Tabs {
|
|||
pub fn make() -> Self {
|
||||
let mut tabs =
|
||||
Self { cursor: 0, items: (0..BOOT.cwds.len()).map(|_| Tab::default()).collect() };
|
||||
tabs.reorder();
|
||||
|
||||
for (i, tab) in tabs.iter_mut().enumerate() {
|
||||
let file = &BOOT.files[i];
|
||||
|
|
@ -29,7 +28,6 @@ impl Tabs {
|
|||
tabs
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn absolute(&self, rel: isize) -> usize {
|
||||
if rel > 0 {
|
||||
(self.cursor + rel as usize).min(self.items.len() - 1)
|
||||
|
|
@ -38,16 +36,7 @@ impl Tabs {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn reorder(&mut self) {
|
||||
self.items.iter_mut().enumerate().for_each(|(i, tab)| tab.idx = i);
|
||||
}
|
||||
|
||||
pub(super) fn set_idx(&mut self, idx: usize) {
|
||||
if self.cursor == idx {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the preview of the last active tab
|
||||
if let Some(active) = self.items.get_mut(self.cursor) {
|
||||
active.preview.reset_image();
|
||||
|
|
@ -68,16 +57,16 @@ impl Tabs {
|
|||
pub(super) fn active_mut(&mut self) -> &mut Tab { &mut self.items[self.cursor] }
|
||||
|
||||
#[inline]
|
||||
pub fn active_or(&self, idx: Option<usize>) -> &Tab {
|
||||
idx.and_then(|i| self.items.get(i)).unwrap_or(&self.items[self.cursor])
|
||||
pub fn active_or(&self, id: Option<Id>) -> &Tab {
|
||||
id.and_then(|id| self.iter().find(|&t| t.id == id)).unwrap_or(self.active())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn active_or_mut(&mut self, idx: Option<usize>) -> &mut Tab {
|
||||
if let Some(i) = idx.filter(|&i| i < self.items.len()) {
|
||||
pub(super) fn active_or_mut(&mut self, id: Option<Id>) -> &mut Tab {
|
||||
if let Some(i) = id.and_then(|id| self.iter().position(|t| t.id == id)) {
|
||||
&mut self.items[i]
|
||||
} else {
|
||||
&mut self.items[self.cursor]
|
||||
self.active_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
|
||||
ManagerProxy::hover(None, self.idx);
|
||||
ManagerProxy::hover(None, self.id);
|
||||
render!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl Tab {
|
|||
self.backstack.push(opt.target.clone());
|
||||
}
|
||||
|
||||
Pubsub::pub_from_cd(self.idx, self.cwd());
|
||||
Pubsub::pub_from_cd(self.id, self.cwd());
|
||||
ManagerProxy::refresh();
|
||||
render!();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
|
||||
// Copy the CWD path regardless even if the directory is empty
|
||||
if s.is_empty() && opt.type_ == "dirname" {
|
||||
s.push(self.cwd());
|
||||
}
|
||||
|
||||
futures::executor::block_on(CLIPBOARD.set(s));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl Tab {
|
|||
|
||||
self.current.repos(hovered.as_ref().map(|u| u.as_urn()));
|
||||
if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) {
|
||||
ManagerProxy::hover(None, self.idx);
|
||||
ManagerProxy::hover(None, self.id);
|
||||
}
|
||||
|
||||
render!();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ impl Tab {
|
|||
self.apply_files_attrs();
|
||||
|
||||
if hovered.as_ref() != self.hovered().map(|f| &f.url) {
|
||||
ManagerProxy::hover(hovered, self.idx);
|
||||
ManagerProxy::hover(hovered, self.id);
|
||||
} else if self.hovered().is_some_and(|f| f.is_dir()) {
|
||||
ManagerProxy::peek(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,6 @@ impl Tab {
|
|||
|
||||
self.cd(parent.clone());
|
||||
FilesOp::Creating(parent, vec![File::from_dummy(opt.target.clone(), None)]).emit();
|
||||
ManagerProxy::hover(Some(opt.target), self.idx);
|
||||
ManagerProxy::hover(Some(opt.target), self.id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,13 @@ use yazi_adapter::Dimension;
|
|||
use yazi_config::{LAYOUT, popup::{Origin, Position}};
|
||||
use yazi_fs::{Folder, FolderStage};
|
||||
use yazi_macro::render;
|
||||
use yazi_shared::fs::{File, Url};
|
||||
use yazi_shared::{Id, Ids, fs::{File, Url}};
|
||||
|
||||
use super::{Backstack, Config, Finder, History, Mode, Preview};
|
||||
use crate::tab::Selected;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Tab {
|
||||
pub idx: usize,
|
||||
pub id: Id,
|
||||
pub mode: Mode,
|
||||
pub conf: Config,
|
||||
pub current: Folder,
|
||||
|
|
@ -29,6 +28,28 @@ pub struct Tab {
|
|||
pub search: Option<JoinHandle<Result<()>>>,
|
||||
}
|
||||
|
||||
impl Default for Tab {
|
||||
fn default() -> Self {
|
||||
static IDS: Ids = Ids::new();
|
||||
|
||||
Self {
|
||||
id: IDS.next(),
|
||||
mode: Default::default(),
|
||||
conf: Default::default(),
|
||||
current: Default::default(),
|
||||
parent: Default::default(),
|
||||
|
||||
backstack: Default::default(),
|
||||
history: Default::default(),
|
||||
selected: Default::default(),
|
||||
|
||||
preview: Default::default(),
|
||||
finder: Default::default(),
|
||||
search: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn shutdown(&mut self) {
|
||||
if let Some(handle) = self.search.take() {
|
||||
|
|
@ -48,7 +69,7 @@ impl Tab {
|
|||
pub fn hovered_rect(&self) -> Option<Rect> {
|
||||
let y = self.current.files.position(self.hovered()?.urn())? - self.current.offset;
|
||||
|
||||
let mut rect = LAYOUT.load().current;
|
||||
let mut rect = LAYOUT.get().current;
|
||||
rect.y = rect.y.saturating_sub(1) + y as u16;
|
||||
rect.height = 1;
|
||||
Some(rect)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use serde::Serialize;
|
||||
use yazi_scheduler::Ongoing;
|
||||
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
|
||||
pub struct TasksProgress {
|
||||
pub total: u32,
|
||||
pub succ: u32,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ use std::borrow::Cow;
|
|||
|
||||
use mlua::{IntoLua, Lua, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_shared::fs::Url;
|
||||
use yazi_shared::{Id, fs::Url};
|
||||
|
||||
use super::Body;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct BodyCd<'a> {
|
||||
pub tab: usize,
|
||||
pub tab: Id,
|
||||
pub url: Cow<'a, Url>,
|
||||
#[serde(skip)]
|
||||
dummy: bool,
|
||||
|
|
@ -16,14 +16,14 @@ pub struct BodyCd<'a> {
|
|||
|
||||
impl<'a> BodyCd<'a> {
|
||||
#[inline]
|
||||
pub fn borrowed(tab: usize, url: &'a Url) -> Body<'a> {
|
||||
pub fn borrowed(tab: Id, url: &'a Url) -> Body<'a> {
|
||||
Self { tab, url: Cow::Borrowed(url), dummy: false }.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl BodyCd<'static> {
|
||||
#[inline]
|
||||
pub fn dummy(tab: usize) -> Body<'static> {
|
||||
pub fn dummy(tab: Id) -> Body<'static> {
|
||||
Self { tab, url: Default::default(), dummy: true }.into()
|
||||
}
|
||||
}
|
||||
|
|
@ -36,11 +36,11 @@ impl IntoLua<'_> for BodyCd<'static> {
|
|||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
if let Some(Cow::Owned(url)) = Some(self.url).filter(|_| !self.dummy) {
|
||||
lua.create_table_from([
|
||||
("tab", self.tab.into_lua(lua)?),
|
||||
("tab", self.tab.get().into_lua(lua)?),
|
||||
("url", lua.create_any_userdata(url)?.into_lua(lua)?),
|
||||
])?
|
||||
} else {
|
||||
lua.create_table_from([("tab", self.tab)])?
|
||||
lua.create_table_from([("tab", self.tab.get())])?
|
||||
}
|
||||
.into_lua(lua)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,26 @@ use std::borrow::Cow;
|
|||
|
||||
use mlua::{IntoLua, Lua, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_shared::fs::Url;
|
||||
use yazi_shared::{Id, fs::Url};
|
||||
|
||||
use super::Body;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct BodyHover<'a> {
|
||||
pub tab: usize,
|
||||
pub tab: Id,
|
||||
pub url: Option<Cow<'a, Url>>,
|
||||
}
|
||||
|
||||
impl<'a> BodyHover<'a> {
|
||||
#[inline]
|
||||
pub fn borrowed(tab: usize, url: Option<&'a Url>) -> Body<'a> {
|
||||
pub fn borrowed(tab: Id, url: Option<&'a Url>) -> Body<'a> {
|
||||
Self { tab, url: url.map(Cow::Borrowed) }.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl BodyHover<'static> {
|
||||
#[inline]
|
||||
pub fn dummy(tab: usize) -> Body<'static> { Self { tab, url: None }.into() }
|
||||
pub fn dummy(tab: Id) -> Body<'static> { Self { tab, url: None }.into() }
|
||||
}
|
||||
|
||||
impl<'a> From<BodyHover<'a>> for Body<'a> {
|
||||
|
|
@ -32,11 +32,11 @@ impl IntoLua<'_> for BodyHover<'static> {
|
|||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
if let Some(Cow::Owned(url)) = self.url {
|
||||
lua.create_table_from([
|
||||
("tab", self.tab.into_lua(lua)?),
|
||||
("tab", self.tab.get().into_lua(lua)?),
|
||||
("url", lua.create_any_userdata(url)?.into_lua(lua)?),
|
||||
])?
|
||||
} else {
|
||||
lua.create_table_from([("tab", self.tab)])?
|
||||
lua.create_table_from([("tab", self.tab.get())])?
|
||||
}
|
||||
.into_lua(lua)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet};
|
|||
use mlua::Function;
|
||||
use parking_lot::RwLock;
|
||||
use yazi_boot::BOOT;
|
||||
use yazi_shared::{RoCell, fs::Url};
|
||||
use yazi_shared::{Id, RoCell, fs::Url};
|
||||
|
||||
use crate::{Client, ID, PEERS, body::{Body, BodyBulk, BodyCd, BodyDelete, BodyHi, BodyHover, BodyMove, BodyMoveItem, BodyRename, BodyTab, BodyTrash, BodyYank}};
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ impl Pubsub {
|
|||
true
|
||||
}
|
||||
|
||||
pub fn pub_from_cd(tab: usize, url: &Url) {
|
||||
pub fn pub_from_cd(tab: Id, url: &Url) {
|
||||
if LOCAL.read().contains_key("cd") {
|
||||
Self::pub_(BodyCd::dummy(tab));
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ impl Pubsub {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pub_from_hover(tab: usize, url: Option<&Url>) {
|
||||
pub fn pub_from_hover(tab: Id, url: Option<&Url>) {
|
||||
if LOCAL.read().contains_key("hover") {
|
||||
Self::pub_(BodyHover::dummy(tab));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ yazi_macro::mod_flat!(
|
|||
notify
|
||||
plugin
|
||||
quit
|
||||
reflow
|
||||
render
|
||||
resize
|
||||
resume
|
||||
|
|
|
|||
55
yazi-fm/src/app/commands/reflow.rs
Normal file
55
yazi-fm/src/app/commands/reflow.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use mlua::Value;
|
||||
use ratatui::layout::Position;
|
||||
use tracing::error;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_macro::render;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use crate::{Root, app::App, lives::Lives};
|
||||
|
||||
struct Opt;
|
||||
|
||||
impl From<Cmd> for Opt {
|
||||
fn from(_: Cmd) -> Self { Self }
|
||||
}
|
||||
|
||||
impl From<()> for Opt {
|
||||
fn from(_: ()) -> Self { Self }
|
||||
}
|
||||
|
||||
impl App {
|
||||
#[yazi_codegen::command]
|
||||
pub fn reflow(&mut self, _: Opt) {
|
||||
let Some(size) = self.term.as_ref().and_then(|t| t.size().ok()) else { return };
|
||||
let mut layout = LAYOUT.get();
|
||||
|
||||
let result = Lives::scope(&self.cx, |_| {
|
||||
let comps = Root::reflow((Position::ORIGIN, size).into())?;
|
||||
|
||||
for v in comps.sequence_values::<Value>() {
|
||||
let Value::Table(t) = v? else {
|
||||
error!("`reflow()` must return a table of components");
|
||||
continue;
|
||||
};
|
||||
|
||||
let id: mlua::String = t.get("_id")?;
|
||||
match id.to_str()? {
|
||||
"current" => layout.current = *t.raw_get::<_, yazi_plugin::elements::Rect>("_area")?,
|
||||
"preview" => layout.preview = *t.raw_get::<_, yazi_plugin::elements::Rect>("_area")?,
|
||||
"progress" => layout.progress = *t.raw_get::<_, yazi_plugin::elements::Rect>("_area")?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if layout != LAYOUT.get() {
|
||||
LAYOUT.set(layout);
|
||||
render!();
|
||||
}
|
||||
|
||||
if let Err(e) = result {
|
||||
error!("Failed to `reflow()` the `Root` component:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ impl App {
|
|||
Self::patch(frame, self.cx.cursor());
|
||||
}
|
||||
if !self.cx.notify.messages.is_empty() {
|
||||
self.render_notify();
|
||||
self.render_partially();
|
||||
}
|
||||
|
||||
// Reload preview if collision is resolved
|
||||
|
|
@ -40,18 +40,19 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_notify(&mut self) {
|
||||
let Some(term) = &mut self.term else {
|
||||
return;
|
||||
};
|
||||
|
||||
pub(crate) fn render_partially(&mut self) {
|
||||
let Some(term) = &mut self.term else { return };
|
||||
if !term.can_partial() {
|
||||
return self.render();
|
||||
}
|
||||
|
||||
let frame = term
|
||||
.draw_partial(|f| {
|
||||
f.render_widget(crate::notify::Layout::new(&self.cx), f.area());
|
||||
_ = Lives::scope(&self.cx, |_| {
|
||||
f.render_widget(crate::tasks::Progress, f.area());
|
||||
f.render_widget(crate::notify::Notify::new(&self.cx), f.area());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if let Some(pos) = self.cx.cursor() {
|
||||
f.set_cursor_position(pos);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ impl App {
|
|||
#[yazi_codegen::command]
|
||||
pub fn resize(&mut self, _: Opt) {
|
||||
self.cx.manager.active_mut().preview.reset();
|
||||
self.render();
|
||||
self.reflow(());
|
||||
|
||||
self.cx.manager.current_mut().sync_page(true);
|
||||
self.cx.manager.hover(None);
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ impl App {
|
|||
pub(crate) fn update_notify(&mut self, cmd: Cmd) {
|
||||
let WindowSize { rows, columns, .. } = Dimension::available();
|
||||
let area =
|
||||
notify::Layout::available(Rect { x: 0, y: 0, width: columns, height: rows });
|
||||
notify::Notify::available(Rect { x: 0, y: 0, width: columns, height: rows });
|
||||
|
||||
self.cx.notify.tick(cmd, area);
|
||||
|
||||
if self.cx.notify.messages.is_empty() {
|
||||
self.render();
|
||||
} else {
|
||||
self.render_notify();
|
||||
self.render_partially();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use ratatui::backend::Backend;
|
||||
use yazi_core::tasks::TasksProgress;
|
||||
use yazi_macro::render;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use crate::{app::App, components::Progress, lives::Lives};
|
||||
use crate::app::App;
|
||||
|
||||
pub struct Opt {
|
||||
progress: TasksProgress,
|
||||
|
|
@ -19,41 +18,28 @@ impl TryFrom<Cmd> for Opt {
|
|||
|
||||
impl App {
|
||||
pub(crate) fn update_progress(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Ok(opt) = opt.try_into() else {
|
||||
return;
|
||||
};
|
||||
let Ok(opt) = opt.try_into() else { return };
|
||||
|
||||
// Update the progress of all tasks.
|
||||
let tasks = &mut self.cx.tasks;
|
||||
let progressed = tasks.progress != opt.progress;
|
||||
tasks.progress = opt.progress;
|
||||
|
||||
// If the task manager is visible, update the summaries with a complete render.
|
||||
if tasks.visible {
|
||||
let new = tasks.paginate();
|
||||
if new.len() != tasks.summaries.len()
|
||||
|| new.iter().zip(&tasks.summaries).any(|(a, b)| a.name != b.name)
|
||||
{
|
||||
if tasks.summaries != new {
|
||||
tasks.summaries = new;
|
||||
tasks.arrow(0);
|
||||
return render!();
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, only partially update the progress.
|
||||
let Some(term) = &mut self.term else {
|
||||
return;
|
||||
};
|
||||
|
||||
_ = Lives::scope(&self.cx, |_| {
|
||||
for patch in Progress::partial_render(term.current_buffer_mut()) {
|
||||
term.backend_mut().draw(patch.iter().map(|(x, y, cell)| (*x, *y, cell)))?;
|
||||
if let Some(pos) = self.cx.cursor() {
|
||||
term.show_cursor()?;
|
||||
term.set_cursor_position(pos)?;
|
||||
if !progressed {
|
||||
} else if tasks.progress.total == 0 {
|
||||
render!();
|
||||
} else {
|
||||
self.render_partially();
|
||||
}
|
||||
term.backend_mut().flush()?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
yazi_macro::mod_flat!(preview progress);
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
use std::mem;
|
||||
|
||||
use mlua::{AnyUserData, Table, TableExt};
|
||||
use tracing::error;
|
||||
use yazi_plugin::{LUA, cast_to_renderable};
|
||||
|
||||
pub(crate) struct Progress;
|
||||
|
||||
impl Progress {
|
||||
pub(crate) fn partial_render(
|
||||
buf: &mut ratatui::buffer::Buffer,
|
||||
) -> Vec<Vec<(u16, u16, ratatui::buffer::Cell)>> {
|
||||
let mut patches = vec![];
|
||||
let mut f = || {
|
||||
let comp: Table = LUA.globals().raw_get("Progress")?;
|
||||
for widget in comp.call_method::<_, Vec<AnyUserData>>("partial_render", ())? {
|
||||
let Some(w) = cast_to_renderable(&widget) else { continue };
|
||||
|
||||
let area = w.area();
|
||||
w.render(buf);
|
||||
|
||||
let mut patch = Vec::with_capacity(area.width as usize * area.height as usize);
|
||||
for y in area.top()..area.bottom() {
|
||||
for x in area.left()..area.right() {
|
||||
patch.push((x, y, mem::take(&mut buf[(x, y)])));
|
||||
}
|
||||
}
|
||||
|
||||
buf.reset();
|
||||
patches.push(patch);
|
||||
}
|
||||
|
||||
Ok::<_, anyhow::Error>(())
|
||||
};
|
||||
|
||||
if let Err(e) = f() {
|
||||
error!("{e}");
|
||||
}
|
||||
patches
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
use ratatui::{buffer::Buffer, layout::{Constraint, Rect}, style::Stylize, text::Span, widgets::{Paragraph, Widget}};
|
||||
use ratatui::{buffer::Buffer, layout::{Constraint, Rect}, text::Span, widgets::{Paragraph, Widget}};
|
||||
use yazi_config::THEME;
|
||||
|
||||
pub(crate) struct Buttons;
|
||||
|
||||
|
|
@ -7,7 +8,11 @@ impl Widget for Buttons {
|
|||
let chunks =
|
||||
ratatui::layout::Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)]).split(area);
|
||||
|
||||
Paragraph::new(Span::raw(" [Y]es ").reversed()).centered().render(chunks[0], buf);
|
||||
Paragraph::new(Span::raw(" (N)o ")).centered().render(chunks[1], buf);
|
||||
Paragraph::new(Span::raw(&THEME.confirm.btn_labels[0]).style(THEME.confirm.btn_yes))
|
||||
.centered()
|
||||
.render(chunks[0], buf);
|
||||
Paragraph::new(Span::raw(&THEME.confirm.btn_labels[1]).style(THEME.confirm.btn_no))
|
||||
.centered()
|
||||
.render(chunks[1], buf);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use ratatui::{buffer::Buffer, layout::{Alignment, Constraint, Layout, Margin, Rect}, style::{Style, Stylize}, text::Line, widgets::{Block, BorderType, Widget}};
|
||||
use ratatui::{buffer::Buffer, layout::{Alignment, Constraint, Layout, Margin, Rect}, text::Line, widgets::{Block, BorderType, Widget}};
|
||||
use yazi_config::THEME;
|
||||
|
||||
use crate::Ctx;
|
||||
|
||||
|
|
@ -19,8 +20,8 @@ impl<'a> Widget for Confirm<'a> {
|
|||
|
||||
Block::bordered()
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(Style::new().blue())
|
||||
.title(Line::styled(&confirm.title, Style::new().blue()))
|
||||
.border_style(THEME.confirm.border)
|
||||
.title(Line::styled(&confirm.title, THEME.confirm.title))
|
||||
.title_alignment(Alignment::Center)
|
||||
.render(area, buf);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use ratatui::{buffer::Buffer, layout::{Margin, Rect}, style::{Style, Stylize}, widgets::{Block, Borders, Paragraph, Widget}};
|
||||
use ratatui::{buffer::Buffer, layout::{Margin, Rect}, widgets::{Block, Borders, Paragraph, Widget}};
|
||||
use yazi_config::THEME;
|
||||
|
||||
pub(crate) struct Content<'a> {
|
||||
p: Paragraph<'a>,
|
||||
|
|
@ -14,9 +15,14 @@ impl<'a> Widget for Content<'a> {
|
|||
let inner = area.inner(Margin::new(1, 0));
|
||||
|
||||
// Bottom border
|
||||
let block = Block::new().borders(Borders::BOTTOM).border_style(Style::new().blue());
|
||||
let block = Block::new().borders(Borders::BOTTOM).border_style(THEME.confirm.border);
|
||||
block.clone().render(area.inner(Margin::new(1, 0)), buf);
|
||||
|
||||
self.p.alignment(ratatui::layout::Alignment::Center).block(block).render(inner, buf);
|
||||
self
|
||||
.p
|
||||
.alignment(ratatui::layout::Alignment::Center)
|
||||
.block(block)
|
||||
.style(THEME.confirm.content)
|
||||
.render(inner, buf);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use ratatui::{buffer::Buffer, layout::{Margin, Rect}, style::{Style, Stylize}, widgets::{Block, Borders, Scrollbar, ScrollbarOrientation, ScrollbarState, StatefulWidget, Widget, Wrap}};
|
||||
use ratatui::{buffer::Buffer, layout::{Margin, Rect}, widgets::{Block, Borders, Scrollbar, ScrollbarOrientation, ScrollbarState, StatefulWidget, Widget, Wrap}};
|
||||
use yazi_config::THEME;
|
||||
|
||||
use crate::Ctx;
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ impl<'a> Widget for List<'a> {
|
|||
let inner = area.inner(Margin::new(2, 0));
|
||||
|
||||
// Bottom border
|
||||
let block = Block::new().borders(Borders::BOTTOM).border_style(Style::new().blue());
|
||||
let block = Block::new().borders(Borders::BOTTOM).border_style(THEME.confirm.border);
|
||||
block.clone().render(area.inner(Margin::new(1, 0)), buf);
|
||||
|
||||
let list = self
|
||||
|
|
@ -26,6 +27,7 @@ impl<'a> Widget for List<'a> {
|
|||
.clone()
|
||||
.scroll((self.cx.confirm.offset as u16, 0))
|
||||
.block(block)
|
||||
.style(THEME.confirm.list)
|
||||
.wrap(Wrap { trim: false });
|
||||
|
||||
// Vertical scrollbar
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ use yazi_config::{KEYMAP, THEME};
|
|||
use super::Bindings;
|
||||
use crate::Ctx;
|
||||
|
||||
pub(crate) struct Layout<'a> {
|
||||
pub(crate) struct Help<'a> {
|
||||
cx: &'a Ctx,
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a> {
|
||||
impl<'a> Help<'a> {
|
||||
pub fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
fn tips() -> String {
|
||||
|
|
@ -19,7 +19,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Layout<'a> {
|
||||
impl<'a> Widget for Help<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let help = &self.cx.help;
|
||||
yazi_plugin::elements::Clear::default().render(area, buf);
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(bindings layout);
|
||||
yazi_macro::mod_flat!(bindings help);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, Lua};
|
||||
use mlua::{AnyUserData, Lua, MetaMethod, UserDataMethods};
|
||||
|
||||
use super::SCOPE;
|
||||
|
||||
|
|
@ -21,6 +21,8 @@ impl Finder {
|
|||
}
|
||||
|
||||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|_| {})
|
||||
lua.register_userdata_type::<Self>(|reg| {
|
||||
reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.filter.to_string()));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ impl Folder {
|
|||
let window = match window {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
let limit = LAYOUT.load().preview.height as usize;
|
||||
let limit = LAYOUT.get().preview.height as usize;
|
||||
inner.offset..inner.files.len().min(inner.offset + limit)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ impl Preview {
|
|||
me.tab()
|
||||
.hovered_folder()
|
||||
.map(|f| {
|
||||
let limit = LAYOUT.load().preview.height as usize;
|
||||
let limit = LAYOUT.get().preview.height as usize;
|
||||
Folder::make(Some(me.skip..f.files.len().min(me.skip + limit)), f, me.tab())
|
||||
})
|
||||
.transpose()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ impl Tab {
|
|||
|
||||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|reg| {
|
||||
reg.add_field_method_get("id", |_, me| Ok(me.id.get()));
|
||||
reg.add_method("name", |lua, me, ()| {
|
||||
lua.create_string(me.current.url.name().as_encoded_bytes())
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@
|
|||
#[global_allocator]
|
||||
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
yazi_macro::mod_pub!(
|
||||
app completion components confirm help input lives notify pick tasks which
|
||||
);
|
||||
yazi_macro::mod_pub!(app completion confirm help input lives manager notify pick tasks which);
|
||||
|
||||
yazi_macro::mod_flat!(context executor logs panic root router signals term);
|
||||
|
||||
|
|
|
|||
1
yazi-fm/src/manager/mod.rs
Normal file
1
yazi-fm/src/manager/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(preview);
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(layout);
|
||||
yazi_macro::mod_flat!(notify);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ use yazi_core::notify::Message;
|
|||
|
||||
use crate::Ctx;
|
||||
|
||||
pub(crate) struct Layout<'a> {
|
||||
pub(crate) struct Notify<'a> {
|
||||
cx: &'a Ctx,
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a> {
|
||||
impl<'a> Notify<'a> {
|
||||
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
pub(crate) fn available(area: Rect) -> Rect {
|
||||
|
|
@ -38,7 +38,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Layout<'a> {
|
||||
impl<'a> Widget for Notify<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let notify = &self.cx.notify;
|
||||
|
||||
|
|
@ -3,8 +3,8 @@ use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
|||
use tracing::error;
|
||||
use yazi_plugin::{LUA, elements::render_widgets};
|
||||
|
||||
use super::{completion, confirm, input, pick, tasks, which};
|
||||
use crate::{Ctx, components, help};
|
||||
use super::{completion, confirm, help, input, manager, pick, tasks, which};
|
||||
use crate::Ctx;
|
||||
|
||||
pub(super) struct Root<'a> {
|
||||
cx: &'a Ctx,
|
||||
|
|
@ -12,6 +12,12 @@ pub(super) struct Root<'a> {
|
|||
|
||||
impl<'a> Root<'a> {
|
||||
pub(super) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
pub(super) fn reflow<'lua>(area: Rect) -> mlua::Result<Table<'lua>> {
|
||||
let area = yazi_plugin::elements::Rect::from(area);
|
||||
let root = LUA.globals().raw_get::<_, Table>("Root")?.call_method::<_, Table>("new", area)?;
|
||||
root.call_method("reflow", ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Root<'a> {
|
||||
|
|
@ -20,17 +26,17 @@ impl<'a> Widget for Root<'a> {
|
|||
let area = yazi_plugin::elements::Rect::from(area);
|
||||
let root = LUA.globals().raw_get::<_, Table>("Root")?.call_method::<_, Table>("new", area)?;
|
||||
|
||||
render_widgets(root.call_method("render", ())?, buf);
|
||||
render_widgets(root.call_method("redraw", ())?, buf);
|
||||
Ok::<_, mlua::Error>(())
|
||||
};
|
||||
if let Err(e) = f() {
|
||||
error!("Failed to render the `Root` component:\n{e}");
|
||||
error!("Failed to redraw the `Root` component:\n{e}");
|
||||
}
|
||||
|
||||
components::Preview::new(self.cx).render(area, buf);
|
||||
manager::Preview::new(self.cx).render(area, buf);
|
||||
|
||||
if self.cx.tasks.visible {
|
||||
tasks::Layout::new(self.cx).render(area, buf);
|
||||
tasks::Tasks::new(self.cx).render(area, buf);
|
||||
}
|
||||
|
||||
if self.cx.pick.visible {
|
||||
|
|
@ -46,7 +52,7 @@ impl<'a> Widget for Root<'a> {
|
|||
}
|
||||
|
||||
if self.cx.help.visible {
|
||||
help::Layout::new(self.cx).render(area, buf);
|
||||
help::Help::new(self.cx).render(area, buf);
|
||||
}
|
||||
|
||||
if self.cx.completion.visible {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(layout);
|
||||
yazi_macro::mod_flat!(progress tasks);
|
||||
|
|
|
|||
23
yazi-fm/src/tasks/progress.rs
Normal file
23
yazi-fm/src/tasks/progress.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use mlua::{Table, TableExt};
|
||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||
use tracing::error;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_plugin::{LUA, elements::render_widgets};
|
||||
|
||||
pub(crate) struct Progress;
|
||||
|
||||
impl Widget for Progress {
|
||||
fn render(self, _: Rect, buf: &mut Buffer) {
|
||||
let mut f = || {
|
||||
let area = yazi_plugin::elements::Rect::from(LAYOUT.get().progress);
|
||||
let progress =
|
||||
LUA.globals().raw_get::<_, Table>("Progress")?.call_method::<_, Table>("use", area)?;
|
||||
|
||||
render_widgets(progress.call_method("redraw", ())?, buf);
|
||||
Ok::<_, mlua::Error>(())
|
||||
};
|
||||
if let Err(e) = f() {
|
||||
error!("Failed to redraw the `Progress` component:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,11 @@ use yazi_core::tasks::TASKS_PERCENT;
|
|||
|
||||
use crate::Ctx;
|
||||
|
||||
pub(crate) struct Layout<'a> {
|
||||
pub(crate) struct Tasks<'a> {
|
||||
cx: &'a Ctx,
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a> {
|
||||
impl<'a> Tasks<'a> {
|
||||
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
pub(super) fn area(area: Rect) -> Rect {
|
||||
|
|
@ -28,7 +28,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Layout<'a> {
|
||||
impl<'a> Widget for Tasks<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let area = Self::area(area);
|
||||
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(cand layout);
|
||||
yazi_macro::mod_flat!(cand which);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
use std::{collections::{HashMap, HashSet}, mem, ops::Deref, sync::atomic::Ordering};
|
||||
use std::{collections::{HashMap, HashSet}, mem, ops::Deref};
|
||||
|
||||
use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_config::{MANAGER, manager::SortBy};
|
||||
use yazi_shared::fs::{Cha, FILES_TICKET, File, FilesOp, Url, Urn, UrnBuf, maybe_exists};
|
||||
use yazi_shared::{Id, fs::{Cha, FILES_TICKET, File, FilesOp, Url, Urn, UrnBuf, maybe_exists}};
|
||||
|
||||
use super::{FilesSorter, Filter};
|
||||
|
||||
pub struct Files {
|
||||
hidden: Vec<File>,
|
||||
items: Vec<File>,
|
||||
ticket: u64,
|
||||
ticket: Id,
|
||||
version: u64,
|
||||
pub revision: u64,
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ impl Files {
|
|||
|
||||
impl Files {
|
||||
pub fn update_full(&mut self, files: Vec<File>) {
|
||||
self.ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed);
|
||||
self.ticket = FILES_TICKET.next();
|
||||
|
||||
(self.hidden, self.items) = self.split_files(files);
|
||||
if !self.items.is_empty() {
|
||||
|
|
@ -126,7 +126,7 @@ impl Files {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_part(&mut self, files: Vec<File>, ticket: u64) {
|
||||
pub fn update_part(&mut self, files: Vec<File>, ticket: Id) {
|
||||
if !files.is_empty() {
|
||||
if ticket != self.ticket {
|
||||
return;
|
||||
|
|
@ -162,7 +162,7 @@ impl Files {
|
|||
}
|
||||
|
||||
pub fn update_ioerr(&mut self) {
|
||||
self.ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed);
|
||||
self.ticket = FILES_TICKET.next();
|
||||
self.hidden.clear();
|
||||
self.items.clear();
|
||||
}
|
||||
|
|
@ -349,7 +349,7 @@ impl Files {
|
|||
|
||||
// --- Ticket
|
||||
#[inline]
|
||||
pub fn ticket(&self) -> u64 { self.ticket }
|
||||
pub fn ticket(&self) -> Id { self.ticket }
|
||||
|
||||
// --- Sorter
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ impl Folder {
|
|||
}
|
||||
|
||||
pub fn sync_page(&mut self, force: bool) {
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
if limit == 0 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ impl Folder {
|
|||
let old = (self.cursor, self.offset);
|
||||
let len = self.files.len();
|
||||
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
let scrolloff = (limit / 2).min(MANAGER.scrolloff as usize);
|
||||
|
||||
self.cursor = step.add(self.cursor, limit).min(len.saturating_sub(1));
|
||||
|
|
@ -126,7 +126,7 @@ impl Folder {
|
|||
let old = (self.cursor, self.offset);
|
||||
let max = self.files.len().saturating_sub(1);
|
||||
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
let scrolloff = (limit / 2).min(MANAGER.scrolloff as usize);
|
||||
|
||||
self.cursor = step.add(self.cursor, limit).min(max);
|
||||
|
|
@ -143,13 +143,13 @@ impl Folder {
|
|||
let old = self.offset;
|
||||
let len = self.files.len();
|
||||
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
let scrolloff = (limit / 2).min(MANAGER.scrolloff as usize);
|
||||
|
||||
self.offset = if self.cursor < (self.offset + limit).min(len).saturating_sub(scrolloff) {
|
||||
len.saturating_sub(limit).min(self.offset)
|
||||
} else {
|
||||
len.saturating_sub(limit).min(self.cursor.saturating_sub(limit) + scrolloff)
|
||||
len.saturating_sub(limit).min(self.cursor.saturating_sub(limit) + 1 + scrolloff)
|
||||
};
|
||||
|
||||
old != self.offset
|
||||
|
|
@ -162,7 +162,7 @@ impl Folder {
|
|||
|
||||
pub fn paginate(&self, page: usize) -> &[File] {
|
||||
let len = self.files.len();
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
|
||||
let start = (page.saturating_sub(1) * limit).min(len.saturating_sub(1));
|
||||
let end = ((page + 2) * limit).min(len);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ impl FilesSorter {
|
|||
if self.sensitive {
|
||||
self.cmp(a.name(), b.name(), self.promote(a, b))
|
||||
} else {
|
||||
self.cmp(a.name().to_ascii_uppercase(), b.name().to_ascii_uppercase(), self.promote(a, b))
|
||||
self.cmp_insensitive(
|
||||
a.name().as_encoded_bytes(),
|
||||
b.name().as_encoded_bytes(),
|
||||
self.promote(a, b),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -40,9 +44,9 @@ impl FilesSorter {
|
|||
let ord = if self.sensitive {
|
||||
self.cmp(a.url.extension(), b.url.extension(), self.promote(a, b))
|
||||
} else {
|
||||
self.cmp(
|
||||
a.url.extension().map(|s| s.to_ascii_lowercase()),
|
||||
b.url.extension().map(|s| s.to_ascii_lowercase()),
|
||||
self.cmp_insensitive(
|
||||
a.url.extension().map_or(&[], |s| s.as_encoded_bytes()),
|
||||
b.url.extension().map_or(&[], |s| s.as_encoded_bytes()),
|
||||
self.promote(a, b),
|
||||
)
|
||||
};
|
||||
|
|
@ -99,6 +103,25 @@ impl FilesSorter {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn cmp_insensitive(&self, a: &[u8], b: &[u8], promote: Ordering) -> Ordering {
|
||||
if promote != Ordering::Equal {
|
||||
return promote;
|
||||
}
|
||||
|
||||
let l = a.len().min(b.len());
|
||||
let (lhs, rhs) = if self.reverse { (&b[..l], &a[..l]) } else { (&a[..l], &b[..l]) };
|
||||
|
||||
for i in 0..l {
|
||||
match lhs[i].to_ascii_lowercase().cmp(&rhs[i].to_ascii_lowercase()) {
|
||||
Ordering::Equal => (),
|
||||
not_eq => return not_eq,
|
||||
}
|
||||
}
|
||||
|
||||
if self.reverse { b.len().cmp(&a.len()) } else { a.len().cmp(&b.len()) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn promote(&self, a: &File, b: &File) -> Ordering {
|
||||
if self.dir_first { b.is_dir().cmp(&a.is_dir()) } else { Ordering::Equal }
|
||||
|
|
|
|||
|
|
@ -11,19 +11,21 @@ function Current:new(area, tab)
|
|||
end
|
||||
|
||||
function Current:empty()
|
||||
local line
|
||||
local text
|
||||
if self._folder.files.filter then
|
||||
line = ui.Line("No filter results")
|
||||
text = ui.Text("No filter results")
|
||||
else
|
||||
line = ui.Line(self._folder.stage.is_loading and "Loading..." or "No items")
|
||||
text = ui.Text(self._folder.stage.is_loading and "Loading..." or "No items")
|
||||
end
|
||||
|
||||
return {
|
||||
ui.Text(line):area(self._area):align(ui.Text.CENTER),
|
||||
text:area(self._area):align(ui.Text.CENTER),
|
||||
}
|
||||
end
|
||||
|
||||
function Current:render()
|
||||
function Current:reflow() return { self } end
|
||||
|
||||
function Current:redraw()
|
||||
local files = self._folder.window
|
||||
if #files == 0 then
|
||||
return self:empty()
|
||||
|
|
@ -31,8 +33,8 @@ function Current:render()
|
|||
|
||||
local entities, linemodes = {}, {}
|
||||
for _, f in ipairs(files) do
|
||||
linemodes[#linemodes + 1] = Linemode:new(f):render()
|
||||
entities[#entities + 1] = Entity:new(f):render()
|
||||
entities[#entities + 1] = Entity:new(f):redraw()
|
||||
linemodes[#linemodes + 1] = Linemode:new(f):redraw()
|
||||
end
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ function Entity:symlink()
|
|||
return to and ui.Line(" -> " .. tostring(to)):italic() or ui.Line {}
|
||||
end
|
||||
|
||||
function Entity:render()
|
||||
function Entity:redraw()
|
||||
local lines = {}
|
||||
for _, c in ipairs(self._children) do
|
||||
lines[#lines + 1] = (type(c[1]) == "string" and self[c[1]] or c[1])(self)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ function Header:new(area, tab)
|
|||
return setmetatable({
|
||||
_area = area,
|
||||
_tab = tab,
|
||||
_current = tab.current,
|
||||
}, { __index = self })
|
||||
end
|
||||
|
||||
|
|
@ -26,22 +27,26 @@ function Header:cwd()
|
|||
return ui.Span("")
|
||||
end
|
||||
|
||||
local s = ya.readable_path(tostring(self._tab.current.cwd)) .. self:flags()
|
||||
local s = ya.readable_path(tostring(self._current.cwd)) .. self:flags()
|
||||
return ui.Span(ya.truncate(s, { max = max, rtl = true })):style(THEME.manager.cwd)
|
||||
end
|
||||
|
||||
function Header:flags()
|
||||
local cwd = self._tab.current.cwd
|
||||
local filter = self._tab.current.files.filter
|
||||
local cwd = self._current.cwd
|
||||
local filter = self._current.files.filter
|
||||
local finder = self._tab.finder
|
||||
|
||||
local s = cwd.is_search and string.format(" (search: %s", cwd:frag()) or ""
|
||||
if not filter then
|
||||
return s == "" and s or s .. ")"
|
||||
elseif s == "" then
|
||||
return string.format(" (filter: %s)", tostring(filter))
|
||||
else
|
||||
return string.format("%s, filter: %s)", s, tostring(filter))
|
||||
local t = {}
|
||||
if cwd.is_search then
|
||||
t[#t + 1] = string.format("search: %s", cwd:frag())
|
||||
end
|
||||
if filter then
|
||||
t[#t + 1] = string.format("filter: %s", filter)
|
||||
end
|
||||
if finder then
|
||||
t[#t + 1] = string.format("find: %s", finder)
|
||||
end
|
||||
return #t == 0 and "" or " (" .. table.concat(t, ", ") .. ")"
|
||||
end
|
||||
|
||||
function Header:count()
|
||||
|
|
@ -65,7 +70,7 @@ function Header:count()
|
|||
|
||||
return ui.Line {
|
||||
ui.Span(string.format(" %d ", count)):style(style),
|
||||
ui.Span(" "),
|
||||
" ",
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -90,11 +95,14 @@ function Header:tabs()
|
|||
return ui.Line(spans)
|
||||
end
|
||||
|
||||
function Header:render()
|
||||
local right = self:children_render(self.RIGHT)
|
||||
function Header:reflow() return { self } end
|
||||
|
||||
function Header:redraw()
|
||||
local right = self:children_redraw(self.RIGHT)
|
||||
self._right_width = right:width()
|
||||
|
||||
local left = self:children_render(self.LEFT)
|
||||
local left = self:children_redraw(self.LEFT)
|
||||
|
||||
return {
|
||||
ui.Text(left):area(self._area),
|
||||
ui.Text(right):area(self._area):align(ui.Text.RIGHT),
|
||||
|
|
@ -129,7 +137,7 @@ function Header:children_remove(id, side)
|
|||
end
|
||||
end
|
||||
|
||||
function Header:children_render(side)
|
||||
function Header:children_redraw(side)
|
||||
local lines = {}
|
||||
for _, c in ipairs(side == self.RIGHT and self._right or self._left) do
|
||||
lines[#lines + 1] = (type(c[1]) == "string" and self[c[1]] or c[1])(self)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue