diff --git a/Cargo.lock b/Cargo.lock index cb736bf..ddc50e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -158,6 +158,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -300,6 +309,21 @@ dependencies = [ "rayon", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bit_field" version = "0.10.3" @@ -380,7 +404,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -424,7 +448,7 @@ dependencies = [ "num_cpus", "objc2-foundation", "objc2-metal", - "rand", + "rand 0.9.2", "rand_distr", "rayon", "safetensors 0.7.0", @@ -515,6 +539,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.44" @@ -559,7 +589,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -608,6 +638,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -735,6 +774,49 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags 2.11.0", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix 0.38.44", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.11.0", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix 1.1.4", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.4" @@ -751,6 +833,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf", +] + [[package]] name = "cudarc" version = "0.17.8" @@ -778,8 +870,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -793,7 +895,20 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", ] [[package]] @@ -802,9 +917,20 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.117", ] [[package]] @@ -816,6 +942,12 @@ dependencies = [ "serde", ] +[[package]] +name = "deltae" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + [[package]] name = "der" version = "0.7.10" @@ -850,10 +982,10 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -863,7 +995,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", ] [[package]] @@ -945,7 +1099,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1003,7 +1157,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1023,7 +1177,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1051,6 +1205,15 @@ dependencies = [ "cc", ] +[[package]] +name = "euclid" +version = "0.22.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" +dependencies = [ + "num-traits", +] + [[package]] name = "exr" version = "1.74.0" @@ -1066,6 +1229,16 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1089,7 +1262,7 @@ checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1101,6 +1274,17 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + [[package]] name = "filetime" version = "0.2.26" @@ -1119,6 +1303,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "finl_unicode" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.1.9" @@ -1138,7 +1334,7 @@ dependencies = [ "cudarc 0.19.3", "half", "num-traits", - "rand", + "rand 0.9.2", "rand_distr", ] @@ -1196,7 +1392,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1276,7 +1472,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1601,7 +1797,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1649,7 +1845,7 @@ dependencies = [ "cfg-if", "crunchy", "num-traits", - "rand", + "rand 0.9.2", "rand_distr", "zerocopy", ] @@ -1688,6 +1884,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 = "hf-hub" version = "0.5.0" @@ -1702,7 +1904,7 @@ dependencies = [ "log", "native-tls", "num_cpus", - "rand", + "rand 0.9.2", "reqwest", "serde", "serde_json", @@ -2040,6 +2242,28 @@ dependencies = [ "web-time", ] +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +dependencies = [ + "darling 0.23.0", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "intel-mkl-src" version = "0.8.1" @@ -2070,7 +2294,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2130,6 +2354,23 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kasuari" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + +[[package]] +name = "lab" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" + [[package]] name = "lazy_static" version = "1.5.0" @@ -2202,6 +2443,21 @@ dependencies = [ "redox_syscall 0.7.2", ] +[[package]] +name = "line-clipping" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -2244,6 +2500,25 @@ dependencies = [ "imgref", ] +[[package]] +name = "lru" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix", + "winapi", +] + [[package]] name = "macro_rules_attribute" version = "0.2.2" @@ -2310,6 +2585,21 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[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.29.0" @@ -2354,6 +2644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] @@ -2377,7 +2668,7 @@ checksum = "e4db6d5580af57bf992f59068d4ea26fd518574ff48d7639b255a36f9de6e7e9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2413,6 +2704,19 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "7.1.3" @@ -2495,7 +2799,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2549,6 +2853,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -2621,8 +2934,8 @@ dependencies = [ "getset", "serde", "serde_json", - "strum", - "strum_macros", + "strum 0.26.3", + "strum_macros 0.26.4", "thiserror 1.0.69", ] @@ -2710,7 +3023,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2737,6 +3050,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -2787,6 +3109,101 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -2861,7 +3278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -2883,7 +3300,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2911,7 +3328,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2993,6 +3410,15 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.2" @@ -3000,7 +3426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -3010,9 +3436,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + [[package]] name = "rand_core" version = "0.9.5" @@ -3029,7 +3461,92 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", - "rand", + "rand 0.9.2", +] + +[[package]] +name = "ratatui" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termwiz", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.11.0", + "compact_str", + "hashbrown 0.16.1", + "indoc", + "itertools", + "kasuari", + "lru", + "strum 0.27.2", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm 0.29.0", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.16.1", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "strum 0.27.2", + "time", + "unicode-segmentation", + "unicode-width", ] [[package]] @@ -3059,7 +3576,7 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand", + "rand 0.9.2", "rand_chacha", "simd_helpers", "thiserror 2.0.18", @@ -3260,6 +3777,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.52.0", +] + [[package]] name = "rustix" version = "1.1.4" @@ -3269,7 +3808,7 @@ dependencies = [ "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.12.1", "windows-sys 0.61.2", ] @@ -3427,7 +3966,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3501,6 +4040,27 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -3526,6 +4086,12 @@ dependencies = [ "quote", ] +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + [[package]] name = "slab" version = "0.4.12" @@ -3595,6 +4161,15 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + [[package]] name = "strum_macros" version = "0.26.4" @@ -3605,7 +4180,19 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -3614,6 +4201,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.117" @@ -3642,7 +4240,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3700,10 +4298,73 @@ dependencies = [ "fastrand", "getrandom 0.4.2", "once_cell", - "rustix", + "rustix 1.1.4", "windows-sys 0.61.2", ] +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom 7.1.3", + "phf", + "phf_codegen", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bitflags 2.11.0", + "fancy-regex", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix", + "num-derive", + "num-traits", + "ordered-float", + "pest", + "pest_derive", + "phf", + "sha2", + "signal-hook", + "siphasher", + "terminfo", + "termios", + "thiserror 1.0.69", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-blob-leases", + "wezterm-color-types", + "wezterm-dynamic", + "wezterm-input-types", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3730,7 +4391,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3741,7 +4402,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3775,7 +4436,9 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde_core", "time-core", @@ -3828,7 +4491,7 @@ dependencies = [ "monostate", "onig", "paste", - "rand", + "rand 0.9.2", "rayon", "rayon-cond", "regex", @@ -3867,7 +4530,7 @@ checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4022,7 +4685,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4082,6 +4745,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "ug" version = "0.5.0" @@ -4151,6 +4820,17 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-truncate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "unicode-width" version = "0.2.2" @@ -4271,6 +4951,7 @@ version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ + "atomic", "getrandom 0.4.2", "js-sys", "wasm-bindgen", @@ -4307,8 +4988,10 @@ dependencies = [ "axum", "base64 0.22.1", "clap", + "crossterm 0.28.1", "dirs 5.0.1", "image", + "ratatui", "serde", "serde_json", "tokio", @@ -4323,7 +5006,7 @@ dependencies = [ [[package]] name = "vecbox-core" version = "0.1.0" -source = "git+https://github.com/alpaim/vecbox-core.git?tag=v0.1.0#1ac97a68a3c0ac035fd19809b13a55f4093f95da" +source = "git+https://github.com/alpaim/vecbox-core.git?tag=v0.1.1#6fb3025e4ff20f1a893c413b7dff7fe293e3fda4" dependencies = [ "accelerate-src", "anyhow", @@ -4347,6 +5030,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -4436,7 +5128,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -4549,6 +5241,78 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-blob-leases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" +dependencies = [ + "getrandom 0.3.4", + "mac_address", + "sha2", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "wezterm-color-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" +dependencies = [ + "log", + "ordered-float", + "strsim", + "thiserror 1.0.69", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wezterm-input-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" +dependencies = [ + "bitflags 1.3.2", + "euclid", + "lazy_static", + "serde", + "wezterm-dynamic", +] + [[package]] name = "winapi" version = "0.3.9" @@ -4601,7 +5365,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4612,7 +5376,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4911,7 +5675,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4927,7 +5691,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4982,7 +5746,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix", + "rustix 1.1.4", ] [[package]] @@ -5022,7 +5786,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -5034,7 +5798,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -5055,7 +5819,7 @@ checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -5075,7 +5839,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -5115,7 +5879,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2c371f4..92af2e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ accelerate = ["vecbox-core/accelerate"] mkl = ["vecbox-core/mkl"] [dependencies] -vecbox-core = { git = "https://github.com/alpaim/vecbox-core.git", tag = "v0.1.0" } +vecbox-core = { git = "https://github.com/alpaim/vecbox-core.git", tag = "v0.1.1" } anyhow = { version = "1" } @@ -28,6 +28,9 @@ dirs = "5" tokio = { version = "1.50.0", features = ["full"] } +ratatui = "0.30.0" +crossterm = "0.28" + axum = "0.8.8" tower = "0.5.3" diff --git a/src/cli.rs b/src/cli.rs index 18c9445..0b63675 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -27,6 +27,8 @@ pub enum Commands { ImageEmbedding(ImageEmbeddingArgs), #[command(name = "server")] Server(ServerArgs), + #[command(name = "tui")] + Tui, } #[derive(clap::Args)] diff --git a/src/main.rs b/src/main.rs index db4dd64..8e5504a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod api; mod cli; mod logging; mod metrics; +mod tui; use vecbox_core::{download, models, utils}; @@ -22,6 +23,9 @@ async fn main() -> anyhow::Result<()> { Commands::Server(args) => { run_server(args).await?; } + Commands::Tui => { + tui::run_wizard()?; + } _ => { run_embedding_command(&cli)?; } @@ -35,6 +39,7 @@ fn run_embedding_command(cli: &Cli) -> anyhow::Result<()> { Commands::TextEmbedding(args) => (&args.repo, &args.quant, args.metrics), Commands::ImageEmbedding(args) => (&args.repo, &args.quant, args.metrics), Commands::Server(_) => unreachable!(), + Commands::Tui => unreachable!(), }; info!("Downloading model from Hugging Face..."); @@ -152,6 +157,7 @@ fn run_embedding_command(cli: &Cli) -> anyhow::Result<()> { } } Commands::Server(_) => unreachable!(), + Commands::Tui => unreachable!(), } Ok(()) diff --git a/src/tui.rs b/src/tui.rs new file mode 100644 index 0000000..429d685 --- /dev/null +++ b/src/tui.rs @@ -0,0 +1,537 @@ +use crossterm::{ + event::{ + self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyEventKind, + KeyModifiers, + }, + execute, + terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, +}; +use ratatui::{ + Frame, Terminal, + backend::CrosstermBackend, + layout::{Alignment, Constraint, Layout}, + style::{Color, Modifier, Style}, + text::Line, + widgets::{Block, Borders, List, ListItem, Paragraph}, +}; +use std::io; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::time::Duration; + +use crate::api; +use vecbox_core::{download, utils}; + +const MODELS: &[&str] = &[ + "alpaim/Qwen3-VL-Embedding-2B-GGUF-vecBox", + "< Enter custom repo >", +]; + +#[derive(Clone)] +pub struct AppState { + pub repo: String, + pub quant: String, + pub host: String, + pub port: u16, + pub max_pixels: Option, + pub min_pixels: Option, + pub server_running: Arc, +} + +impl Default for AppState { + fn default() -> Self { + Self { + repo: "alpaim/Qwen3-VL-Embedding-2B-GGUF-vecBox".to_string(), + quant: "Q4_K_M".to_string(), + host: "0.0.0.0".to_string(), + port: 8080, + max_pixels: None, + min_pixels: None, + server_running: Arc::new(AtomicBool::new(false)), + } + } +} + +pub enum Step { + Welcome, + ModelSelection, + QuantSelection, + ServerConfig, + Summary, + Running, +} + +pub struct State { + pub step: Step, + pub app: AppState, + pub selected: usize, + pub quants: Vec, + pub custom_repo: String, + pub host: String, + pub port: String, + pub max_pixels: String, + pub min_pixels: String, + pub field: usize, +} + +impl Default for State { + fn default() -> Self { + Self { + step: Step::Welcome, + app: AppState::default(), + selected: 0, + quants: Vec::new(), + custom_repo: String::new(), + host: "0.0.0.0".to_string(), + port: "8080".to_string(), + max_pixels: String::new(), + min_pixels: String::new(), + field: 0, + } + } +} + +pub fn run_wizard() -> anyhow::Result<()> { + enable_raw_mode()?; + let mut stdout = io::stdout(); + execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + + let mut state = State::default(); + let res = run_loop(&mut terminal, &mut state); + + disable_raw_mode()?; + execute!( + terminal.backend_mut(), + LeaveAlternateScreen, + DisableMouseCapture + )?; + + terminal.show_cursor()?; + + if let Err(err) = res { + eprintln!("Error: {}", err); + } + Ok(()) +} + +fn run_loop( + terminal: &mut Terminal>, + state: &mut State, +) -> anyhow::Result<()> { + loop { + terminal.draw(|f| render(f, state))?; + + if let Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + if key.modifiers.contains(KeyModifiers::CONTROL) { + return Ok(()); + } + handle_input(key, state)?; + } + } + + if matches!(state.step, Step::Running) { + std::thread::sleep(Duration::from_millis(100)); + } + } +} + +fn handle_input(key: KeyEvent, state: &mut State) -> anyhow::Result<()> { + match state.step { + Step::Welcome => match key.code { + KeyCode::Enter => state.step = Step::ModelSelection, + KeyCode::Esc => return Ok(()), + _ => {} + }, + Step::ModelSelection => match key.code { + KeyCode::Up => { + if state.selected > 0 { + state.selected -= 1 + } + } + KeyCode::Down => { + if state.selected < MODELS.len() - 1 { + state.selected += 1 + } + } + KeyCode::Enter => { + if state.selected == 1 { + state.step = Step::ModelSelection; + } else { + state.app.repo = MODELS[state.selected].to_string(); + fetch_quants(state)?; + state.selected = 0; + state.step = Step::QuantSelection; + } + } + KeyCode::Esc => state.step = Step::Welcome, + _ => {} + }, + Step::QuantSelection => match key.code { + KeyCode::Up => { + if state.selected > 0 { + state.selected -= 1 + } + } + KeyCode::Down => { + if state.selected < state.quants.len().saturating_sub(1) { + state.selected += 1 + } + } + KeyCode::Enter => { + if !state.quants.is_empty() { + state.app.quant = state.quants[state.selected].clone(); + state.field = 0; + state.step = Step::ServerConfig; + } + } + KeyCode::Esc => { + state.quants.clear(); + state.step = Step::ModelSelection; + } + _ => {} + }, + Step::ServerConfig => match key.code { + KeyCode::Up => { + if state.field > 0 { + state.field -= 1 + } + } + KeyCode::Down => { + if state.field < 3 { + state.field += 1 + } + } + KeyCode::Char(c) => match state.field { + 0 => state.host.push(c), + 1 => { + if c.is_ascii_digit() { + state.port.push(c); + } + } + 2 => { + if c.is_ascii_digit() { + state.max_pixels.push(c); + } + } + 3 => { + if c.is_ascii_digit() { + state.min_pixels.push(c); + } + } + _ => {} + }, + KeyCode::Backspace => match state.field { + 0 => { + state.host.pop(); + } + 1 => { + state.port.pop(); + } + 2 => { + state.max_pixels.pop(); + } + 3 => { + state.min_pixels.pop(); + } + _ => {} + }, + KeyCode::Enter => { + state.app.host = state.host.clone(); + state.app.port = state.port.parse().unwrap_or(8080); + state.app.max_pixels = state.max_pixels.parse().ok(); + state.app.min_pixels = state.min_pixels.parse().ok(); + state.step = Step::Summary; + } + KeyCode::Esc => state.step = Step::QuantSelection, + _ => {} + }, + Step::Summary => match key.code { + KeyCode::Enter => { + start_server(state)?; + return Ok(()); + } + KeyCode::Esc => state.step = Step::ServerConfig, + _ => {} + }, + Step::Running => match key.code { + KeyCode::Char('q') | KeyCode::Esc => { + state.app.server_running.store(false, Ordering::SeqCst); + state.step = Step::Summary; + } + _ => {} + }, + } + Ok(()) +} + +fn fetch_quants(state: &mut State) -> anyhow::Result<()> { + state.quants = download::fetch_available_quants(&state.app.repo)?; + Ok(()) +} + +fn start_server(state: &mut State) -> anyhow::Result<()> { + let app = state.app.clone(); + std::thread::spawn(move || { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + if let Err(e) = run_server(app).await { + eprintln!("Server error: {}", e); + } + }); + }); + std::thread::sleep(Duration::from_millis(500)); + Ok(()) +} + +async fn run_server(app: AppState) -> anyhow::Result<()> { + use std::sync::Arc as StdArc; + use vecbox_core::models::qwen3::Qwen3VLEmbedding; + + let downloaded = download::download_model(&app.repo, &app.quant)?; + let device = utils::get_device()?; + let dtype = utils::get_device_dtype(&device)?; + + let mut embedder = Qwen3VLEmbedding::from_gguf_and_mmproj( + &downloaded.gguf_path, + &downloaded.mmproj_path, + &downloaded.config_dir, + &device, + dtype, + )?; + + if let Some(v) = app.max_pixels { + embedder.set_max_pixels(v); + } + if let Some(v) = app.min_pixels { + embedder.set_min_pixels(v); + } + + let api_state = api::AppState { + embedder: StdArc::new(embedder), + model_name: format!("{}-{}", app.repo, app.quant), + }; + + api::run_server(api_state, &app.host, app.port).await +} + +fn render(frame: &mut Frame, state: &mut State) { + let area = frame.area(); + + let block = Block::default() + .borders(Borders::ALL) + .title(match state.step { + Step::Welcome => " vecBox ", + Step::ModelSelection => " Select Model ", + Step::QuantSelection => " Select Quantization ", + Step::ServerConfig => " Server Configuration ", + Step::Summary => " Summary ", + Step::Running => " Server Running ", + }); + + let inner = + Layout::vertical([Constraint::Min(3), Constraint::Length(2)]).split(block.inner(area)); + + frame.render_widget(block, area); + + match state.step { + Step::Welcome => render_welcome(frame, inner[0]), + Step::ModelSelection => render_model_list(frame, inner[0], state), + Step::QuantSelection => render_quant_list(frame, inner[0], state), + Step::ServerConfig => render_config_form(frame, inner[0], state), + Step::Summary => render_summary(frame, inner[0], state), + Step::Running => render_running(frame, inner[0], state), + } + + let hint = match state.step { + Step::Welcome => "ENTER: Start", + Step::ModelSelection => "↑/↓: Select | ENTER: Confirm | ESC: Back", + Step::QuantSelection => "↑/↓: Select | ENTER: Confirm | ESC: Back", + Step::ServerConfig => "↑/↓: Navigate | ENTER: Continue | ESC: Back", + Step::Summary => "ENTER: Start Server | ESC: Back", + Step::Running => "Q/ESC: Stop", + }; + frame.render_widget(Paragraph::new(hint).alignment(Alignment::Center), inner[1]); +} + +fn render_welcome(frame: &mut Frame, area: ratatui::layout::Rect) { + let chunks = Layout::vertical([ + Constraint::Percentage(40), + Constraint::Percentage(20), + Constraint::Percentage(40), + ]) + .split(area); + + frame.render_widget( + Paragraph::new("vecBox") + .style( + Style::default() + .fg(Color::White) + .add_modifier(Modifier::BOLD), + ) + .alignment(Alignment::Center), + chunks[0], + ); + + frame.render_widget( + Paragraph::new("All-in-one inference box for vec* family projects") + .style(Style::default().fg(Color::White)) + .alignment(Alignment::Center), + chunks[1], + ); +} + +fn render_model_list(frame: &mut Frame, area: ratatui::layout::Rect, state: &State) { + let items: Vec = MODELS + .iter() + .enumerate() + .map(|(i, name)| { + let prefix = if i == state.selected { "> " } else { " " }; + let style = if i == state.selected { + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD) + } else { + Style::default() + }; + ListItem::new(format!("{}{}", prefix, name)).style(style) + }) + .collect(); + + frame.render_widget(List::new(items), area); +} + +fn render_quant_list(frame: &mut Frame, area: ratatui::layout::Rect, state: &State) { + if state.quants.is_empty() { + frame.render_widget( + Paragraph::new("Loading...").alignment(Alignment::Center), + area, + ); + return; + } + + let items: Vec = state + .quants + .iter() + .enumerate() + .map(|(i, name)| { + let prefix = if i == state.selected { "> " } else { " " }; + let style = if i == state.selected { + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD) + } else { + Style::default() + }; + ListItem::new(format!("{}{}", prefix, name)).style(style) + }) + .collect(); + + frame.render_widget(List::new(items), area); +} + +fn render_config_form(frame: &mut Frame, area: ratatui::layout::Rect, state: &State) { + let fields = [ + ("Host", &state.host), + ("Port", &state.port), + ("Max Pixels", &state.max_pixels), + ("Min Pixels", &state.min_pixels), + ]; + + let rows: Vec = fields + .iter() + .enumerate() + .map(|(i, (label, value))| { + let cursor = if i == state.field { ">" } else { " " }; + let style = if i == state.field { + Style::default().fg(Color::Yellow) + } else { + Style::default().fg(Color::DarkGray) + }; + Line::styled( + format!( + "{} {}: {}", + cursor, + label, + if value.is_empty() { + "(optional)" + } else { + value + } + ), + style, + ) + }) + .collect(); + + frame.render_widget(Paragraph::new(rows), area); +} + +fn render_summary(frame: &mut Frame, area: ratatui::layout::Rect, state: &State) { + let lines = vec![ + Line::styled(format!("Repo: {}", state.app.repo), Style::default()), + Line::styled(format!("Quant: {}", state.app.quant), Style::default()), + Line::default(), + Line::styled(format!("Host: {}", state.app.host), Style::default()), + Line::styled(format!("Port: {}", state.app.port), Style::default()), + Line::styled( + format!( + "Max Pixels: {}", + state + .app + .max_pixels + .map_or("(none)".into(), |v| v.to_string()) + ), + Style::default(), + ), + Line::styled( + format!( + "Min Pixels: {}", + state + .app + .min_pixels + .map_or("(none)".into(), |v| v.to_string()) + ), + Style::default(), + ), + ]; + + let chunks = + Layout::vertical([Constraint::Percentage(60), Constraint::Percentage(40)]).split(area); + + frame.render_widget(Paragraph::new(lines), chunks[0]); + + frame.render_widget( + Paragraph::new("[ Start Server ]") + .style( + Style::default() + .fg(Color::Green) + .add_modifier(Modifier::BOLD), + ) + .alignment(Alignment::Center), + chunks[1], + ); +} + +fn render_running(frame: &mut Frame, area: ratatui::layout::Rect, state: &State) { + let lines = vec![ + Line::styled( + "Status: RUNNING", + Style::default() + .fg(Color::Green) + .add_modifier(Modifier::BOLD), + ), + Line::default(), + Line::styled( + format!("URL: http://{}:{}", state.app.host, state.app.port), + Style::default(), + ), + Line::default(), + Line::styled(format!("Model: {}", state.app.repo), Style::default()), + Line::styled(format!("Quant: {}", state.app.quant), Style::default()), + ]; + + frame.render_widget(Paragraph::new(lines), area); +}