From 6c0a48258072b0444a5e35df2148fdf9d926b7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Fri, 12 Jul 2024 18:32:01 +0800 Subject: [PATCH] feat: make UI extensions easier (#1257) --- Cargo.lock | 189 ++++++++++++++------- yazi-boot/Cargo.toml | 12 +- yazi-boot/build.rs | 7 +- yazi-cli/Cargo.toml | 14 +- yazi-cli/build.rs | 7 +- yazi-config/Cargo.toml | 4 +- yazi-config/preset/theme.toml | 6 +- yazi-config/preset/yazi.toml | 2 +- yazi-config/src/layout.rs | 7 +- yazi-config/src/lib.rs | 9 +- yazi-core/Cargo.toml | 4 +- yazi-dds/Cargo.toml | 6 +- yazi-dds/build.rs | 6 +- yazi-fm/Cargo.toml | 4 +- yazi-fm/src/app/commands/mouse.rs | 66 +++---- yazi-fm/src/components/current.rs | 23 --- yazi-fm/src/components/header.rs | 39 ----- yazi-fm/src/components/manager.rs | 20 --- yazi-fm/src/components/mod.rs | 10 -- yazi-fm/src/components/parent.rs | 23 --- yazi-fm/src/components/preview.rs | 19 --- yazi-fm/src/components/progress.rs | 4 +- yazi-fm/src/components/status.rs | 39 ----- yazi-fm/src/lives/lives.rs | 19 +-- yazi-fm/src/main.rs | 2 +- yazi-fm/src/root.rs | 36 ++-- yazi-plugin/Cargo.toml | 2 +- yazi-plugin/preset/compat.lua | 40 +++++ yazi-plugin/preset/components/current.lua | 53 +++--- yazi-plugin/preset/components/entity.lua | 109 ++++++++++++ yazi-plugin/preset/components/file.lua | 99 ----------- yazi-plugin/preset/components/folder.lua | 92 ---------- yazi-plugin/preset/components/header.lua | 28 +-- yazi-plugin/preset/components/linemode.lua | 77 +++++++++ yazi-plugin/preset/components/manager.lua | 33 ---- yazi-plugin/preset/components/marker.lua | 71 ++++++++ yazi-plugin/preset/components/parent.lua | 40 ++--- yazi-plugin/preset/components/preview.lua | 21 ++- yazi-plugin/preset/components/progress.lua | 11 +- yazi-plugin/preset/components/rail.lua | 38 +++++ yazi-plugin/preset/components/root.lua | 57 ++++++- yazi-plugin/preset/components/status.lua | 102 ++++++++--- yazi-plugin/preset/components/tab.lua | 58 +++++++ yazi-plugin/preset/plugins/folder.lua | 27 +-- yazi-plugin/preset/plugins/noop.lua | 2 + yazi-plugin/preset/ya.lua | 23 +-- yazi-plugin/src/bindings/mouse.rs | 4 +- yazi-plugin/src/cast.rs | 2 +- yazi-plugin/src/elements/constraint.rs | 24 +-- yazi-plugin/src/elements/elements.rs | 18 +- yazi-plugin/src/elements/line.rs | 12 +- yazi-plugin/src/elements/mod.rs | 2 + yazi-plugin/src/elements/position.rs | 38 +++++ yazi-plugin/src/elements/rect.rs | 4 +- yazi-plugin/src/isolate/entry.rs | 2 +- yazi-plugin/src/isolate/fetch.rs | 2 +- yazi-plugin/src/isolate/isolate.rs | 2 +- yazi-plugin/src/isolate/peek.rs | 2 +- yazi-plugin/src/isolate/preload.rs | 2 +- yazi-plugin/src/lib.rs | 5 +- yazi-plugin/src/loader/loader.rs | 56 +++--- yazi-plugin/src/lua.rs | 38 +++-- yazi-plugin/src/utils/call.rs | 39 ++++- yazi-plugin/src/utils/log.rs | 4 +- yazi-plugin/src/utils/preview.rs | 2 +- yazi-shared/Cargo.toml | 4 +- 66 files changed, 1034 insertions(+), 788 deletions(-) delete mode 100644 yazi-fm/src/components/current.rs delete mode 100644 yazi-fm/src/components/header.rs delete mode 100644 yazi-fm/src/components/manager.rs delete mode 100644 yazi-fm/src/components/parent.rs delete mode 100644 yazi-fm/src/components/status.rs create mode 100644 yazi-plugin/preset/compat.lua create mode 100644 yazi-plugin/preset/components/entity.lua delete mode 100644 yazi-plugin/preset/components/file.lua delete mode 100644 yazi-plugin/preset/components/folder.lua create mode 100644 yazi-plugin/preset/components/linemode.lua delete mode 100644 yazi-plugin/preset/components/manager.lua create mode 100644 yazi-plugin/preset/components/marker.lua create mode 100644 yazi-plugin/preset/components/rail.lua create mode 100644 yazi-plugin/preset/components/tab.lua create mode 100644 yazi-plugin/src/elements/position.rs diff --git a/Cargo.lock b/Cargo.lock index e12338114..14a5a7e3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,9 +206,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.7" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -313,9 +313,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -325,9 +325,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.6" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbca90c87c2a04da41e95d1856e8bcd22f159bdbfa147314d2ce5218057b0e58" +checksum = "5b4be9c4c4b1f30b78d8a750e0822b6a6102d97e62061c583a6c1dea2dfb33ae" dependencies = [ "clap", ] @@ -354,14 +354,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -372,9 +372,9 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clipboard-win" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ "error-code", ] @@ -480,7 +480,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "futures-core", "libc", @@ -537,7 +537,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -548,7 +548,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -560,6 +560,37 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +dependencies = [ + "derive_builder_core", + "syn 2.0.68", +] + [[package]] name = "digest" version = "0.10.7" @@ -792,7 +823,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -846,6 +877,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getset" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "gif" version = "0.13.1" @@ -1110,7 +1153,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] @@ -1257,7 +1300,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1282,7 +1325,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "filetime", "fsevent-sys", "inotify", @@ -1366,7 +1409,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "block2", "libc", "objc2", @@ -1588,7 +1631,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", @@ -1638,7 +1681,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1699,7 +1742,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1735,9 +1778,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -1754,20 +1797,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -1893,7 +1936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1927,7 +1970,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1937,14 +1980,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", + "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.67" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -1989,7 +2033,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -2108,7 +2152,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -2159,9 +2203,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap", "serde", @@ -2201,7 +2245,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -2371,7 +2415,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -2382,14 +2426,41 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vergen" -version = "8.3.1" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27d6bdd219887a9eadd19e1c34f32e47fa332301184935c6d9bca26f3cca525" +checksum = "c32e7318e93a9ac53693b6caccfb05ff22e04a44c7cf8a279051f24c09da286f" dependencies = [ "anyhow", - "cfg-if", + "derive_builder", + "rustversion", + "time", + "vergen-lib", +] + +[[package]] +name = "vergen-gitcl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bbdc9746577cb4767f218d320ee0b623d415e8130332f8f562b910b61cc2c4e" +dependencies = [ + "anyhow", + "derive_builder", "rustversion", "time", + "vergen", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e06bee42361e43b60f363bad49d63798d0f42fb1768091812270eca00c784720" +dependencies = [ + "anyhow", + "derive_builder", + "getset", + "rustversion", ] [[package]] @@ -2435,7 +2506,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", "wasm-bindgen-shared", ] @@ -2457,7 +2528,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2556,7 +2627,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -2567,7 +2638,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -2765,7 +2836,7 @@ dependencies = [ "clap_complete_nushell", "regex", "serde", - "vergen", + "vergen-gitcl", "yazi-adapter", "yazi-config", "yazi-shared", @@ -2785,7 +2856,7 @@ dependencies = [ "serde_json", "tokio", "toml_edit", - "vergen", + "vergen-gitcl", "yazi-dds", "yazi-shared", ] @@ -2796,7 +2867,7 @@ version = "0.2.5" dependencies = [ "anyhow", "arc-swap", - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm", "globset", "indexmap", @@ -2812,7 +2883,7 @@ name = "yazi-core" version = "0.2.5" dependencies = [ "anyhow", - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm", "dirs", "futures", @@ -2852,7 +2923,7 @@ dependencies = [ "tokio-stream", "tokio-util", "uzers", - "vergen", + "vergen-gitcl", "yazi-boot", "yazi-shared", ] @@ -2962,7 +3033,7 @@ name = "yazi-shared" version = "0.2.5" dependencies = [ "anyhow", - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm", "dirs", "futures", @@ -2979,22 +3050,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index ebb7864f3..415fe5763 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -15,12 +15,12 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -clap = { version = "4.5.7", features = [ "derive" ] } -serde = { version = "1.0.203", features = [ "derive" ] } +clap = { version = "4.5.9", features = [ "derive" ] } +serde = { version = "1.0.204", features = [ "derive" ] } [build-dependencies] -clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.6" -clap_complete_nushell = "4.5.2" +clap = { version = "4.5.9", features = [ "derive" ] } +clap_complete = "4.5.8" clap_complete_fig = "4.5.1" -vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } +clap_complete_nushell = "4.5.2" +vergen-gitcl = { version = "1.0.0", features = [ "build" ] } diff --git a/yazi-boot/build.rs b/yazi-boot/build.rs index 2eacb6aa5..754b1e8fb 100644 --- a/yazi-boot/build.rs +++ b/yazi-boot/build.rs @@ -5,10 +5,13 @@ use std::{env, error::Error}; use clap::CommandFactory; use clap_complete::{generate_to, Shell}; -use vergen::EmitBuilder; +use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; fn main() -> Result<(), Box> { - EmitBuilder::builder().build_date().git_sha(true).emit()?; + Emitter::default() + .add_instructions(&BuildBuilder::default().build_date(true).build()?)? + .add_instructions(&GitclBuilder::default().commit_date(true).sha(true).build()?)? + .emit()?; if env::var_os("YAZI_GEN_COMPLETIONS").is_none() { return Ok(()); diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 1b2d04c53..25b4ae792 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -14,21 +14,21 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" -clap = { version = "4.5.7", features = [ "derive" ] } +clap = { version = "4.5.9", features = [ "derive" ] } crossterm = "0.27.0" md-5 = "0.10.6" -serde_json = "1.0.117" +serde_json = "1.0.120" tokio = { version = "1.38.0", features = [ "full" ] } -toml_edit = "0.22.14" +toml_edit = "0.22.15" [build-dependencies] anyhow = "1.0.86" -clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.6" +clap = { version = "4.5.9", features = [ "derive" ] } +clap_complete = "4.5.8" clap_complete_fig = "4.5.1" clap_complete_nushell = "4.5.2" -serde_json = "1.0.117" -vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } +serde_json = "1.0.120" +vergen-gitcl = { version = "1.0.0", features = [ "build" ] } [[bin]] name = "ya" diff --git a/yazi-cli/build.rs b/yazi-cli/build.rs index b9eb8efd0..878d36714 100644 --- a/yazi-cli/build.rs +++ b/yazi-cli/build.rs @@ -5,10 +5,13 @@ use std::{env, error::Error}; use clap::CommandFactory; use clap_complete::{generate_to, Shell}; -use vergen::EmitBuilder; +use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; fn main() -> Result<(), Box> { - EmitBuilder::builder().build_date().git_sha(true).emit()?; + Emitter::default() + .add_instructions(&BuildBuilder::default().build_date(true).build()?)? + .add_instructions(&GitclBuilder::default().commit_date(true).sha(true).build()?)? + .emit()?; if env::var_os("YAZI_GEN_COMPLETIONS").is_none() { return Ok(()); diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 8df9f74db..9b73f1a97 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -14,11 +14,11 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" arc-swap = "1.7.1" -bitflags = "2.5.0" +bitflags = "2.6.0" crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" ratatui = "0.27.0" -serde = { version = "1.0.203", features = [ "derive" ] } +serde = { version = "1.0.204", features = [ "derive" ] } toml = { version = "0.8.14", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 5a0b86246..444af5d1b 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -773,9 +773,9 @@ conds = [ { if = "sticky", text = "" }, # Fallback - { if = "dir", text = "" }, - { if = "exec", text = "" }, - { if = "!dir", text = "" }, + { if = "dir", text = "󰉋" }, + { if = "exec", text = "" }, + { if = "!dir", text = "󰈔" }, ] # : }}} diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index c36417e26..cdbe1ebca 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -122,7 +122,7 @@ previewers = [ ] [input] -cursor_blink = true +cursor_blink = false # cd cd_title = "Change directory:" diff --git a/yazi-config/src/layout.rs b/yazi-config/src/layout.rs index c63809d54..80b3d986f 100644 --- a/yazi-config/src/layout.rs +++ b/yazi-config/src/layout.rs @@ -1,12 +1,7 @@ use ratatui::layout::Rect; -#[derive(Default)] +#[derive(Clone, Copy, Default)] pub struct Layout { - pub header: Rect, - - pub parent: Rect, pub current: Rect, pub preview: Rect, - - pub status: Rect, } diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index 60204f59f..bf177afad 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -61,13 +61,20 @@ pub fn init() -> anyhow::Result<()> { // TODO: Remove in v0.3.2 for c in &KEYMAP.manager { for r in &c.run { - if r.name == "shell" && !r.bool("confirm") && !r.bool("interactive") { + if r.name != "shell" { + continue; + } + if !r.bool("confirm") && !r.bool("interactive") { eprintln!( r#"WARNING: In Yazi v0.3, the behavior of the interactive `shell` (i.e., shell templates) must be explicitly specified with `--interactive`. Please replace e.g. `shell` with `shell --interactive`, `shell "my-template"` with `shell "my-template" --interactive`, in your keymap.toml"# ); return Ok(()); + } else if r.bool("confirm") && r.bool("interactive") { + eprintln!( + "The `shell` command cannot specify both `--confirm` and `--interactive` at the same time.", + ); } } } diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index f35bdc6ae..19fa589d5 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -20,7 +20,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" -bitflags = "2.5.0" +bitflags = "2.6.0" crossterm = "0.27.0" dirs = "5.0.1" futures = "0.3.30" @@ -29,7 +29,7 @@ parking_lot = "0.12.3" ratatui = "0.27.0" regex = "1.10.5" scopeguard = "1.2.0" -serde = "1.0.203" +serde = "1.0.204" shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index d364f54d6..04b19d86d 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -20,14 +20,14 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } anyhow = "1.0.86" mlua = { version = "0.9.9", features = [ "lua54" ] } parking_lot = "0.12.3" -serde = { version = "1.0.203", features = [ "derive" ] } -serde_json = "1.0.117" +serde = { version = "1.0.204", features = [ "derive" ] } +serde_json = "1.0.120" tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" [build-dependencies] -vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } +vergen-gitcl = { version = "1.0.0", features = [ "build" ] } [target."cfg(unix)".dependencies] uzers = "0.12.0" diff --git a/yazi-dds/build.rs b/yazi-dds/build.rs index aef969b94..1b50a623d 100644 --- a/yazi-dds/build.rs +++ b/yazi-dds/build.rs @@ -1,9 +1,11 @@ use std::error::Error; -use vergen::EmitBuilder; +use vergen_gitcl::{Emitter, GitclBuilder}; fn main() -> Result<(), Box> { - EmitBuilder::builder().git_sha(true).emit()?; + Emitter::default() + .add_instructions(&GitclBuilder::default().commit_date(true).sha(true).build()?)? + .emit()?; Ok(()) } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index f243a0ba4..b4d25202b 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -52,5 +52,5 @@ name = "yazi" path = "src/main.rs" [package.metadata.binstall] -pkg-url = "{ repo }/releases/download/v{ version }/yazi-{ target }{ archive-suffix }" -bin-dir = "yazi-{ target }/{ bin }{ binary-ext }" +pkg-url = "{repo}/releases/download/v{version}/yazi-{target}{archive-suffix}" +bin-dir = "yazi-{target}/{bin}{binary-ext}" diff --git a/yazi-fm/src/app/commands/mouse.rs b/yazi-fm/src/app/commands/mouse.rs index 5e786efc5..b124cb3ef 100644 --- a/yazi-fm/src/app/commands/mouse.rs +++ b/yazi-fm/src/app/commands/mouse.rs @@ -1,11 +1,10 @@ use crossterm::event::{MouseEvent, MouseEventKind}; -use mlua::Table; -use ratatui::layout::{Position, Rect}; +use mlua::{Table, TableExt}; use tracing::error; -use yazi_config::{LAYOUT, MANAGER}; +use yazi_config::MANAGER; use yazi_plugin::{bindings::Cast, LUA}; -use crate::{app::App, components, lives::Lives}; +use crate::{app::App, lives::Lives}; pub struct Opt { event: MouseEvent, @@ -19,47 +18,36 @@ impl App { pub(crate) fn mouse(&mut self, opt: impl Into) { let event = (opt.into() as Opt).event; - let layout = LAYOUT.load(); - let position = Position { x: event.column, y: event.row }; + let Some(size) = self.term.as_ref().and_then(|t| t.size().ok()) else { return }; + let Ok(evt) = yazi_plugin::bindings::MouseEvent::cast(&LUA, event) else { return }; - if matches!(event.kind, MouseEventKind::Moved | MouseEventKind::Drag(_)) { - self.mouse_do(crate::Root::mouse, event, None); - return; - } + let res = Lives::scope(&self.cx, move |_| { + let area = yazi_plugin::elements::Rect::cast(&LUA, size)?; + let root = LUA.globals().raw_get::<_, Table>("Root")?.call_method::<_, Table>("new", area)?; - if layout.current.contains(position) { - self.mouse_do(components::Current::mouse, event, Some(layout.current)); - } else if layout.preview.contains(position) { - self.mouse_do(components::Preview::mouse, event, Some(layout.preview)); - } else if layout.parent.contains(position) { - self.mouse_do(components::Parent::mouse, event, Some(layout.parent)); - } else if layout.header.contains(position) { - self.mouse_do(components::Header::mouse, event, Some(layout.header)); - } else if layout.status.contains(position) { - self.mouse_do(components::Status::mouse, event, Some(layout.status)); - } - } + if matches!(event.kind, MouseEventKind::Down(_) if MANAGER.mouse_events.draggable()) { + root.raw_set("_drag_start", evt.clone())?; + } + + match event.kind { + MouseEventKind::Down(_) => root.call_method("click", (evt, false))?, + MouseEventKind::Up(_) => root.call_method("click", (evt, true))?, - fn mouse_do( - &self, - f: impl FnOnce(MouseEvent) -> mlua::Result<()>, - mut event: MouseEvent, - rect: Option, - ) { - if matches!(event.kind, MouseEventKind::Down(_) if MANAGER.mouse_events.draggable()) { - let evt = yazi_plugin::bindings::MouseEvent::cast(&LUA, event); - if let (Ok(evt), Ok(root)) = (evt, LUA.globals().raw_get::<_, Table>("Root")) { - root.raw_set("drag_start", evt).ok(); + MouseEventKind::ScrollDown => root.call_method("scroll", (evt, 1))?, + MouseEventKind::ScrollUp => root.call_method("scroll", (evt, -1))?, + + MouseEventKind::ScrollRight => root.call_method("touch", (evt, 1))?, + MouseEventKind::ScrollLeft => root.call_method("touch", (evt, -1))?, + + MouseEventKind::Moved => root.call_method("move", evt)?, + MouseEventKind::Drag(_) => root.call_method("drag", evt)?, } - } - if let Some(rect) = rect { - event.row -= rect.y; - event.column -= rect.x; - } + Ok(()) + }); - if let Err(e) = Lives::scope(&self.cx, move |_| f(event)) { - error!("{:?}", e); + if let Err(e) = res { + error!("{e}"); } } } diff --git a/yazi-fm/src/components/current.rs b/yazi-fm/src/components/current.rs deleted file mode 100644 index a487047c8..000000000 --- a/yazi-fm/src/components/current.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crossterm::event::MouseEventKind; -use mlua::{Table, TableExt}; -use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; - -pub(crate) struct Current; - -impl Current { - pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { - let evt = MouseEvent::cast(&LUA, event)?; - let comp: Table = LUA.globals().raw_get("Current")?; - - match event.kind { - MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, - MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, - MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, - MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, - MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, - MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, - _ => (), - } - Ok(()) - } -} diff --git a/yazi-fm/src/components/header.rs b/yazi-fm/src/components/header.rs deleted file mode 100644 index 67ab82162..000000000 --- a/yazi-fm/src/components/header.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crossterm::event::MouseEventKind; -use mlua::{Table, TableExt}; -use ratatui::{buffer::Buffer, widgets::Widget}; -use tracing::error; -use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA}; - -pub(crate) struct Header; - -impl Widget for Header { - fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) { - let mut f = || { - let area = Rect::cast(&LUA, area)?; - let comp: Table = LUA.globals().raw_get("Header")?; - render_widgets(comp.call_method("render", area)?, buf); - Ok::<_, anyhow::Error>(()) - }; - if let Err(e) = f() { - error!("{:?}", e); - } - } -} - -impl Header { - pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { - let evt = MouseEvent::cast(&LUA, event)?; - let comp: Table = LUA.globals().raw_get("Header")?; - - match event.kind { - MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, - MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, - MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, - MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, - MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, - MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, - _ => (), - } - Ok(()) - } -} diff --git a/yazi-fm/src/components/manager.rs b/yazi-fm/src/components/manager.rs deleted file mode 100644 index bf4b266b5..000000000 --- a/yazi-fm/src/components/manager.rs +++ /dev/null @@ -1,20 +0,0 @@ -use mlua::{Table, TableExt}; -use ratatui::{buffer::Buffer, widgets::Widget}; -use tracing::error; -use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA}; - -pub(crate) struct Manager; - -impl Widget for Manager { - fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) { - let mut f = || { - let area = Rect::cast(&LUA, area)?; - let comp: Table = LUA.globals().raw_get("Manager")?; - render_widgets(comp.call_method("render", area)?, buf); - Ok::<_, anyhow::Error>(()) - }; - if let Err(e) = f() { - error!("{:?}", e); - } - } -} diff --git a/yazi-fm/src/components/mod.rs b/yazi-fm/src/components/mod.rs index 1898af855..96057cdcb 100644 --- a/yazi-fm/src/components/mod.rs +++ b/yazi-fm/src/components/mod.rs @@ -1,17 +1,7 @@ #![allow(clippy::module_inception)] -mod current; -mod header; -mod manager; -mod parent; mod preview; mod progress; -mod status; -pub(super) use current::*; -pub(super) use header::*; -pub(super) use manager::*; -pub(super) use parent::*; pub(super) use preview::*; pub(super) use progress::*; -pub(super) use status::*; diff --git a/yazi-fm/src/components/parent.rs b/yazi-fm/src/components/parent.rs deleted file mode 100644 index 86adf246a..000000000 --- a/yazi-fm/src/components/parent.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crossterm::event::MouseEventKind; -use mlua::{Table, TableExt}; -use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; - -pub(crate) struct Parent; - -impl Parent { - pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { - let evt = MouseEvent::cast(&LUA, event)?; - let comp: Table = LUA.globals().raw_get("Parent")?; - - match event.kind { - MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, - MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, - MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, - MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, - MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, - MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, - _ => (), - } - Ok(()) - } -} diff --git a/yazi-fm/src/components/preview.rs b/yazi-fm/src/components/preview.rs index d6ae62d0a..897a9f135 100644 --- a/yazi-fm/src/components/preview.rs +++ b/yazi-fm/src/components/preview.rs @@ -1,7 +1,4 @@ -use crossterm::event::MouseEventKind; -use mlua::{Table, TableExt}; use ratatui::{buffer::Buffer, widgets::Widget}; -use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; use crate::Ctx; @@ -12,22 +9,6 @@ pub(crate) struct Preview<'a> { impl<'a> Preview<'a> { #[inline] pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } - - pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { - let evt = MouseEvent::cast(&LUA, event)?; - let comp: Table = LUA.globals().raw_get("Preview")?; - - match event.kind { - MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, - MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, - MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, - MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, - MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, - MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, - _ => (), - } - Ok(()) - } } impl Widget for Preview<'_> { diff --git a/yazi-fm/src/components/progress.rs b/yazi-fm/src/components/progress.rs index 39de994ef..6bb093334 100644 --- a/yazi-fm/src/components/progress.rs +++ b/yazi-fm/src/components/progress.rs @@ -14,7 +14,7 @@ impl Progress { let mut f = || { let comp: Table = LUA.globals().raw_get("Progress")?; for widget in comp.call_method::<_, Vec>("partial_render", ())? { - let Some(w) = cast_to_renderable(widget) else { continue }; + let Some(w) = cast_to_renderable(&widget) else { continue }; let area = w.area(); w.render(buf); @@ -34,7 +34,7 @@ impl Progress { }; if let Err(e) = f() { - error!("{:?}", e); + error!("{e}"); } patches } diff --git a/yazi-fm/src/components/status.rs b/yazi-fm/src/components/status.rs deleted file mode 100644 index b9a1fe5e5..000000000 --- a/yazi-fm/src/components/status.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crossterm::event::MouseEventKind; -use mlua::{Table, TableExt}; -use ratatui::widgets::Widget; -use tracing::error; -use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA}; - -pub(crate) struct Status; - -impl Widget for Status { - fn render(self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { - let mut f = || { - let area = Rect::cast(&LUA, area)?; - let comp: Table = LUA.globals().raw_get("Status")?; - render_widgets(comp.call_method("render", area)?, buf); - Ok::<_, anyhow::Error>(()) - }; - if let Err(e) = f() { - error!("{:?}", e); - } - } -} - -impl Status { - pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { - let evt = MouseEvent::cast(&LUA, event)?; - let comp: Table = LUA.globals().raw_get("Status")?; - - match event.kind { - MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, - MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, - MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, - MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, - MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, - MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, - _ => (), - } - Ok(()) - } -} diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index 7380fc473..6f09852a2 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -1,10 +1,9 @@ -use std::{mem, sync::Arc}; +use std::mem; -use mlua::{Scope, Table}; +use mlua::Scope; use scopeguard::defer; use tracing::error; -use yazi_config::LAYOUT; -use yazi_plugin::{elements::RectRef, LUA}; +use yazi_plugin::LUA; use yazi_shared::RoCell; use crate::Ctx; @@ -52,17 +51,7 @@ impl Lives { ])?, )?; - let ret = f(scope)?; - - LAYOUT.store(Arc::new(yazi_config::Layout { - header: *globals.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("area")?, - parent: *globals.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("area")?, - current: *globals.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("area")?, - preview: *globals.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("area")?, - status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?, - })); - - Ok(ret) + f(scope) }); if let Err(ref e) = result { diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index 93d67543f..86363ab69 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -53,7 +53,7 @@ async fn main() -> anyhow::Result<()> { yazi_dds::init(); - yazi_plugin::init(); + yazi_plugin::init()?; yazi_core::init(); diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index 3ffeaaf89..684e997fb 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -1,7 +1,7 @@ -use crossterm::event::MouseEventKind; use mlua::{Table, TableExt}; -use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Rect}, widgets::Widget}; -use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; +use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; +use tracing::error; +use yazi_plugin::{bindings::Cast, elements::render_widgets, LUA}; use super::{completion, input, select, tasks, which}; use crate::{components, help, Ctx}; @@ -16,13 +16,17 @@ impl<'a> Root<'a> { impl<'a> Widget for Root<'a> { fn render(self, area: Rect, buf: &mut Buffer) { - let chunks = - Layout::vertical([Constraint::Length(1), Constraint::Fill(1), Constraint::Length(1)]) - .split(area); + let mut f = || { + let area = yazi_plugin::elements::Rect::cast(&LUA, area)?; + let root = LUA.globals().raw_get::<_, Table>("Root")?.call_method::<_, Table>("new", area)?; + + render_widgets(root.call_method("render", ())?, buf); + Ok::<_, mlua::Error>(()) + }; + if let Err(e) = f() { + error!("Failed to render the `Root` component:\n{e}"); + } - components::Header.render(chunks[0], buf); - components::Manager.render(chunks[1], buf); - components::Status.render(chunks[2], buf); components::Preview::new(self.cx).render(area, buf); if self.cx.tasks.visible { @@ -50,17 +54,3 @@ impl<'a> Widget for Root<'a> { } } } - -impl Root<'_> { - pub(super) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { - let evt = MouseEvent::cast(&LUA, event)?; - let comp: Table = LUA.globals().raw_get("Root")?; - - match event.kind { - MouseEventKind::Moved => comp.call_method("move", evt)?, - MouseEventKind::Drag(_) => comp.call_method("drag", evt)?, - _ => (), - } - Ok(()) - } -} diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index f8d966934..ea6f542f9 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -46,4 +46,4 @@ tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_lev uzers = "0.12.0" [target."cfg(windows)".dependencies] -clipboard-win = "5.3.1" +clipboard-win = "5.4.0" diff --git a/yazi-plugin/preset/compat.lua b/yazi-plugin/preset/compat.lua new file mode 100644 index 000000000..96cf8fbff --- /dev/null +++ b/yazi-plugin/preset/compat.lua @@ -0,0 +1,40 @@ +-- TODO: remove this after 0.3.0 release + +Manager = {} +Folder = {} +File = {} + +local b = false +function __yazi_check_and_warn_deprecated_api() + if b then + return + end + + local warn = function(name) + ya.notify { + title = "Deprecated API", + content = string.format( + [[The `%s` global variable has been removed in Yazi v0.3, please remove it from your `init.lua`. + +See https://github.com/sxyazi/yazi/pull/1257 for details.]], + name + ), + timeout = 20, + level = "warn", + } + end + + b = true + for _ in pairs(Manager) do + warn("Manager") + break + end + for _ in pairs(Folder) do + warn("Folder") + break + end + for _ in pairs(File) do + warn("File") + break + end +end diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index c79bcb264..77c07ea95 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -1,59 +1,58 @@ Current = { - area = ui.Rect.default, + _id = "current", } -function Current:empty(area) - local folder = Folder:by_kind(Folder.CURRENT) +function Current:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + _folder = tab.current, + }, { __index = self }) +end +function Current:empty() local line - if folder.files.filter then + if self._folder.files.filter then line = ui.Line("No filter results") else - line = ui.Line(folder.stage == "loading" and "Loading..." or "No items") + line = ui.Line(self._folder.stage == "loading" and "Loading..." or "No items") end return { - ui.Paragraph(area, { line }):align(ui.Paragraph.CENTER), + ui.Paragraph(self._area, { line }):align(ui.Paragraph.CENTER), } end -function Current:render(area) - self.area = area - - local files = Folder:by_kind(Folder.CURRENT).window +function Current:render() + local files = self._folder.window if #files == 0 then - return self:empty(area) + return self:empty() end - local items, markers = {}, {} - for i, f in ipairs(files) do - items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f)) - - -- Yanked/marked/selected files - local marker = File:marker(f) - if marker ~= 0 then - markers[#markers + 1] = { i, marker } - end + local items = {} + for _, f in ipairs(files) do + items[#items + 1] = ui.ListItem(Entity:render(f)):style(Entity:style(f)) end - return ya.flat { - ui.List(area, items), - Folder:linemode(area, files), - Folder:markers(area, markers), + return { + ui.List(self._area, items), + ui.Paragraph(self._area, Linemode:render(files)):align(ui.Paragraph.RIGHT), } end +-- Mouse events function Current:click(event, up) if up or event.is_middle then return end - local f = Folder:by_kind(Folder.CURRENT) - if event.y > #f.window or not f.hovered then + local f = self._folder + local y = event.y - self._area.y + 1 + if y > #f.window or not f.hovered then return end - ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx }) + ya.manager_emit("arrow", { y + f.offset - f.hovered.idx }) if event.is_right then ya.manager_emit("open", {}) end diff --git a/yazi-plugin/preset/components/entity.lua b/yazi-plugin/preset/components/entity.lua new file mode 100644 index 000000000..95d42b562 --- /dev/null +++ b/yazi-plugin/preset/components/entity.lua @@ -0,0 +1,109 @@ +Entity = { + _inc = 1000, +} + +function Entity:style(file) + local style = file:style() + if not file:is_hovered() then + return style + elseif file:in_preview() then + return style and style:patch(THEME.manager.preview_hovered) or THEME.manager.preview_hovered + else + return style and style:patch(THEME.manager.hovered) or THEME.manager.hovered + end +end + +function Entity:icon(file) + local icon = file:icon() + if not icon then + return ui.Line("") + elseif file:is_hovered() then + return ui.Line(" " .. icon.text .. " ") + else + return ui.Line(" " .. icon.text .. " "):style(icon.style) + end +end + +function Entity:prefix(file) + local prefix = file:prefix() or "" + return ui.Line(prefix ~= "" and prefix .. "/" or "") +end + +function Entity:highlights(file) + local name = file.name:gsub("\r", "?", 1) + local highlights = file:highlights() + if not highlights or #highlights == 0 then + return ui.Line(name) + end + + local spans, last = {}, 0 + for _, h in ipairs(highlights) do + if h[1] > last then + spans[#spans + 1] = ui.Span(name:sub(last + 1, h[1])) + end + spans[#spans + 1] = ui.Span(name:sub(h[1] + 1, h[2])):style(THEME.manager.find_keyword) + last = h[2] + end + if last < #name then + spans[#spans + 1] = ui.Span(name:sub(last + 1)) + end + return ui.Line(spans) +end + +function Entity:found(file) + if not file:is_hovered() then + return ui.Line {} + end + + local found = file:found() + if not found then + return ui.Line {} + end + + return ui.Line { + ui.Span(" "), + ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position), + } +end + +function Entity:symlink(file) + if not MANAGER.show_symlink then + return ui.Line {} + end + + local to = file.link_to + return ui.Line(to and { ui.Span(" -> " .. tostring(to)):italic() } or {}) +end + +function Entity:render(file) + local lines = {} + for _, child in ipairs(self._children) do + lines[#lines + 1] = child[1](self, file) + end + return ui.Line(lines) +end + +-- Initialize children +Entity._children = { + { Entity.icon, id = 1, order = 1000 }, + { Entity.prefix, id = 2, order = 2000 }, + { Entity.highlights, id = 3, order = 3000 }, + { Entity.found, id = 4, order = 4000 }, + { Entity.symlink, id = 5, order = 5000 }, +} + +function Entity:children_add(fn, order) + self._inc = self._inc + 1 + self._children[#self._children + 1] = { fn, id = self._inc, order = order } + table.sort(self._children, function(a, b) return a.order < b.order end) + return self._inc +end + +function Entity:children_remove(id) + for i, child in ipairs(self._children) do + if child.id == id then + table.remove(self._children, i) + break + end + end +end diff --git a/yazi-plugin/preset/components/file.lua b/yazi-plugin/preset/components/file.lua deleted file mode 100644 index 1e7cabd20..000000000 --- a/yazi-plugin/preset/components/file.lua +++ /dev/null @@ -1,99 +0,0 @@ -File = {} - -function File:icon(file) - local icon = file:icon() - if not icon then - return {} - elseif file:is_hovered() then - return { ui.Span(" " .. icon.text .. " ") } - else - return { ui.Span(" " .. icon.text .. " "):style(icon.style) } - end -end - -function File:prefix(file) - local prefix = file:prefix() or "" - return prefix == "" and {} or { ui.Span(prefix .. "/") } -end - -function File:highlights(file) - local name = file.name:gsub("\r", "?", 1) - local highlights = file:highlights() - if not highlights or #highlights == 0 then - return { ui.Span(name) } - end - - local spans, last = {}, 0 - for _, h in ipairs(highlights) do - if h[1] > last then - spans[#spans + 1] = ui.Span(name:sub(last + 1, h[1])) - end - spans[#spans + 1] = ui.Span(name:sub(h[1] + 1, h[2])):style(THEME.manager.find_keyword) - last = h[2] - end - if last < #name then - spans[#spans + 1] = ui.Span(name:sub(last + 1)) - end - return spans -end - -function File:found(file) - if not file:is_hovered() then - return {} - end - - local found = file:found() - if not found then - return {} - end - - return { - ui.Span(" "), - ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position), - } -end - -function File:symlink(file) - if not MANAGER.show_symlink then - return {} - end - - local to = file.link_to - return to and { ui.Span(" -> " .. tostring(to)):italic() } or {} -end - -function File:full(file) - return ya.flat { - self:icon(file), - self:prefix(file), - self:highlights(file), - self:found(file), - self:symlink(file), - } -end - -function File:style(file) - local style = file:style() - if not file:is_hovered() then - return style - elseif file:in_preview() then - return style and style:patch(THEME.manager.preview_hovered) or THEME.manager.preview_hovered - else - return style and style:patch(THEME.manager.hovered) or THEME.manager.hovered - end -end - -function File:marker(file) - local yanked = file:is_yanked() - if yanked ~= 0 then - return yanked -- 1: copied, 2: cut - end - - local marked = file:is_marked() - if marked == 1 then - return 3 -- 3: marked - elseif marked == 0 and file:is_selected() then - return 4 -- 4: selected - end - return 0 -end diff --git a/yazi-plugin/preset/components/folder.lua b/yazi-plugin/preset/components/folder.lua deleted file mode 100644 index b172d4199..000000000 --- a/yazi-plugin/preset/components/folder.lua +++ /dev/null @@ -1,92 +0,0 @@ -Folder = { - PARENT = 0, - CURRENT = 1, - PREVIEW = 2, -} - -function Folder:linemode(area, files) - local mode = cx.active.conf.linemode - if mode == "none" then - return {} - end - - local lines = {} - for _, f in ipairs(files) do - local spans = { ui.Span(" ") } - if mode == "size" then - local size = f:size() - spans[#spans + 1] = ui.Span(size and ya.readable_size(size) or "") - elseif mode == "mtime" then - local time = f.cha.modified - spans[#spans + 1] = ui.Span(time and os.date("%y-%m-%d %H:%M", time // 1) or "") - elseif mode == "permissions" then - spans[#spans + 1] = ui.Span(f.cha:permissions() or "") - elseif mode == "owner" then - spans[#spans + 1] = ya.user_name and ui.Span(ya.user_name(f.cha.uid) .. ":" .. ya.group_name(f.cha.gid)) - or ui.Span("") - end - - spans[#spans + 1] = ui.Span(" ") - lines[#lines + 1] = ui.Line(spans) - end - return ui.Paragraph(area, lines):align(ui.Paragraph.RIGHT) -end - -function Folder:markers(area, markers) - if #markers == 0 or area.w * area.h == 0 then - return {} - end - - local elements = {} - local append = function(last) - local y = math.min(area.y + last[1], area.y + area.h) - 1 - local bar = ui.Bar( - ui.Rect { - x = math.max(0, area.x - 1), - y = y, - w = 1, - h = math.min(1 + last[2] - last[1], area.y + area.h - y), - }, - ui.Bar.LEFT - ) - - if last[3] == 1 then - bar = bar:style(THEME.manager.marker_copied) - elseif last[3] == 2 then - bar = bar:style(THEME.manager.marker_cut) - elseif last[3] == 3 then - bar = bar:style(THEME.manager.marker_marked) - elseif last[3] == 4 then - bar = bar:style(THEME.manager.marker_selected) - end - elements[#elements + 1] = bar - end - - local last = { markers[1][1], markers[1][1], markers[1][2] } -- start, end, type - for _, m in ipairs(markers) do - if m[1] - last[2] > 1 or last[3] ~= m[2] then - append(last) - last = { m[1], m[1], m[2] } - else - last[2] = m[1] - end - end - - append(last) - return elements -end - -function Folder:by_kind(kind) - if kind == self.PARENT then - return cx.active.parent - elseif kind == self.CURRENT then - return cx.active.current - elseif kind == self.PREVIEW then - return cx.active.preview.folder - end -end - -function Folder:window(kind) - local folder = self:by_kind(kind) - return folder and folder.window -end diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index d973266d7..d08e2e0ed 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -1,15 +1,22 @@ Header = { - area = ui.Rect.default, + _id = "header", } +function Header:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + }, { __index = self }) +end + function Header:cwd(max) - local s = ya.readable_path(tostring(cx.active.current.cwd)) .. self:flags() + local s = ya.readable_path(tostring(self._tab.current.cwd)) .. self:flags() return ui.Span(ya.truncate(s, { max = max, rtl = true })):style(THEME.manager.cwd) end function Header:flags() - local cwd = cx.active.current.cwd - local filter = cx.active.current.files.filter + local cwd = self._tab.current.cwd + local filter = self._tab.current.files.filter local s = cwd.is_search and string.format(" (search: %s", cwd:frag()) or "" if not filter then @@ -26,7 +33,7 @@ function Header:count() local count, style if yanked == 0 then - count = #cx.active.selected + count = #self._tab.selected style = THEME.manager.count_selected elseif cx.yanked.is_cut then count = yanked @@ -67,17 +74,16 @@ function Header:tabs() return ui.Line(spans) end -function Header:render(area) - self.area = area - +function Header:render() local right = ui.Line { self:count(), self:tabs() } - local left = ui.Line { self:cwd(math.max(0, area.w - right:width())) } + local left = ui.Line { self:cwd(math.max(0, self._area.w - right:width())) } return { - ui.Paragraph(area, { left }), - ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT), + ui.Paragraph(self._area, { left }), + ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT), } end +-- Mouse events function Header:click(event, up) end function Header:scroll(event, step) end diff --git a/yazi-plugin/preset/components/linemode.lua b/yazi-plugin/preset/components/linemode.lua new file mode 100644 index 000000000..9c329aed2 --- /dev/null +++ b/yazi-plugin/preset/components/linemode.lua @@ -0,0 +1,77 @@ +Linemode = { + _inc = 1000, +} + +function Linemode:solo(file) + local mode = cx.active.conf.linemode + if mode == "none" or mode == "solo" then + return ui.Line("") + end + + if not self[mode] then + return ui.Line(" " .. mode .. " ") + end + + return ui.Line { + ui.Span(" "), + self[mode](self, file), + ui.Span(" "), + } +end + +function Linemode:size(file) + local size = file:size() + return ui.Line(size and ya.readable_size(size) or "") +end + +function Linemode:mtime(file) + local time = file.cha.modified + return ui.Line(time and os.date("%y-%m-%d %H:%M", time // 1) or "") +end + +function Linemode:permissions(file) return ui.Line(file.cha:permissions() or "") end + +function Linemode:owner(file) + if not ya.user_name then + return ui.Line("") + else + return ui.Line(ya.user_name(file.cha.uid) .. ":" .. ya.group_name(file.cha.gid)) + end +end + +function Linemode:render(files) + local lines = {} + for _, f in ipairs(files) do + lines[#lines + 1] = self:children_render(f) + end + return lines +end + +-- Initialize children +Linemode._children = { + { Linemode.solo, id = 1, order = 1000 }, +} + +function Linemode:children_add(fn, order) + self._inc = self._inc + 1 + self._children[#self._children + 1] = { fn, id = self._inc, order = order } + table.sort(self._children, function(a, b) return a.order < b.order end) + return self._inc +end + +function Linemode:children_remove(id) + for i, child in ipairs(self._children) do + if child.id == id then + table.remove(self._children, i) + break + end + end +end + +function Linemode:children_render(file) + local lines = {} + for _, child in ipairs(self._children) do + lines[#lines + 1] = child[1](self, file) + end + return ui.Line(lines) +end diff --git a/yazi-plugin/preset/components/manager.lua b/yazi-plugin/preset/components/manager.lua deleted file mode 100644 index 72633ada9..000000000 --- a/yazi-plugin/preset/components/manager.lua +++ /dev/null @@ -1,33 +0,0 @@ -Manager = { - area = ui.Rect.default, -} - -function Manager:layout(area) - self.area = area - - return ui.Layout() - :direction(ui.Layout.HORIZONTAL) - :constraints({ - ui.Constraint.Ratio(MANAGER.ratio.parent, MANAGER.ratio.all), - ui.Constraint.Ratio(MANAGER.ratio.current, MANAGER.ratio.all), - ui.Constraint.Ratio(MANAGER.ratio.preview, MANAGER.ratio.all), - }) - :split(area) -end - -function Manager:render(area) - local chunks = self:layout(area) - - return ya.flat { - -- Borders - ui.Bar(chunks[1], ui.Bar.RIGHT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style), - ui.Bar(chunks[3], ui.Bar.LEFT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style), - - -- Parent - Parent:render(chunks[1]:padding(ui.Padding.x(1))), - -- Current - Current:render(chunks[2]), - -- Preview - Preview:render(chunks[3]:padding(ui.Padding.x(1))), - } -end diff --git a/yazi-plugin/preset/components/marker.lua b/yazi-plugin/preset/components/marker.lua new file mode 100644 index 000000000..f75b370ba --- /dev/null +++ b/yazi-plugin/preset/components/marker.lua @@ -0,0 +1,71 @@ +Marker = { + _id = "marker", +} + +function Marker:new(area, folder) + return setmetatable({ + _area = area, + _folder = folder, + }, { __index = self }) +end + +function Marker:render() + if self._area.w * self._area.h == 0 then + return {} + elseif not self._folder or #self._folder.window == 0 then + return {} + end + + local elements = {} + local append = function(last) + if not last[3] then + return + end + + local y = math.min(self._area.y + last[1], self._area.y + self._area.h) - 1 + local rect = ui.Rect { + x = math.max(0, self._area.x - 1), + y = y, + w = 1, + h = math.min(1 + last[2] - last[1], self._area.y + self._area.h - y), + } + elements[#elements + 1] = ui.Bar(rect, ui.Bar.LEFT):style(last[3]) + end + + local last = { 0, 0, nil } -- start, end, style + for i, f in ipairs(self._folder.window) do + local style = self:style(f) + if i - last[2] > 1 or last[3] ~= style then + append(last) + last = { i, i, style } + else + last[2] = i + end + end + + append(last) + return elements +end + +function Marker:style(file) + local marked = file:is_marked() + if marked == 1 then + return THEME.manager.marker_marked + elseif marked == 0 and file:is_selected() then + return THEME.manager.marker_selected + end + + local yanked = file:is_yanked() + if yanked == 1 then + return THEME.manager.marker_copied + elseif yanked == 2 then + return THEME.manager.marker_cut + end +end + +-- Mouse events +function Marker:click(event, up) end + +function Marker:scroll(event, step) end + +function Marker:touch(event, step) end diff --git a/yazi-plugin/preset/components/parent.lua b/yazi-plugin/preset/components/parent.lua index a60ef3e8d..e9de37b55 100644 --- a/yazi-plugin/preset/components/parent.lua +++ b/yazi-plugin/preset/components/parent.lua @@ -1,40 +1,40 @@ Parent = { - area = ui.Rect.default, + _id = "parent", } -function Parent:render(area) - self.area = area +function Parent:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + _folder = tab.parent, + }, { __index = self }) +end - local folder = Folder:by_kind(Folder.PARENT) - if not folder then +function Parent:render() + if not self._folder then return {} end - local items, markers = {}, {} - for i, f in ipairs(folder.window) do - items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f)) - - -- Yanked/marked/selected files - local marker = File:marker(f) - if marker ~= 0 then - markers[#markers + 1] = { i, marker } - end + local items = {} + for _, f in ipairs(self._folder.window) do + items[#items + 1] = ui.ListItem(Entity:render(f)):style(Entity:style(f)) end - return ya.flat { - ui.List(area, items), - Folder:markers(area, markers), + return { + ui.List(self._area, items), } end +-- Mouse events function Parent:click(event, up) if up or not event.is_left then return end - local window = Folder:window(Folder.PARENT) or {} - if window[event.y] then - ya.manager_emit("reveal", { window[event.y].url }) + local y = event.y - self._area.y + 1 + local window = self._folder and self._folder.window or {} + if window[y] then + ya.manager_emit("reveal", { window[y].url }) else ya.manager_emit("leave", {}) end diff --git a/yazi-plugin/preset/components/preview.lua b/yazi-plugin/preset/components/preview.lua index 4bf5866dc..63b5c6d6a 100644 --- a/yazi-plugin/preset/components/preview.lua +++ b/yazi-plugin/preset/components/preview.lua @@ -1,20 +1,27 @@ Preview = { - area = ui.Rect.default, + _id = "preview", } -function Preview:render(area) - self.area = area - return {} +function Preview:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + _folder = tab.preview.folder, + }, { __index = self }) end +function Preview:render() return {} end + +-- Mouse events function Preview:click(event, up) if up or not event.is_left then return end - local window = Folder:window(Folder.PREVIEW) or {} - if window[event.y] then - ya.manager_emit("reveal", { window[event.y].url }) + local y = event.y - self._area.y + 1 + local window = self._folder and self._folder.window or {} + if window[y] then + ya.manager_emit("reveal", { window[y].url }) else ya.manager_emit("enter", {}) end diff --git a/yazi-plugin/preset/components/progress.lua b/yazi-plugin/preset/components/progress.lua index 079a445ed..4ed8da986 100644 --- a/yazi-plugin/preset/components/progress.lua +++ b/yazi-plugin/preset/components/progress.lua @@ -1,9 +1,9 @@ Progress = { - area = ui.Rect.default, + _area = ui.Rect.default, -- TODO: remove this } function Progress:render(area, offset) - self.area = ui.Rect { + self._area = ui.Rect { x = math.max(0, area.w - offset - 21), y = area.y, w = ya.clamp(0, area.w - offset - 1, 20), @@ -15,16 +15,13 @@ end -- Progress bars usually need frequent updates to report the latest task progress. -- We use `partial_render()` to partially render it when there is progress change, -- which has almost no cost compared to a full render by `render()`. --- --- However, at this time, we can only access `cx.tasks`. If you need certain data from the complete `cx`, --- just cache it to `self` during `render()`, and read it in `partial_render()` - this process is referred to as "composition". function Progress:partial_render() local progress = cx.tasks.progress if progress.total == 0 then - return { ui.Paragraph(self.area, {}) } + return { ui.Paragraph(self._area, {}) } end - local gauge = ui.Gauge(self.area) + local gauge = ui.Gauge(self._area) if progress.fail == 0 then gauge = gauge:gauge_style(THEME.status.progress_normal) else diff --git a/yazi-plugin/preset/components/rail.lua b/yazi-plugin/preset/components/rail.lua new file mode 100644 index 000000000..8385389ee --- /dev/null +++ b/yazi-plugin/preset/components/rail.lua @@ -0,0 +1,38 @@ +Rail = { + _id = "rail", + _area = ui.Rect.default, +} + +function Rail:new(chunks, tab) + return setmetatable({ + _chunks = chunks, + _tab = tab, + }, { __index = self }):build() +end + +function Rail:build() + self._base = { + ui.Bar(self._chunks[1], ui.Bar.RIGHT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style), + ui.Bar(self._chunks[3], ui.Bar.LEFT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style), + } + self._children = { + Marker:new(self._chunks[1], self._tab.parent), + Marker:new(self._chunks[2], self._tab.current), + } + return self +end + +function Rail:render() + local children = self._base or {} + for _, child in ipairs(self._children) do + children = ya.list_merge(children, ya.render_with(child)) + end + return children +end + +-- Mouse events +function Rail:click(event, up) end + +function Rail:scroll(event, step) end + +function Rail:touch(event, step) end diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua index cccc315e4..77f3eacad 100644 --- a/yazi-plugin/preset/components/root.lua +++ b/yazi-plugin/preset/components/root.lua @@ -1,7 +1,62 @@ Root = { - drag_start = ui.Rect.default, + _id = "root", + _drag_start = ui.Rect.default, } +function Root:new(area) + return setmetatable({ + _area = area, + }, { __index = self }) + :layout() + :build() +end + +function Root:layout() + self._chunks = ui.Layout() + :direction(ui.Layout.VERTICAL) + :constraints({ + ui.Constraint.Length(1), + ui.Constraint.Fill(1), + ui.Constraint.Length(1), + }) + :split(self._area) + return self +end + +function Root:build() + self._children = { + Header:new(self._chunks[1], cx.active), + Tab:new(self._chunks[2], cx.active), + Status:new(self._chunks[3], cx.active), + } + return self +end + +function Root:render() + local children = self._base or {} + for _, child in ipairs(self._children) do + children = ya.list_merge(children, ya.render_with(child)) + end + __yazi_check_and_warn_deprecated_api() -- TODO: remove this after 0.3.0 release + return children +end + +-- Mouse events +function Root:click(event, up) + local c = ya.child_at(ui.Position { x = event.x, y = event.y }, self._children) + return c and c:click(event, up) +end + +function Root:scroll(event, step) + local c = ya.child_at(ui.Position { x = event.x, y = event.y }, self._children) + return c and c:scroll(event, step) +end + +function Root:touch(event, step) + local c = ya.child_at(ui.Position { x = event.x, y = event.y }, self._children) + return c and c:touch(event, step) +end + function Root:move(event) end function Root:drag(event) end diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index 571b9469f..f82195410 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -1,11 +1,22 @@ Status = { - area = ui.Rect.default, + LEFT = 0, + RIGHT = 1, + + _id = "status", + _inc = 1000, } -function Status.style() - if cx.active.mode.is_select then +function Status:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + }, { __index = self }) +end + +function Status:style() + if self._tab.mode.is_select then return THEME.status.mode_select - elseif cx.active.mode.is_unset then + elseif self._tab.mode.is_unset then return THEME.status.mode_unset else return THEME.status.mode_normal @@ -13,12 +24,12 @@ function Status.style() end function Status:mode() - local mode = tostring(cx.active.mode):upper() + local mode = tostring(self._tab.mode):upper() if mode == "UNSET" then mode = "UN-SET" end - local style = self.style() + local style = self:style() return ui.Line { ui.Span(THEME.status.separator_open):fg(style.bg), ui.Span(" " .. mode .. " "):style(style), @@ -26,12 +37,12 @@ function Status:mode() end function Status:size() - local h = cx.active.current.hovered + local h = self._tab.current.hovered if not h then return ui.Line {} end - local style = self.style() + local style = self:style() return ui.Line { ui.Span(" " .. ya.readable_size(h:size() or h.cha.length) .. " "):fg(style.bg):bg(THEME.status.separator_style.bg), ui.Span(THEME.status.separator_close):fg(THEME.status.separator_style.fg), @@ -39,16 +50,16 @@ function Status:size() end function Status:name() - local h = cx.active.current.hovered + local h = self._tab.current.hovered if not h then - return ui.Span("") + return ui.Line {} end - return ui.Span(" " .. h.name) + return ui.Line(" " .. h.name) end function Status:permissions() - local h = cx.active.current.hovered + local h = self._tab.current.hovered if not h then return ui.Line {} end @@ -78,8 +89,8 @@ end function Status:percentage() local percent = 0 - local cursor = cx.active.current.cursor - local length = #cx.active.current.files + local cursor = self._tab.current.cursor + local length = #self._tab.current.files if cursor ~= 0 and length ~= 0 then percent = math.floor((cursor + 1) * 100 / length) end @@ -92,7 +103,7 @@ function Status:percentage() percent = string.format(" %3d%% ", percent) end - local style = self.style() + local style = self:style() return ui.Line { ui.Span(" " .. THEME.status.separator_open):fg(THEME.status.separator_style.fg), ui.Span(percent):fg(style.bg):bg(THEME.status.separator_style.bg), @@ -100,30 +111,69 @@ function Status:percentage() end function Status:position() - local cursor = cx.active.current.cursor - local length = #cx.active.current.files + local cursor = self._tab.current.cursor + local length = #self._tab.current.files - local style = self.style() + local style = self:style() return ui.Line { ui.Span(string.format(" %2d/%-2d ", cursor + 1, length)):style(style), ui.Span(THEME.status.separator_close):fg(style.bg), } end -function Status:render(area) - self.area = area - - local left = ui.Line { self:mode(), self:size(), self:name() } - local right = ui.Line { self:permissions(), self:percentage(), self:position() } +function Status:render() + local left = self:children_render(self.LEFT) + local right = self:children_render(self.RIGHT) return { - ui.Paragraph(area, { left }), - ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT), - table.unpack(Progress:render(area, right:width())), + ui.Paragraph(self._area, { left }), + ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT), + table.unpack(Progress:render(self._area, right:width())), } end +-- Mouse events function Status:click(event, up) end function Status:scroll(event, step) end function Status:touch(event, step) end + +-- Initialize children +Status._left = { + { Status.mode, id = 1, order = 1000 }, + { Status.size, id = 2, order = 2000 }, + { Status.name, id = 3, order = 3000 }, +} +Status._right = { + { Status.permissions, id = 4, order = 1000 }, + { Status.percentage, id = 5, order = 2000 }, + { Status.position, id = 6, order = 3000 }, +} + +function Status:children_add(fn, order, side) + self._inc = self._inc + 1 + local children = side == self.RIGHT and self._right or self._left + + children[#children + 1] = { fn, id = self._inc, order = order } + table.sort(children, function(a, b) return a.order < b.order end) + + return self._inc +end + +function Status:children_remove(id, side) + local children = side == self.RIGHT and self._right or self._left + for i, child in ipairs(children) do + if child.id == id then + table.remove(children, i) + break + end + end +end + +function Status:children_render(side) + local lines = {} + for _, child in ipairs(side == self.RIGHT and self._right or self._left) do + lines[#lines + 1] = child[1](self) + end + return ui.Line(lines) +end diff --git a/yazi-plugin/preset/components/tab.lua b/yazi-plugin/preset/components/tab.lua new file mode 100644 index 000000000..c5a3aab07 --- /dev/null +++ b/yazi-plugin/preset/components/tab.lua @@ -0,0 +1,58 @@ +Tab = { + _id = "tab", +} + +function Tab:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + }, { __index = self }) + :layout() + :build() +end + +function Tab:layout() + self._chunks = ui.Layout() + :direction(ui.Layout.HORIZONTAL) + :constraints({ + ui.Constraint.Ratio(MANAGER.ratio.parent, MANAGER.ratio.all), + ui.Constraint.Ratio(MANAGER.ratio.current, MANAGER.ratio.all), + ui.Constraint.Ratio(MANAGER.ratio.preview, MANAGER.ratio.all), + }) + :split(self._area) + return self +end + +function Tab:build() + self._children = { + Rail:new(self._chunks, self._tab), + Parent:new(self._chunks[1]:padding(ui.Padding.x(1)), self._tab), + Current:new(self._chunks[2], self._tab), + Preview:new(self._chunks[3]:padding(ui.Padding.x(1)), self._tab), + } + return self +end + +function Tab:render() + local children = self._base or {} + for _, child in ipairs(self._children) do + children = ya.list_merge(children, ya.render_with(child)) + end + return children +end + +-- Mouse events +function Tab:click(event, up) + local c = ya.child_at(ui.Position { x = event.x, y = event.y }, self._children) + return c and c:click(event, up) +end + +function Tab:scroll(event, step) + local c = ya.child_at(ui.Position { x = event.x, y = event.y }, self._children) + return c and c:scroll(event, step) +end + +function Tab:touch(event, step) + local c = ya.child_at(ui.Position { x = event.x, y = event.y }, self._children) + return c and c:touch(event, step) +end diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index bac2a4f2f..e853bc92f 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -1,7 +1,7 @@ local M = {} function M:peek() - local folder = Folder:by_kind(Folder.PREVIEW) + local folder = cx.active.preview.folder if not folder or folder.cwd ~= self.file.url then return end @@ -18,28 +18,19 @@ function M:peek() }) end - local items, markers = {}, {} - for i, f in ipairs(folder.window) do - items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f)) - - -- Yanked/marked/selected files - local marker = File:marker(f) - if marker ~= 0 then - markers[#markers + 1] = { i, marker } - end + local items = {} + for _, f in ipairs(folder.window) do + items[#items + 1] = ui.ListItem(Entity:render(f)):style(Entity:style(f)) end - ya.preview_widgets( - self, - ya.flat { - ui.List(self.area, items), - Folder:markers(self.area, markers), - } - ) + ya.preview_widgets(self, { + ui.List(self.area, items), + table.unpack(Marker:new(self.area, folder):render()), + }) end function M:seek(units) - local folder = Folder:by_kind(Folder.PREVIEW) + local folder = cx.active.preview.folder if folder and folder.cwd == self.file.url then local step = math.floor(units * self.area.h / 10) local bound = math.max(0, #folder.files - self.area.h) diff --git a/yazi-plugin/preset/plugins/noop.lua b/yazi-plugin/preset/plugins/noop.lua index ad93ad7f5..a6f709846 100644 --- a/yazi-plugin/preset/plugins/noop.lua +++ b/yazi-plugin/preset/plugins/noop.lua @@ -4,6 +4,8 @@ function M:peek() end function M:seek() end +function M:fetch() return 1 end + function M:preload() return 1 end return M diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 8713c0a15..85dad18ed 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -14,18 +14,11 @@ end function ya.round(x) return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) end -function ya.flat(t) - local r = {} - for _, v in ipairs(t) do - if type(v) == "table" then - for _, v2 in ipairs(ya.flat(v)) do - r[#r + 1] = v2 - end - else - r[#r + 1] = v - end +function ya.list_merge(a, b) + for _, v in ipairs(b) do + a[#a + 1] = v end - return r + return a end function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end @@ -50,3 +43,11 @@ function ya.readable_path(path) return path end end + +function ya.child_at(position, children) + for i = #children, 1, -1 do + if children[i]._area:contains(position) then + return children[i] + end + end +end diff --git a/yazi-plugin/src/bindings/mouse.rs b/yazi-plugin/src/bindings/mouse.rs index a3e7727f4..9f7e1948b 100644 --- a/yazi-plugin/src/bindings/mouse.rs +++ b/yazi-plugin/src/bindings/mouse.rs @@ -8,8 +8,8 @@ pub struct MouseEvent; impl MouseEvent { pub fn register(lua: &Lua) -> mlua::Result<()> { lua.register_userdata_type::(|reg| { - reg.add_field_method_get("x", |_, me| Ok(me.column as u32 + 1)); - reg.add_field_method_get("y", |_, me| Ok(me.row as u32 + 1)); + reg.add_field_method_get("x", |_, me| Ok(me.column)); + reg.add_field_method_get("y", |_, me| Ok(me.row)); reg.add_field_method_get("is_left", |_, me| { use crossterm::event::MouseEventKind as K; Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Left)) diff --git a/yazi-plugin/src/cast.rs b/yazi-plugin/src/cast.rs index b9429d19c..9acf8b94f 100644 --- a/yazi-plugin/src/cast.rs +++ b/yazi-plugin/src/cast.rs @@ -2,7 +2,7 @@ use mlua::AnyUserData; use crate::elements::Renderable; -pub fn cast_to_renderable(ud: AnyUserData) -> Option> { +pub fn cast_to_renderable(ud: &AnyUserData) -> Option> { if let Ok(c) = ud.take::() { Some(Box::new(c)) } else if let Ok(c) = ud.take::() { diff --git a/yazi-plugin/src/elements/constraint.rs b/yazi-plugin/src/elements/constraint.rs index 1f05df28d..69b25ff1b 100644 --- a/yazi-plugin/src/elements/constraint.rs +++ b/yazi-plugin/src/elements/constraint.rs @@ -7,6 +7,18 @@ impl Constraint { pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { let constraint = lua.create_table()?; + constraint.raw_set( + "Min", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Min(n))))?, + )?; + constraint.raw_set( + "Max", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Max(n))))?, + )?; + constraint.raw_set( + "Length", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Length(n))))?, + )?; constraint.raw_set( "Percentage", lua @@ -19,16 +31,8 @@ impl Constraint { })?, )?; constraint.raw_set( - "Length", - lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Length(n))))?, - )?; - constraint.raw_set( - "Max", - lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Max(n))))?, - )?; - constraint.raw_set( - "Min", - lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Min(n))))?, + "Fill", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Fill(n))))?, )?; ui.raw_set("Constraint", constraint) diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs index 9a4330ce2..c835174e6 100644 --- a/yazi-plugin/src/elements/elements.rs +++ b/yazi-plugin/src/elements/elements.rs @@ -1,4 +1,5 @@ -use mlua::{AnyUserData, Lua}; +use mlua::{AnyUserData, Lua, Table}; +use tracing::error; use crate::cast_to_renderable; @@ -21,6 +22,7 @@ pub fn pour(lua: &Lua) -> mlua::Result<()> { super::ListItem::install(lua, &ui)?; super::Padding::install(lua, &ui)?; super::Paragraph::install(lua, &ui)?; + super::Position::install(lua, &ui)?; super::Rect::install(lua, &ui)?; super::Span::install(lua, &ui)?; super::Style::install(lua, &ui)?; @@ -36,10 +38,16 @@ pub trait Renderable { fn clone_render(&self, buf: &mut ratatui::buffer::Buffer); } -pub fn render_widgets(widgets: Vec, buf: &mut ratatui::buffer::Buffer) { - for widget in widgets { - if let Some(w) = cast_to_renderable(widget) { - w.render(buf); +pub fn render_widgets(widgets: Table, buf: &mut ratatui::buffer::Buffer) { + for widget in widgets.sequence_values::() { + let Ok(widget) = widget else { + error!("Failed to convert to renderable UserData: {}", widget.unwrap_err()); + continue; + }; + + match cast_to_renderable(&widget) { + Some(w) => w.render(buf), + None => error!("Only the UserData of renderable element is accepted: {widget:#?}"), } } } diff --git a/yazi-plugin/src/elements/line.rs b/yazi-plugin/src/elements/line.rs index ca9261118..fcf74b243 100644 --- a/yazi-plugin/src/elements/line.rs +++ b/yazi-plugin/src/elements/line.rs @@ -23,7 +23,11 @@ impl TryFrom> for Line { if let Ok(span) = ud.take::() { spans.push(span.0); } else if let Ok(line) = ud.take::() { - spans.extend(line.0.spans.into_iter().collect::>()); + let style = line.0.style; + spans.extend(line.0.spans.into_iter().map(|mut span| { + span.style = style.patch(span.style); + span + })); } else { return Err("expected a table of Spans or Lines".into_lua_err()); } @@ -83,9 +87,9 @@ impl UserData for Line { { let mut me = ud.borrow_mut::()?; me.0.style = match value { - Value::Nil => me.0.style.patch(ratatui::style::Style::reset()), - Value::Table(tb) => me.0.style.patch(Style::try_from(tb)?.0), - Value::UserData(ud) => me.0.style.patch(ud.borrow::