From 0557ffe85337720c3902c9faaf5b5222d46106a7 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 15:52:37 -0500 Subject: [PATCH 01/10] title, quit doesn't work --- Cargo.lock | 1169 ++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/main.rs | 34 +- src/tui/mod.rs | 63 +++ 4 files changed, 1214 insertions(+), 54 deletions(-) create mode 100644 src/tui/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 3903ec8..105ded2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -111,6 +117,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "apple-native-keyring-store" version = "1.0.0" @@ -122,6 +134,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arboard" version = "3.6.1" @@ -247,7 +268,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -282,7 +303,16 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", ] [[package]] @@ -297,12 +327,39 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[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 = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.13.0" @@ -355,6 +412,12 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + [[package]] name = "bytemuck" version = "1.25.0" @@ -379,6 +442,15 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + [[package]] name = "cbc" version = "0.1.2" @@ -404,6 +476,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.45" @@ -458,7 +536,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -491,6 +569,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "compact_str" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -500,6 +592,15 @@ dependencies = [ "crossbeam-utils", ] +[[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 = "core-foundation" version = "0.10.1" @@ -534,12 +635,45 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.13.0", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix", + "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" @@ -557,6 +691,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 = "ctr" version = "0.9.2" @@ -566,6 +710,74 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[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.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "deltae" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[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.118", +] + [[package]] name = "digest" version = "0.10.7" @@ -583,10 +795,25 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags", + "bitflags 2.13.0", "objc2", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + [[package]] name = "endi" version = "1.1.1" @@ -611,7 +838,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -636,6 +863,15 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + [[package]] name = "event-listener" version = "5.4.1" @@ -657,6 +893,22 @@ dependencies = [ "pin-project-lite", ] +[[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 = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + [[package]] name = "fastrand" version = "2.4.1" @@ -678,12 +930,35 @@ 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 = "find-msvc-tools" 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" @@ -694,6 +969,18 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "futures-core" version = "0.3.32" @@ -727,7 +1014,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -780,6 +1067,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + [[package]] name = "getrandom" version = "0.4.3" @@ -788,7 +1087,7 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", ] [[package]] @@ -812,11 +1111,27 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "heck" @@ -878,6 +1193,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "image" version = "0.25.10" @@ -899,7 +1220,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.17.1", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", ] [[package]] @@ -912,12 +1242,34 @@ dependencies = [ "generic-array", ] +[[package]] +name = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -935,6 +1287,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kasuari" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + [[package]] name = "keyring" version = "4.1.2" @@ -956,18 +1319,51 @@ dependencies = [ "log", ] +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "line-clipping" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" +dependencies = [ + "bitflags 2.13.0", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -983,12 +1379,37 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "lru" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" +dependencies = [ + "hashbrown 0.17.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 = "memchr" version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +[[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" @@ -998,6 +1419,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1015,6 +1442,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] @@ -1030,10 +1458,33 @@ dependencies = [ ] [[package]] -name = "num" -version = "0.4.3" +name = "nix" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -1062,6 +1513,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -1102,6 +1570,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "objc2" version = "0.6.4" @@ -1117,7 +1594,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags", + "bitflags 2.13.0", "objc2", "objc2-core-graphics", "objc2-foundation", @@ -1129,7 +1606,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags", + "bitflags 2.13.0", "dispatch2", "objc2", ] @@ -1140,7 +1617,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags", + "bitflags 2.13.0", "dispatch2", "objc2", "objc2-core-foundation", @@ -1159,7 +1636,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags", + "bitflags 2.13.0", "objc2", "objc2-core-foundation", ] @@ -1170,7 +1647,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags", + "bitflags 2.13.0", "objc2", "objc2-core-foundation", ] @@ -1193,6 +1670,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[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 = "ordered-stream" version = "0.2.0" @@ -1203,6 +1689,30 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "libm", + "palette_derive", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "parking" version = "2.2.1" @@ -1249,6 +1759,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.118", +] + +[[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", +] + +[[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.118", +] + +[[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" @@ -1272,7 +1877,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags", + "bitflags 2.13.0", "crc32fast", "fdeflate", "flate2", @@ -1305,6 +1910,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -1344,12 +1961,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -1365,13 +1997,114 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "ratatui" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3274ba0a2c5e1bcad2a2005d20f4dc59dad26b2eb0940fb094500dba4099d57d" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termina", + "ratatui-termwiz", + "ratatui-widgets", + "serde", +] + +[[package]] +name = "ratatui-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c" +dependencies = [ + "bitflags 2.13.0", + "compact_str", + "critical-section", + "hashbrown 0.17.1", + "itertools", + "kasuari", + "lru", + "palette", + "serde", + "strum", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567584a3b0e6a8203c23de40b4861497266725eb5363dbfd18a1edd603cca9f0" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7dc68daa7498a43e4d68e0eb078427e10c38fbcfbb1e42d955f1fa2140d814" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termina" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0bf912d9e66f057a759d92e386a280ea886b352ab757d6ac4d653c7ed2c43c2" +dependencies = [ + "instability", + "ratatui-core", + "termina", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf03e0380b7744054d6cb74224fe3adf062a029754933f575ca1e3b4c2ce977" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1" +dependencies = [ + "bitflags 2.13.0", + "hashbrown 0.17.1", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "serde", + "strum", + "time", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.13.0", ] [[package]] @@ -1424,13 +2157,22 @@ dependencies = [ "windows-sys 0.59.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 = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys", @@ -1443,6 +2185,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "scopeguard" version = "1.2.0" @@ -1474,7 +2222,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.13.0", "core-foundation", "core-foundation-sys", "libc", @@ -1491,6 +2239,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -1518,7 +2272,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1542,7 +2296,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1562,6 +2316,27 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[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" @@ -1582,9 +2357,11 @@ dependencies = [ "chrono", "clap", "colored", + "crossterm", "hex", "keyring", "rand_core 0.10.1", + "ratatui", "rpassword", "serde", "serde_json", @@ -1598,6 +2375,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + [[package]] name = "slab" version = "0.4.12" @@ -1620,18 +2403,56 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "subtle" 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.118" @@ -1656,6 +2477,122 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "termina" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9048a889effe34a5cddee0af7f53285198b16dca3be510858d38dfdb3e62a04e" +dependencies = [ + "bitflags 2.13.0", + "parking_lot", + "rustix", + "signal-hook", + "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", + "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", + "bitflags 2.13.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "tiff" version = "0.11.3" @@ -1670,6 +2607,27 @@ dependencies = [ "zune-jpeg", ] +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + [[package]] name = "tokio" version = "1.52.3" @@ -1695,7 +2653,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1747,7 +2705,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1765,6 +2723,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uds_windows" version = "1.2.1" @@ -1782,6 +2746,29 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "universal-hash" version = "0.5.1" @@ -1804,6 +2791,8 @@ version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ + "atomic", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -1815,12 +2804,30 @@ 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 = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.125" @@ -1853,7 +2860,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -1872,6 +2879,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 = "which" version = "8.0.4" @@ -1881,6 +2960,28 @@ dependencies = [ "libc", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.62.2" @@ -1902,7 +3003,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1913,7 +3014,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2118,6 +3219,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "x11rb" version = "0.13.2" @@ -2190,7 +3297,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "zbus_names", "zvariant", "zvariant_utils", @@ -2224,7 +3331,7 @@ checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2277,7 +3384,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "zvariant_utils", ] @@ -2290,6 +3397,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.118", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index ebea9b3..2cd2cb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,3 +27,5 @@ which = "8.0.4" serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.96" chrono = "0.4.45" +ratatui = "0.30.2" +crossterm = "0.29.0" diff --git a/src/main.rs b/src/main.rs index 0112368..08e2912 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use std::{ io::{self, Read, Write}, }; mod json; +mod tui; #[derive(Parser)] #[clap( name = "Silicate", @@ -1042,30 +1043,17 @@ fn main() { } }, None => { - let websites = - silicate::list_passwords(&config_dir()).expect("Failed to list passwords."); - if websites.is_empty() { - println!("{}", "No passwords stored yet.".yellow()); - } else { - println!("Stored passwords for the following websites:"); - for site in websites { - let (site, tag) = if let Some((s, t)) = site.split_once('-') { - (s.to_string(), Some(t.to_string())) - } else { - (site, None) - }; - if let Some(tag) = tag { - let tag = tag.dimmed().italic(); - println!("{}", format!("- ({}) {}", tag, site.green().bold())); - } else { - println!("{}", format!("- {}", site.green().bold())); - } - } + let mut terminal = ratatui::init(); + let mut app = tui::App::default(); - println!( - "{}", - "Use `silicate show ` to view a password.".dimmed() - ); + match app.run(&mut terminal) { + Ok(_) => { + ratatui::restore(); + } + Err(e) => { + let msg = format!("Failed to open TUI. {}", e).red().bold(); + println!("{msg}"); + } } } } diff --git a/src/tui/mod.rs b/src/tui/mod.rs new file mode 100644 index 0000000..ff81d73 --- /dev/null +++ b/src/tui/mod.rs @@ -0,0 +1,63 @@ +use std::{io, process}; + +use crossterm::event::{Event::Key, KeyCode, KeyEvent, KeyEventKind}; +use ratatui::{DefaultTerminal, Frame, style::Stylize, text::Line, widgets::Widget}; + +pub struct App { + exit: bool, +} + +impl App { + pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> { + while !self.exit { + let input = match crossterm::event::read() { + Ok(k) => k, + Err(e) => { + println!("Failed to read TUI input."); + process::exit(1); + } + }; + + match input { + Key(key_event) => self.handle_key(key_event)?, + _ => {} + } + terminal.draw(|frame| self.draw(frame))?; + } + + Ok(()) + } + + fn draw(&self, frame: &mut Frame) { + frame.render_widget(self, frame.area()); + } + + fn handle_key(&mut self, key_event: KeyEvent) -> io::Result<()> { + if key_event.kind == KeyEventKind::Press { + match key_event.code { + KeyCode::Char('q') => self.exit = false, + _ => {} + } + } + + Ok(()) + } +} + +impl Widget for &App { + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) + where + Self: Sized, + { + Line::from("Silicate - Password Manager") + .bold() + .green() + .render(area, buf); + } +} + +impl Default for App { + fn default() -> App { + App { exit: false } + } +} From a30a4650191774fbc9cbb1f4c3134580c1a6176b Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 16:09:09 -0500 Subject: [PATCH 02/10] fixed bug, was setting exit to false on q :( --- src/tui/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index ff81d73..4d25688 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -35,7 +35,7 @@ impl App { fn handle_key(&mut self, key_event: KeyEvent) -> io::Result<()> { if key_event.kind == KeyEventKind::Press { match key_event.code { - KeyCode::Char('q') => self.exit = false, + KeyCode::Char('q') => self.exit = true, _ => {} } } From 3fdb75ba14e58880910c4264e51089f7b8cdc508 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 16:34:56 -0500 Subject: [PATCH 03/10] idk might work --- src/main.rs | 11 ++++++- src/tui/mod.rs | 82 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index 08e2912..5bc8c5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1043,8 +1043,17 @@ fn main() { } }, None => { + let passwords = match list_passwords(&config_dir()) { + Ok(passwords) => passwords, + Err(e) => { + let msg = format!("Failed to get passwords: {e}").dimmed().red(); + println!("{msg}"); + return; + } + }; + let mut terminal = ratatui::init(); - let mut app = tui::App::default(); + let mut app = tui::App::new(passwords); match app.run(&mut terminal) { Ok(_) => { diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 4d25688..f1f40d9 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,19 +1,63 @@ use std::{io, process}; use crossterm::event::{Event::Key, KeyCode, KeyEvent, KeyEventKind}; -use ratatui::{DefaultTerminal, Frame, style::Stylize, text::Line, widgets::Widget}; +use ratatui::{ + DefaultTerminal, Frame, + layout::{Constraint, Layout}, + style::{Modifier, Style, Stylize}, + widgets::{ + Block, Borders, HighlightSpacing, List, ListItem, ListState, StatefulWidget, Widget, + }, +}; pub struct App { exit: bool, + entries: Vec, + state: ListState, } impl App { + pub fn new(entries: Vec) -> Self { + let mut state = ListState::default(); + state.select(Some(0)); + App { + exit: false, + entries, + state, + } + } + + pub fn next(&mut self) { + let selected = self.state.selected().unwrap_or(0); + + let next = if selected >= self.entries.len() - 1 { + 0 + } else { + selected + 1 + }; + + self.state.select(Some(next)); + } + + pub fn previous(&mut self) { + let selected = self.state.selected().unwrap_or(0); + + let previous = if selected == 0 { + self.entries.len() - 1 + } else { + selected - 1 + }; + + self.state.select(Some(previous)); + } + pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> { while !self.exit { let input = match crossterm::event::read() { Ok(k) => k, Err(e) => { - println!("Failed to read TUI input."); + let msg = format!("Failed to read TUI input: {e}").red(); + println!("{msg}"); process::exit(1); } }; @@ -28,8 +72,20 @@ impl App { Ok(()) } - fn draw(&self, frame: &mut Frame) { - frame.render_widget(self, frame.area()); + fn draw(&mut self, frame: &mut Frame) { + let area = frame.area(); + + let horizontal_area = + Layout::horizontal([Constraint::Percentage(10), Constraint::Percentage(90)]) + .areas(area); + + let [list_area, view_area] = horizontal_area; + + let items = self.entries.iter().map(|item| ListItem::new(item.clone())); // Ensure items map cleanly + let list = + List::new(items).block(Block::default().title("Passwords").borders(Borders::ALL)); + + frame.render_stateful_widget(list, list_area, &mut self.state); } fn handle_key(&mut self, key_event: KeyEvent) -> io::Result<()> { @@ -43,21 +99,3 @@ impl App { Ok(()) } } - -impl Widget for &App { - fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) - where - Self: Sized, - { - Line::from("Silicate - Password Manager") - .bold() - .green() - .render(area, buf); - } -} - -impl Default for App { - fn default() -> App { - App { exit: false } - } -} From 7e06f1df87cf77ce3f054b80ed38b379eb28e892 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 16:38:18 -0500 Subject: [PATCH 04/10] added up and down movement --- src/tui/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index f1f40d9..b32d84c 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,5 +1,6 @@ use std::{io, process}; +use aes_gcm::Key; use crossterm::event::{Event::Key, KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ DefaultTerminal, Frame, @@ -92,6 +93,8 @@ impl App { if key_event.kind == KeyEventKind::Press { match key_event.code { KeyCode::Char('q') => self.exit = true, + KeyCode::Up => self.previous(), + KeyCode::Down => self.next(), _ => {} } } From f8d261018fe9754b02587e7581f4693f335e26f4 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 16:39:05 -0500 Subject: [PATCH 05/10] made it compile --- src/tui/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index b32d84c..283ae03 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,6 +1,5 @@ use std::{io, process}; -use aes_gcm::Key; use crossterm::event::{Event::Key, KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ DefaultTerminal, Frame, From dbeb706ee28a7fcc4a1a97d82c86e514528bbba4 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 16:43:58 -0500 Subject: [PATCH 06/10] added highlight styling --- src/tui/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 283ae03..20b96a0 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -82,8 +82,15 @@ impl App { let [list_area, view_area] = horizontal_area; let items = self.entries.iter().map(|item| ListItem::new(item.clone())); // Ensure items map cleanly - let list = - List::new(items).block(Block::default().title("Passwords").borders(Borders::ALL)); + let list = List::new(items) + .block(Block::default().title("Passwords").borders(Borders::ALL)) + .highlight_symbol("> ") + .highlight_style( + Style::default() + .fg(ratatui::style::Color::Green) + .add_modifier(Modifier::REVERSED), + ) + .highlight_spacing(HighlightSpacing::Always); frame.render_stateful_widget(list, list_area, &mut self.state); } From 389394246dc2f69c2d671e0c27dd43d42434a840 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 17:48:06 -0500 Subject: [PATCH 07/10] basic tui layout, searching variables implemented --- src/lib.rs | 13 +++++- src/main.rs | 4 +- src/tui/mod.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 116 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 921fcd4..c27ad89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ const USERNAME: &str = "default"; #[derive(Debug)] pub enum SilicateError { + Plain(&'static str), KeyringError(keyring::Error), IoError(std::io::Error), HexError(hex::FromHexError), @@ -31,6 +32,12 @@ pub enum SilicateError { TryFromSliceError(std::array::TryFromSliceError), } +impl From<&'static str> for SilicateError { + fn from(err: &'static str) -> SilicateError { + SilicateError::Plain(err) + } +} + impl From for SilicateError { fn from(err: keyring::Error) -> SilicateError { SilicateError::KeyringError(err) @@ -116,6 +123,10 @@ impl std::fmt::Display for SilicateError { write!(f, "Argon2 password hash error: {}", e) } SilicateError::StdioError(e) => write!(f, "Stdio error: {}", e), + SilicateError::Plain(e) => { + let msg = e.to_string(); + write!(f, "Program Error: {msg}") + } } } } @@ -260,7 +271,7 @@ pub fn search_password( }) .map(|w| { if let Some((site, _)) = w.split_once('-') { - format!("{}", site) + site.to_string() } else { w.clone() } diff --git a/src/main.rs b/src/main.rs index 5bc8c5c..cc181b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1052,8 +1052,10 @@ fn main() { } }; + let key = get_key().try_into().unwrap(); + let mut terminal = ratatui::init(); - let mut app = tui::App::new(passwords); + let mut app = tui::App::new(passwords, key); match app.run(&mut terminal) { Ok(_) => { diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 20b96a0..171cb24 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -1,29 +1,35 @@ -use std::{io, process}; +use std::{fs, io, process}; use crossterm::event::{Event::Key, KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ DefaultTerminal, Frame, layout::{Constraint, Layout}, style::{Modifier, Style, Stylize}, - widgets::{ - Block, Borders, HighlightSpacing, List, ListItem, ListState, StatefulWidget, Widget, - }, + text::{Line, Span}, + widgets::{Block, Borders, HighlightSpacing, List, ListItem, ListState, Paragraph, Wrap}, }; +use silicate::SilicateError; pub struct App { exit: bool, entries: Vec, state: ListState, + key: [u8; 32], + search_query: String, + is_searching: bool, } impl App { - pub fn new(entries: Vec) -> Self { + pub fn new(entries: Vec, key: [u8; 32]) -> Self { let mut state = ListState::default(); state.select(Some(0)); App { exit: false, entries, state, + key, + search_query: String::new(), + is_searching: false, } } @@ -75,13 +81,24 @@ impl App { fn draw(&mut self, frame: &mut Frame) { let area = frame.area(); + let main_layout = Layout::vertical([ + Constraint::Min(0), + Constraint::Length(if self.is_searching { 3 } else { 0 }), + ]) + .split(area); + + let main_area = main_layout[0]; + let search_area = main_layout[1]; + let horizontal_area = - Layout::horizontal([Constraint::Percentage(10), Constraint::Percentage(90)]) - .areas(area); + Layout::horizontal([Constraint::Percentage(25), Constraint::Percentage(75)]) + .areas(main_area); let [list_area, view_area] = horizontal_area; - let items = self.entries.iter().map(|item| ListItem::new(item.clone())); // Ensure items map cleanly + let filtered = self.filtered_entries(); + let items = filtered.iter().map(|item| ListItem::new((*item).clone())); + let list = List::new(items) .block(Block::default().title("Passwords").borders(Borders::ALL)) .highlight_symbol("> ") @@ -93,6 +110,82 @@ impl App { .highlight_spacing(HighlightSpacing::Always); frame.render_stateful_widget(list, list_area, &mut self.state); + + // 3. Determine the decrypted content for the right pane + let details_content = if let Some(selected_idx) = self.state.selected() { + if let Some(entry_name) = self.entries.get(selected_idx) { + match self.get_decrypted_password(entry_name) { + Ok(password) => vec![ + Line::from(vec![ + Span::raw("Account/Site: "), + Span::styled( + entry_name, + Style::default() + .fg(ratatui::style::Color::Cyan) + .add_modifier(Modifier::BOLD), + ), + ]), + Line::from(""), + Line::from(vec![ + Span::raw("Password: "), + Span::styled( + password, + Style::default().fg(ratatui::style::Color::Green), + ), + ]), + ], + Err(e) => vec![Line::from(Span::styled( + format!("Decryption Error: {}", e), + Style::default().fg(ratatui::style::Color::Red), + ))], + } + } else { + vec![Line::from("No entry found.")] + } + } else { + vec![Line::from("Select an entry to view details.")] + }; + + let details_block = Paragraph::new(details_content) + .block(Block::default().title("Details").borders(Borders::ALL)) + .wrap(Wrap { trim: true }); + + frame.render_widget(details_block, view_area); + } + + /// Helper method to fetch and decrypt the file for the TUI view pane + fn get_decrypted_password(&self, entry_name: &str) -> Result { + // Construct the expected file path inside ~/.silicate/ + let home = std::env::var("HOME").unwrap(); + let full_path = format!("{}/.silicate/{}.bin", home, entry_name); + + // Read the encrypted file binary chunk + let data = fs::read(full_path)?; + if data.len() < 12 { + return Err(SilicateError::Plain( + "Invalid password file format (too short)", + )); + } + + let (nonce_bytes, cipher_bytes) = data.split_at(12); + + let decrypted = + silicate::decrypt_passwd(&self.key, cipher_bytes.to_vec(), nonce_bytes.try_into()?)?; + + Ok(decrypted) + } + + /// Helper to get entries filtered by the current search query + fn filtered_entries(&self) -> Vec<&String> { + if self.search_query.is_empty() { + self.entries.iter().collect() + } else { + let query = self.search_query.to_lowercase(); + self.entries + .iter() + .filter(|entry| entry.to_lowercase().contains(&query)) + .collect() + } } fn handle_key(&mut self, key_event: KeyEvent) -> io::Result<()> { From c49cae5307a3c0ff23a38ccaac1b43cd015c05ff Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 20:07:58 -0500 Subject: [PATCH 08/10] search implemented --- src/tui/mod.rs | 69 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 171cb24..c5338f3 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -3,13 +3,24 @@ use std::{fs, io, process}; use crossterm::event::{Event::Key, KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ DefaultTerminal, Frame, - layout::{Constraint, Layout}, + layout::{Alignment, Constraint, Layout, Rect}, style::{Modifier, Style, Stylize}, text::{Line, Span}, - widgets::{Block, Borders, HighlightSpacing, List, ListItem, ListState, Paragraph, Wrap}, + widgets::{ + Block, Borders, Clear, HighlightSpacing, List, ListItem, ListState, Paragraph, Wrap, + }, }; use silicate::SilicateError; +fn centered_rect(width: u16, height: u16, area: Rect) -> Rect { + Rect { + x: area.x + (area.width.saturating_sub(width)) / 2, + y: area.y + (area.height.saturating_sub(height)) / 2, + width, + height, + } +} + pub struct App { exit: bool, entries: Vec, @@ -81,18 +92,9 @@ impl App { fn draw(&mut self, frame: &mut Frame) { let area = frame.area(); - let main_layout = Layout::vertical([ - Constraint::Min(0), - Constraint::Length(if self.is_searching { 3 } else { 0 }), - ]) - .split(area); - - let main_area = main_layout[0]; - let search_area = main_layout[1]; - let horizontal_area = Layout::horizontal([Constraint::Percentage(25), Constraint::Percentage(75)]) - .areas(main_area); + .areas(area); let [list_area, view_area] = horizontal_area; @@ -111,7 +113,6 @@ impl App { frame.render_stateful_widget(list, list_area, &mut self.state); - // 3. Determine the decrypted content for the right pane let details_content = if let Some(selected_idx) = self.state.selected() { if let Some(entry_name) = self.entries.get(selected_idx) { match self.get_decrypted_password(entry_name) { @@ -151,6 +152,21 @@ impl App { .wrap(Wrap { trim: true }); frame.render_widget(details_block, view_area); + + if self.is_searching { + let popup = centered_rect(50, 3, area); + + frame.render_widget(Clear, popup); + + let input = Paragraph::new(self.search_query.as_str()) + .block(Block::default().title(" Search ").borders(Borders::ALL)) + .alignment(Alignment::Left); + + frame.render_widget(input, popup); + + // Cursor goes inside the box + frame.set_cursor_position((popup.x + 1 + self.search_query.len() as u16, popup.y + 1)); + } } /// Helper method to fetch and decrypt the file for the TUI view pane @@ -190,11 +206,28 @@ impl App { fn handle_key(&mut self, key_event: KeyEvent) -> io::Result<()> { if key_event.kind == KeyEventKind::Press { - match key_event.code { - KeyCode::Char('q') => self.exit = true, - KeyCode::Up => self.previous(), - KeyCode::Down => self.next(), - _ => {} + if self.is_searching { + match key_event.code { + KeyCode::Esc => self.is_searching = false, + KeyCode::Enter => { + self.is_searching = false; + } + KeyCode::Backspace => { + self.search_query.pop(); + } + KeyCode::Char(c) => { + self.search_query.push(c); + } + _ => {} + } + } else { + match key_event.code { + KeyCode::Char('q') => self.exit = true, + KeyCode::Char('/') => self.is_searching = true, + KeyCode::Up => self.previous(), + KeyCode::Down => self.next(), + _ => {} + } } } From 8aa409d3a9e05534b29d57b17ef82bdd91b0e250 Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 20:11:39 -0500 Subject: [PATCH 09/10] changed tag appearance --- src/tui/mod.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index c5338f3..2565536 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -99,7 +99,25 @@ impl App { let [list_area, view_area] = horizontal_area; let filtered = self.filtered_entries(); - let items = filtered.iter().map(|item| ListItem::new((*item).clone())); + + let items: Vec = filtered + .iter() + .map(|item_str| { + // Split the string into name and tag components by the hyphen + if let Some((name, tag)) = item_str.split_once('-') { + // Trim whitespace to keep the layout clean + let name_span = ratatui::text::Span::raw(format!("{} ", name.trim_end())); + let tag_span = ratatui::text::Span::raw(format!("({})", tag.trim())).dim(); + + // Combine them into a single Line + let line = ratatui::text::Line::from(vec![name_span, tag_span]); + ListItem::new(line) + } else { + // Fallback in case a string doesn't contain a hyphen + ListItem::new((*item_str).clone()) + } + }) + .collect(); let list = List::new(items) .block(Block::default().title("Passwords").borders(Borders::ALL)) From 6a6c86739428a4eb649b27f5824f06867857878f Mon Sep 17 00:00:00 2001 From: Maaz Khokhar Date: Thu, 25 Jun 2026 20:27:43 -0500 Subject: [PATCH 10/10] added a toggle between tag and name --- src/tui/mod.rs | 97 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 2565536..6965466 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -21,6 +21,12 @@ fn centered_rect(width: u16, height: u16, area: Rect) -> Rect { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SearchTarget { + Name, + Tag, +} + pub struct App { exit: bool, entries: Vec, @@ -28,6 +34,7 @@ pub struct App { key: [u8; 32], search_query: String, is_searching: bool, + search_target: SearchTarget, } impl App { @@ -41,13 +48,14 @@ impl App { key, search_query: String::new(), is_searching: false, + search_target: SearchTarget::Name, } } pub fn next(&mut self) { let selected = self.state.selected().unwrap_or(0); - let next = if selected >= self.entries.len() - 1 { + let next = if selected >= self.filtered_entries().len() - 1 { 0 } else { selected + 1 @@ -60,7 +68,7 @@ impl App { let selected = self.state.selected().unwrap_or(0); let previous = if selected == 0 { - self.entries.len() - 1 + self.filtered_entries().len() - 1 } else { selected - 1 }; @@ -79,9 +87,13 @@ impl App { } }; - match input { - Key(key_event) => self.handle_key(key_event)?, - _ => {} + //match input { + // Key(key_event) => self.handle_key(key_event)?, + // _ => {} + //} + + if let Key(key_event) = input { + self.handle_key(key_event)?; } terminal.draw(|frame| self.draw(frame))?; } @@ -103,18 +115,14 @@ impl App { let items: Vec = filtered .iter() .map(|item_str| { - // Split the string into name and tag components by the hyphen if let Some((name, tag)) = item_str.split_once('-') { - // Trim whitespace to keep the layout clean let name_span = ratatui::text::Span::raw(format!("{} ", name.trim_end())); let tag_span = ratatui::text::Span::raw(format!("({})", tag.trim())).dim(); - - // Combine them into a single Line let line = ratatui::text::Line::from(vec![name_span, tag_span]); ListItem::new(line) } else { - // Fallback in case a string doesn't contain a hyphen - ListItem::new((*item_str).clone()) + // item_str is now &String, so we can clone it directly without dereferencing + ListItem::new(item_str.clone()) } }) .collect(); @@ -132,7 +140,7 @@ impl App { frame.render_stateful_widget(list, list_area, &mut self.state); let details_content = if let Some(selected_idx) = self.state.selected() { - if let Some(entry_name) = self.entries.get(selected_idx) { + if let Some(entry_name) = filtered.get(selected_idx) { match self.get_decrypted_password(entry_name) { Ok(password) => vec![ Line::from(vec![ @@ -172,14 +180,56 @@ impl App { frame.render_widget(details_block, view_area); if self.is_searching { - let popup = centered_rect(50, 3, area); + let popup = centered_rect(50, 5, area); frame.render_widget(Clear, popup); - let input = Paragraph::new(self.search_query.as_str()) + // Construct the visual toggle selectors with custom highlights + let name_style = if self.search_target == SearchTarget::Name { + Style::default() + .fg(ratatui::style::Color::Green) + .add_modifier(Modifier::BOLD) + } else { + Style::default().fg(ratatui::style::Color::DarkGray) + }; + + let tag_style = if self.search_target == SearchTarget::Tag { + Style::default() + .fg(ratatui::style::Color::Green) + .add_modifier(Modifier::BOLD) + } else { + Style::default().fg(ratatui::style::Color::DarkGray) + }; + + let name_icon = if self.search_target == SearchTarget::Name { + "[● Name] " + } else { + "[○ Name] " + }; + let tag_icon = if self.search_target == SearchTarget::Tag { + "[● Tag]" + } else { + "[○ Tag]" + }; + + let popup_content = vec![ + Line::from(self.search_query.as_str()), + Line::from("─".repeat((popup.width as usize).saturating_sub(2))).dim(), + Line::from(vec![ + Span::styled(name_icon, name_style), + Span::styled(tag_icon, tag_style), + ]), + ]; + + let input = Paragraph::new(popup_content) .block(Block::default().title(" Search ").borders(Borders::ALL)) .alignment(Alignment::Left); + frame.render_widget(&input, popup); + + // Pin the cursor strictly to the query string line (top row of popup payload area) + frame.set_cursor_position((popup.x + 1 + self.search_query.len() as u16, popup.y + 1)); + frame.render_widget(input, popup); // Cursor goes inside the box @@ -187,6 +237,14 @@ impl App { } } + /// Helper method to toggle between name and tag + fn toggle_search(&mut self) { + match self.search_target { + SearchTarget::Name => self.search_target = SearchTarget::Tag, + SearchTarget::Tag => self.search_target = SearchTarget::Name, + } + } + /// Helper method to fetch and decrypt the file for the TUI view pane fn get_decrypted_password(&self, entry_name: &str) -> Result { // Construct the expected file path inside ~/.silicate/ @@ -210,14 +268,15 @@ impl App { } /// Helper to get entries filtered by the current search query - fn filtered_entries(&self) -> Vec<&String> { + fn filtered_entries(&self) -> Vec { if self.search_query.is_empty() { - self.entries.iter().collect() + self.entries.to_vec() } else { let query = self.search_query.to_lowercase(); self.entries .iter() .filter(|entry| entry.to_lowercase().contains(&query)) + .cloned() .collect() } } @@ -230,11 +289,17 @@ impl App { KeyCode::Enter => { self.is_searching = false; } + KeyCode::Tab => { + self.toggle_search(); + self.state.select(Some(0)); + } KeyCode::Backspace => { self.search_query.pop(); + self.state.select(Some(0)); } KeyCode::Char(c) => { self.search_query.push(c); + self.state.select(Some(0)); } _ => {} }