diff --git a/Cargo.lock b/Cargo.lock index 38aa8e4..60959ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,6 +174,181 @@ dependencies = [ "libloading", ] +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "raw-window-handle", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.2", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.2", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.2", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -228,7 +403,29 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2 0.6.4", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", ] [[package]] @@ -466,6 +663,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.9.4", + "block2 0.6.2", + "libc", + "objc2 0.6.4", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -513,6 +722,33 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -552,6 +788,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "faster-hex" version = "0.9.0" @@ -625,6 +882,72 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + [[package]] name = "gethostname" version = "1.0.2" @@ -1224,6 +1547,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -1551,6 +1880,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metal" version = "0.32.0" @@ -1689,6 +2027,15 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-app-kit" version = "0.2.2" @@ -1696,15 +2043,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "libc", - "objc2", + "objc2 0.5.2", "objc2-core-data", "objc2-core-image", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-quartz-core", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.9.4", + "block2 0.6.2", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -1712,10 +2071,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.4", - "block2", - "objc2", + "block2 0.5.1", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1724,9 +2083,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1736,9 +2095,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.4", - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.9.4", + "dispatch2", + "objc2 0.6.4", ] [[package]] @@ -1747,9 +2117,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -1759,10 +2129,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", - "objc2", + "block2 0.5.1", + "objc2 0.5.2", "objc2-contacts", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1778,10 +2148,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "dispatch", "libc", - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.4", + "objc2-core-foundation", ] [[package]] @@ -1790,10 +2171,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1803,9 +2184,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.4", - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1815,9 +2196,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.4", - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -1827,8 +2208,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1838,13 +2219,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.4", - "block2", - "objc2", + "block2 0.5.1", + "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-image", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-link-presentation", "objc2-quartz-core", "objc2-symbols", @@ -1858,9 +2239,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1870,10 +2251,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.4", - "block2", - "objc2", + "block2 0.5.1", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1906,6 +2287,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "owned_ttf_parser" version = "0.25.1" @@ -1915,6 +2306,12 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.4" @@ -1976,6 +2373,17 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -2026,6 +2434,15 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "presser" version = "0.3.1" @@ -2090,6 +2507,35 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] + [[package]] name = "range-alloc" version = "0.1.4" @@ -2155,6 +2601,30 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "rfd" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +dependencies = [ + "ashpd", + "block2 0.6.2", + "dispatch2", + "js-sys", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2271,6 +2741,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha1_smol" version = "1.0.1" @@ -2283,6 +2764,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.8" @@ -2548,14 +3039,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] [[package]] name = "tree-sitter" @@ -2603,6 +3109,17 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + [[package]] name = "unicode-bom" version = "2.0.3" @@ -2646,8 +3163,15 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -2660,6 +3184,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + [[package]] name = "version_check" version = "0.9.5" @@ -2905,6 +3440,7 @@ dependencies = [ "env_logger", "gix", "pollster", + "rfd", "tree-sitter", "tree-sitter-rust", "tree-sitter-toml-ng", @@ -3387,7 +3923,7 @@ dependencies = [ "android-activity", "atomic-waker", "bitflags 2.9.4", - "block2", + "block2 0.5.1", "bytemuck", "calloop", "cfg_aliases", @@ -3400,9 +3936,9 @@ dependencies = [ "libc", "memmap2", "ndk", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", "percent-encoding", @@ -3545,6 +4081,67 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix 1.1.2", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 0.7.13", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +dependencies = [ + "serde", + "winnow 0.7.13", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.8.27" @@ -3624,3 +4221,44 @@ name = "zmij" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" + +[[package]] +name = "zvariant" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +dependencies = [ + "endi", + "enumflags2", + "serde", + "url", + "winnow 0.7.13", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "winnow 0.7.13", +] diff --git a/Cargo.toml b/Cargo.toml index 2e99538..100392f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ gix = { version = "0.67.0", default-features = false } tree-sitter = "0.25.10" tree-sitter-rust = "0.24.0" tree-sitter-toml-ng = "0.7.0" +rfd = "0.15" diff --git a/src/app.rs b/src/app.rs index fda8b70..63488b8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -63,6 +63,7 @@ enum InputMode { CommitBody, RepoPicker, DiscardConfirm, + BranchSwitcher, } struct State { @@ -115,6 +116,13 @@ struct State { repo_picker_index: usize, repo_picker_scroll: usize, pending_discard_path: Option, + + // ── Branch switcher ─────────────────────────────────────── + branch_list: Vec, + branch_current: String, + branch_picker_index: usize, + branch_picker_scroll: usize, + mouse_pos: PhysicalPosition, window_controls: Vec, toolbar_buttons: Vec, @@ -443,6 +451,10 @@ impl State { repo_picker_index: 0, repo_picker_scroll: 0, pending_discard_path: None, + branch_list: Vec::new(), + branch_current: String::new(), + branch_picker_index: 0, + branch_picker_scroll: 0, mouse_pos: PhysicalPosition::new(0.0, 0.0), window_controls: Vec::new(), toolbar_buttons: Vec::new(), @@ -634,6 +646,7 @@ impl State { self.commit_summary.clear(); self.commit_body.clear(); self.pending_discard_path = None; + self.branch_list.clear(); self.set_selection_status(); } @@ -889,6 +902,141 @@ impl State { } } + // ── Folder browser (native OS dialog) ───────────────────────── + + fn open_folder_dialog(&mut self) -> anyhow::Result<()> { + let start_dir = self.git.repo_root().parent() + .unwrap_or_else(|| std::path::Path::new("/")) + .to_path_buf(); + + let picked = rfd::FileDialog::new() + .set_title("Open Git Repository") + .set_directory(&start_dir) + .pick_folder(); + + let Some(path) = picked else { + return Ok(()); // user cancelled + }; + + let git = GitModel::open_at(&path) + .with_context(|| format!("{} is not a git repository", path.display()))?; + self.git = git; + let _ = repo_store::remember_repo(self.git.repo_root()); + self.refresh_recent_repos(); + self.refresh_document_from_git()?; + self.set_status( + StatusKind::Success, + format!("Opened repository {}", self.git.repo_root().display()), + ); + Ok(()) + } + + // ── Branch switcher ───────────────────────────────────────────── + + fn prompt_branch_switcher(&mut self) { + match self.git.list_branches() { + Ok(branches) => { + self.branch_current = self.git.branch().to_string(); + self.branch_picker_index = branches + .iter() + .position(|b| b == &self.branch_current) + .unwrap_or(0); + self.branch_list = branches; + self.branch_picker_scroll = 0; + self.set_branch_picker_index(self.branch_picker_index); + self.input_mode = InputMode::BranchSwitcher; + self.update_branch_picker_prompt(); + } + Err(err) => { + self.set_status(StatusKind::Error, format!("Failed to list branches: {err}")); + } + } + } + + fn update_branch_picker_prompt(&mut self) { + let message = if let Some(name) = self.branch_list.get(self.branch_picker_index) { + format!( + "Branch: {} [Enter] checkout [Esc] cancel", + compact_status_message(name, 48) + ) + } else { + String::from("Branch switcher: use arrows, Enter to checkout, Esc to cancel") + }; + self.set_status(StatusKind::Prompt, message); + } + + fn set_branch_picker_index(&mut self, index: usize) { + if self.branch_list.is_empty() { + self.branch_picker_index = 0; + self.branch_picker_scroll = 0; + return; + } + let last = self.branch_list.len() - 1; + self.branch_picker_index = index.min(last); + let visible = 6usize; + if self.branch_picker_index < self.branch_picker_scroll { + self.branch_picker_scroll = self.branch_picker_index; + } else if self.branch_picker_index >= self.branch_picker_scroll + visible { + self.branch_picker_scroll = self.branch_picker_index + 1 - visible; + } + } + + fn checkout_selected_branch(&mut self) -> anyhow::Result<()> { + let Some(name) = self.branch_list.get(self.branch_picker_index).cloned() else { + anyhow::bail!("no branch selected"); + }; + if name == self.branch_current { + self.cancel_input_mode(); + return Ok(()); + } + self.git.checkout_branch(&name)?; + self.refresh_document_from_git()?; + self.input_mode = InputMode::Normal; + self.branch_list.clear(); + self.set_status(StatusKind::Success, format!("Switched to branch {name}")); + Ok(()) + } + + fn handle_branch_switcher_input(&mut self, key: &Key) -> anyhow::Result { + match key { + Key::Named(NamedKey::Escape) => { + self.cancel_input_mode(); + Ok(true) + } + Key::Named(NamedKey::Enter) => { + self.checkout_selected_branch()?; + Ok(true) + } + Key::Named(NamedKey::ArrowUp) => { + if self.branch_picker_index > 0 { + self.set_branch_picker_index(self.branch_picker_index - 1); + self.update_branch_picker_prompt(); + } + Ok(true) + } + Key::Named(NamedKey::ArrowDown) => { + if self.branch_picker_index + 1 < self.branch_list.len() { + self.set_branch_picker_index(self.branch_picker_index + 1); + self.update_branch_picker_prompt(); + } + Ok(true) + } + Key::Named(NamedKey::Home) => { + self.set_branch_picker_index(0); + self.update_branch_picker_prompt(); + Ok(true) + } + Key::Named(NamedKey::End) => { + if !self.branch_list.is_empty() { + self.set_branch_picker_index(self.branch_list.len() - 1); + self.update_branch_picker_prompt(); + } + Ok(true) + } + _ => Ok(false), + } + } + fn execute_action(&mut self, label: &str, action: F) -> bool where F: FnOnce(&mut Self) -> anyhow::Result<()>, @@ -912,6 +1060,14 @@ impl State { self.prompt_repo_picker(); Ok(true) } + ToolbarAction::Browse => { + self.open_folder_dialog()?; + Ok(true) + } + ToolbarAction::BranchSwitch => { + self.prompt_branch_switcher(); + Ok(true) + } ToolbarAction::Commit => { self.prompt_commit_message(); Ok(true) @@ -1070,6 +1226,9 @@ impl State { InputMode::DiscardConfirm => { self.build_discard_overlay_geometry(text_vertices, rect_instances) } + InputMode::BranchSwitcher => { + self.build_branch_switcher_overlay_geometry(text_vertices, rect_instances) + } InputMode::Normal => Ok(()), } } @@ -1365,6 +1524,115 @@ impl State { Ok(()) } + fn build_branch_switcher_overlay_geometry( + &mut self, + text_vertices: &mut Vec, + rect_instances: &mut Vec, + ) -> anyhow::Result<()> { + let visible_start = self.branch_picker_scroll.min(self.branch_list.len()); + let visible_end = (visible_start + 6).min(self.branch_list.len()); + let visible_count = visible_end.saturating_sub(visible_start); + let panel_h = self.ui(92.0) + visible_count as f32 * (self.line_height + self.ui(6.0)); + let panel = self.modal_panel_rect(panel_h); + push_styled_rect( + rect_instances, + panel, + theme::MODAL_BG_TOP, + theme::MODAL_BG_BOTTOM, + theme::MODAL_BORDER, + [0.0, 0.0, 0.0, 0.28], + self.ui(12.0), + 1.0, + 1.0, + self.ui(16.0), + [0.0, self.ui(4.0)], + self.ui(2.0), + ); + + let mut x = panel[0] + self.ui(16.0); + let mut y = panel[1] + self.ui(18.0); + self.append_text_run( + text_vertices, + &mut x, + y + self.ascent, + "Switch branch", + [0.92, 0.96, 1.0, 1.0], + )?; + + y += self.line_height * 1.4; + let row_x = panel[0] + self.ui(14.0); + let row_w = panel[2] - self.ui(28.0); + let visible_branches: Vec = self.branch_list[visible_start..visible_end].to_vec(); + for (offset, branch) in visible_branches.iter().enumerate() { + let idx = visible_start + offset; + let row_y = y + offset as f32 * (self.line_height + self.ui(6.0)); + let selected = idx == self.branch_picker_index; + let is_current = branch == &self.branch_current; + let (fill_top, fill_bottom, stroke) = if selected { + ( + [0.25, 0.33, 0.50, 1.0], + [0.18, 0.24, 0.38, 1.0], + [0.52, 0.68, 0.98, 0.70], + ) + } else { + ( + [0.16, 0.18, 0.26, 1.0], + [0.12, 0.14, 0.20, 1.0], + [0.30, 0.36, 0.48, 0.40], + ) + }; + push_styled_rect( + rect_instances, + [row_x, row_y, row_w, self.line_height + self.ui(4.0)], + fill_top, + fill_bottom, + stroke, + [0.0, 0.0, 0.0, 0.0], + self.ui(8.0), + 1.0, + 1.0, + 0.0, + [0.0, 0.0], + 0.0, + ); + + let mut tx = row_x + self.ui(10.0); + let mut label = branch.clone(); + if is_current { + label.push_str(" (current)"); + } + let text_color = if is_current && !selected { + theme::BRANCH_CURRENT_BADGE + } else if selected { + [1.0, 1.0, 1.0, 1.0] + } else { + [0.86, 0.90, 0.96, 1.0] + }; + self.append_text_run( + text_vertices, + &mut tx, + row_y + self.ascent + 2.0, + &compact_status_message( + &label, + ((row_w - self.ui(20.0)) / self.cell_width).max(1.0) as usize, + ), + text_color, + )?; + } + + let mut hint_x = panel[0] + self.ui(16.0); + let hint_y = panel[1] + panel[3] - self.line_height + self.ui(4.0); + self.append_text_run( + text_vertices, + &mut hint_x, + hint_y + self.ascent, + "Enter checkout Esc cancel Up/Down move", + [0.76, 0.82, 0.92, 1.0], + )?; + + Ok(()) + } + fn refresh_document_from_git(&mut self) -> anyhow::Result<()> { let (file_doc, meta, diff_doc) = self.git.build_split_documents()?; let (file_line_to_index, file_index_to_line) = @@ -1981,6 +2249,18 @@ impl State { group: ToolbarGroup::App, style: gray_btn, }, + ButtonConfig { + label: String::from("b browse"), + action: ToolbarAction::Browse, + group: ToolbarGroup::App, + style: gray_btn, + }, + ButtonConfig { + label: String::from("B branch"), + action: ToolbarAction::BranchSwitch, + group: ToolbarGroup::App, + style: gray_btn, + }, ButtonConfig { label: String::from("r refresh"), action: ToolbarAction::Refresh, @@ -2707,6 +2987,9 @@ impl State { if raw == "X" { return self.handle_toolbar_action(ToolbarAction::Discard); } + if raw == "B" { + return self.handle_toolbar_action(ToolbarAction::BranchSwitch); + } let c = raw.to_ascii_lowercase(); match c.as_str() { @@ -2721,6 +3004,7 @@ impl State { Ok(true) } "o" => self.handle_toolbar_action(ToolbarAction::RepoSwitch), + "b" => self.handle_toolbar_action(ToolbarAction::Browse), "j" => match self.focus_pane { FocusPane::Files => self.move_selection_and_refresh(1), FocusPane::Diff => { @@ -2941,6 +3225,9 @@ impl ApplicationHandler for App { InputMode::DiscardConfirm => { st.handle_discard_confirm_input(&event.logical_key) } + InputMode::BranchSwitcher => { + st.handle_branch_switcher_input(&event.logical_key) + } InputMode::Normal => Ok(false), } } else { @@ -2973,6 +3260,7 @@ impl ApplicationHandler for App { } InputMode::RepoPicker => "Repo switch failed", InputMode::DiscardConfirm => "Discard failed", + InputMode::BranchSwitcher => "Branch switch failed", InputMode::Normal => "Input handling failed", }; st.set_status(StatusKind::Error, format!("{label}: {err}")); diff --git a/src/git_model.rs b/src/git_model.rs index d3360a4..d89e5e9 100644 --- a/src/git_model.rs +++ b/src/git_model.rs @@ -117,6 +117,10 @@ enum GitCommand<'a> { branch: Option<&'a str>, set_upstream: bool, }, + ListBranches, + CheckoutBranch { + name: &'a str, + }, } impl<'a> GitCommand<'a> { @@ -211,6 +215,8 @@ impl<'a> GitCommand<'a> { } desc } + Self::ListBranches => String::from("git branch --list"), + Self::CheckoutBranch { name } => format!("git checkout {}", name), } } @@ -316,6 +322,12 @@ impl<'a> GitCommand<'a> { command.arg(branch); } } + Self::ListBranches => { + command.args(["branch", "--list"]); + } + Self::CheckoutBranch { name } => { + command.args(["checkout", name]); + } } } } @@ -436,6 +448,23 @@ impl GitModel { self.refresh() } + pub fn list_branches(&self) -> anyhow::Result> { + let output = self.run_git(GitCommand::ListBranches)?; + let mut branches = Vec::new(); + for line in output.lines() { + let name = line.trim().trim_start_matches("* ").to_string(); + if !name.is_empty() { + branches.push(name); + } + } + Ok(branches) + } + + pub fn checkout_branch(&mut self, name: &str) -> anyhow::Result<()> { + self.run_git(GitCommand::CheckoutBranch { name })?; + self.refresh() + } + pub fn unstage_all(&mut self) -> anyhow::Result<()> { self.run_git(GitCommand::UnstageAll)?; self.refresh() diff --git a/src/models.rs b/src/models.rs index 58a5bc7..b3b7bcd 100644 --- a/src/models.rs +++ b/src/models.rs @@ -100,6 +100,8 @@ impl LineStyle { #[derive(Clone, Copy, Debug)] pub enum ToolbarAction { RepoSwitch, + Browse, + BranchSwitch, Commit, Fetch, Pull, diff --git a/src/theme.rs b/src/theme.rs index 8662b55..7672566 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -112,6 +112,10 @@ pub const MODAL_DANGER_BG_TOP: [f32; 4] = [0.20, 0.12, 0.12, 1.0]; pub const MODAL_DANGER_BG_BOTTOM: [f32; 4] = [0.14, 0.08, 0.08, 1.0]; pub const MODAL_DANGER_BORDER: [f32; 4] = [0.80, 0.35, 0.35, 0.60]; +// ── Branch switcher ───────────────────────────────────────────── +/// Current branch indicator +pub const BRANCH_CURRENT_BADGE: [f32; 4] = [0.40, 0.82, 0.52, 1.0]; + // ── Status bar per-kind ────────────────────────────────────────── pub const STATUS_NEUTRAL: ([f32; 4], [f32; 4], [f32; 4], [f32; 4]) = ( [0.12, 0.15, 0.22, 0.88],