Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e264ddb98 | ||
|
|
f12ab50146 | ||
|
|
735e1677f5 | ||
|
|
15f2c5f239 | ||
|
|
dd2544c736 | ||
|
|
955a836774 | ||
|
|
407dbcbdd7 |
450
Cargo.lock
generated
450
Cargo.lock
generated
@ -314,7 +314,7 @@ dependencies = [
|
|||||||
"futures-lite",
|
"futures-lite",
|
||||||
"parking",
|
"parking",
|
||||||
"polling",
|
"polling",
|
||||||
"rustix",
|
"rustix 1.1.4",
|
||||||
"slab",
|
"slab",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
@ -735,12 +735,27 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
|
checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cassowary"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "castaway"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cbc"
|
name = "cbc"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -961,6 +976,34 @@ version = "1.0.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "compact_str"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
|
||||||
|
dependencies = [
|
||||||
|
"castaway",
|
||||||
|
"cfg-if",
|
||||||
|
"itoa",
|
||||||
|
"rustversion",
|
||||||
|
"ryu",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "compact_str"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
|
||||||
|
dependencies = [
|
||||||
|
"castaway",
|
||||||
|
"cfg-if",
|
||||||
|
"itoa",
|
||||||
|
"rustversion",
|
||||||
|
"ryu",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compression-codecs"
|
name = "compression-codecs"
|
||||||
version = "0.4.37"
|
version = "0.4.37"
|
||||||
@ -996,7 +1039,7 @@ dependencies = [
|
|||||||
"encode_unicode",
|
"encode_unicode",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"unicode-width 0.2.2",
|
"unicode-width 0.2.0",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1008,7 +1051,7 @@ checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"encode_unicode",
|
"encode_unicode",
|
||||||
"libc",
|
"libc",
|
||||||
"unicode-width 0.2.2",
|
"unicode-width 0.2.0",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1033,6 +1076,15 @@ version = "0.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
|
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.16.2"
|
version = "0.16.2"
|
||||||
@ -1205,6 +1257,49 @@ version = "0.8.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
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 2.1.1",
|
||||||
|
"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]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@ -1286,8 +1381,18 @@ version = "0.20.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.20.11",
|
||||||
"darling_macro",
|
"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]]
|
[[package]]
|
||||||
@ -1304,13 +1409,37 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"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]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.20.11"
|
version = "0.20.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.20.11",
|
||||||
|
"quote",
|
||||||
|
"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",
|
"quote",
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
@ -1399,7 +1528,7 @@ version = "0.18.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58cb0719583cbe4e81fb40434ace2f0d22ccc3e39a74bb3796c22b451b4f139d"
|
checksum = "58cb0719583cbe4e81fb40434ace2f0d22ccc3e39a74bb3796c22b451b4f139d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.20.11",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1485,6 +1614,7 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
@ -1739,7 +1869,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_plain",
|
"serde_plain",
|
||||||
"strum",
|
"strum 0.27.2",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1763,7 +1893,7 @@ dependencies = [
|
|||||||
"object 0.38.1",
|
"object 0.38.1",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"strum",
|
"strum 0.27.2",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1937,6 +2067,12 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@ -2245,7 +2381,9 @@ version = "0.15.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"foldhash",
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
|
"foldhash 0.1.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2253,6 +2391,11 @@ name = "hashbrown"
|
|||||||
version = "0.16.1"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
|
"foldhash 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashify"
|
name = "hashify"
|
||||||
@ -2837,10 +2980,19 @@ dependencies = [
|
|||||||
"console 0.15.11",
|
"console 0.15.11",
|
||||||
"number_prefix",
|
"number_prefix",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"unicode-width 0.2.2",
|
"unicode-width 0.2.0",
|
||||||
"web-time",
|
"web-time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@ -2851,6 +3003,19 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instability"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971"
|
||||||
|
dependencies = [
|
||||||
|
"darling 0.23.0",
|
||||||
|
"indoc",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
@ -2985,6 +3150,17 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "konst"
|
name = "konst"
|
||||||
version = "0.3.16"
|
version = "0.3.16"
|
||||||
@ -3090,6 +3266,21 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -3157,11 +3348,23 @@ dependencies = [
|
|||||||
"weezl",
|
"weezl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.12.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.16.3"
|
version = "0.16.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru-slab"
|
name = "lru-slab"
|
||||||
@ -3851,7 +4054,7 @@ version = "0.44.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1"
|
checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru 0.16.3",
|
||||||
"nostr",
|
"nostr",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@ -3875,7 +4078,7 @@ dependencies = [
|
|||||||
"async-wsocket",
|
"async-wsocket",
|
||||||
"atomic-destructor",
|
"atomic-destructor",
|
||||||
"hex",
|
"hex",
|
||||||
"lru",
|
"lru 0.16.3",
|
||||||
"negentropy",
|
"negentropy",
|
||||||
"nostr",
|
"nostr",
|
||||||
"nostr-database",
|
"nostr-database",
|
||||||
@ -3932,6 +4135,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "number_prefix"
|
name = "number_prefix"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -3948,10 +4160,10 @@ dependencies = [
|
|||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"io-kit-sys 0.5.0",
|
"io-kit-sys 0.5.0",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys 0.12.1",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix 1.1.4",
|
||||||
"slab",
|
"slab",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
@ -4179,6 +4391,12 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbkdf2"
|
name = "pbkdf2"
|
||||||
version = "0.12.2"
|
version = "0.12.2"
|
||||||
@ -4417,7 +4635,7 @@ dependencies = [
|
|||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustix",
|
"rustix 1.1.4",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4963,6 +5181,90 @@ version = "1.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
|
checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratatui"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
"cassowary",
|
||||||
|
"compact_str 0.8.1",
|
||||||
|
"crossterm 0.28.1",
|
||||||
|
"indoc",
|
||||||
|
"instability",
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"lru 0.12.5",
|
||||||
|
"paste",
|
||||||
|
"strum 0.26.3",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-truncate 1.1.0",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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-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 0.9.0",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"indoc",
|
||||||
|
"itertools 0.14.0",
|
||||||
|
"kasuari",
|
||||||
|
"lru 0.16.3",
|
||||||
|
"strum 0.27.2",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-truncate 2.0.1",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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-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 0.14.0",
|
||||||
|
"line-clipping",
|
||||||
|
"ratatui-core",
|
||||||
|
"strum 0.27.2",
|
||||||
|
"time",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@ -5392,6 +5694,19 @@ dependencies = [
|
|||||||
"semver",
|
"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.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
@ -5401,7 +5716,7 @@ dependencies = [
|
|||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys 0.12.1",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5864,6 +6179,27 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
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]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.8"
|
version = "1.4.8"
|
||||||
@ -5955,6 +6291,12 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stop-token"
|
name = "stop-token"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -6009,13 +6351,35 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros 0.26.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.27.2"
|
version = "0.27.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
|
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strum_macros",
|
"strum_macros 0.27.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -6099,7 +6463,7 @@ dependencies = [
|
|||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix 1.1.4",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6171,7 +6535,9 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"libc",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
|
"num_threads",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"time-core",
|
"time-core",
|
||||||
@ -6644,6 +7010,17 @@ version = "0.25.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
|
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tui-textarea"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a5318dd619ed73c52a9417ad19046724effc1287fb75cdcc4eca1d6ac1acbae"
|
||||||
|
dependencies = [
|
||||||
|
"crossterm 0.28.1",
|
||||||
|
"ratatui 0.29.0",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.26.2"
|
version = "0.26.2"
|
||||||
@ -6796,6 +7173,34 @@ version = "0.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-truncate"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||||
|
dependencies = [
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width 0.1.14",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-truncate"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5"
|
||||||
|
dependencies = [
|
||||||
|
"itertools 0.14.0",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
@ -6804,9 +7209,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.2.2"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
@ -7992,6 +8397,7 @@ dependencies = [
|
|||||||
"console 0.16.3",
|
"console 0.16.3",
|
||||||
"criterion",
|
"criterion",
|
||||||
"cron",
|
"cron",
|
||||||
|
"crossterm 0.29.0",
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
"directories",
|
"directories",
|
||||||
"fantoccini",
|
"fantoccini",
|
||||||
@ -8024,6 +8430,7 @@ dependencies = [
|
|||||||
"prost 0.14.3",
|
"prost 0.14.3",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"rand 0.10.0",
|
"rand 0.10.0",
|
||||||
|
"ratatui 0.30.0",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"ring",
|
"ring",
|
||||||
@ -8054,6 +8461,7 @@ dependencies = [
|
|||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"tui-textarea",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wa-rs",
|
"wa-rs",
|
||||||
|
|||||||
@ -193,6 +193,11 @@ pdf-extract = { version = "0.10", optional = true }
|
|||||||
# Terminal QR rendering for WhatsApp Web pairing flow.
|
# Terminal QR rendering for WhatsApp Web pairing flow.
|
||||||
qrcode = { version = "0.14", optional = true }
|
qrcode = { version = "0.14", optional = true }
|
||||||
|
|
||||||
|
# TUI (Terminal User Interface)
|
||||||
|
ratatui = { version = "0.30", optional = true, default-features = false, features = ["crossterm"] }
|
||||||
|
crossterm = { version = "0.29", optional = true }
|
||||||
|
tui-textarea = { version = "0.7", optional = true, features = ["crossterm"] }
|
||||||
|
|
||||||
# WhatsApp Web client (wa-rs) — optional, enable with --features whatsapp-web
|
# WhatsApp Web client (wa-rs) — optional, enable with --features whatsapp-web
|
||||||
# Uses wa-rs for Bot and Client, wa-rs-core for storage traits, custom rusqlite backend avoids Diesel conflict.
|
# Uses wa-rs for Bot and Client, wa-rs-core for storage traits, custom rusqlite backend avoids Diesel conflict.
|
||||||
wa-rs = { version = "0.2", optional = true, default-features = false }
|
wa-rs = { version = "0.2", optional = true, default-features = false }
|
||||||
@ -239,6 +244,8 @@ probe = ["dep:probe-rs"]
|
|||||||
rag-pdf = ["dep:pdf-extract"]
|
rag-pdf = ["dep:pdf-extract"]
|
||||||
# whatsapp-web = Native WhatsApp Web client with custom rusqlite storage backend
|
# whatsapp-web = Native WhatsApp Web client with custom rusqlite storage backend
|
||||||
whatsapp-web = ["dep:wa-rs", "dep:wa-rs-core", "dep:wa-rs-binary", "dep:wa-rs-proto", "dep:wa-rs-ureq-http", "dep:wa-rs-tokio-transport", "dep:serde-big-array", "dep:prost", "dep:qrcode"]
|
whatsapp-web = ["dep:wa-rs", "dep:wa-rs-core", "dep:wa-rs-binary", "dep:wa-rs-proto", "dep:wa-rs-ureq-http", "dep:wa-rs-tokio-transport", "dep:serde-big-array", "dep:prost", "dep:qrcode"]
|
||||||
|
# TUI (Terminal User Interface)
|
||||||
|
tui = ["dep:ratatui", "dep:crossterm", "dep:tui-textarea"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "z" # Optimize for size
|
opt-level = "z" # Optimize for size
|
||||||
|
|||||||
@ -236,7 +236,8 @@ async fn handle_socket(socket: WebSocket, state: AppState, session_id: Option<St
|
|||||||
let user_msg = crate::providers::ChatMessage::user(&content);
|
let user_msg = crate::providers::ChatMessage::user(&content);
|
||||||
let _ = backend.append(&session_key, &user_msg);
|
let _ = backend.append(&session_key, &user_msg);
|
||||||
}
|
}
|
||||||
process_chat_message(&state, &mut agent, &mut sender, &content, &session_key).await;
|
process_chat_message(&state, &mut agent, &mut sender, &content, &session_key)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,6 +73,9 @@ pub mod tools;
|
|||||||
pub(crate) mod tunnel;
|
pub(crate) mod tunnel;
|
||||||
pub(crate) mod util;
|
pub(crate) mod util;
|
||||||
|
|
||||||
|
#[cfg(feature = "tui")]
|
||||||
|
pub mod tui;
|
||||||
|
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
|
|
||||||
/// Gateway management subcommands
|
/// Gateway management subcommands
|
||||||
|
|||||||
26
src/main.rs
26
src/main.rs
@ -528,6 +528,20 @@ Examples:
|
|||||||
#[arg(value_enum)]
|
#[arg(value_enum)]
|
||||||
shell: CompletionShell,
|
shell: CompletionShell,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Launch the Terminal User Interface
|
||||||
|
#[cfg(feature = "tui")]
|
||||||
|
Tui {
|
||||||
|
/// Gateway URL to connect to (default: http://127.0.0.1:<gateway_port>)
|
||||||
|
#[arg(long)]
|
||||||
|
gateway_url: Option<String>,
|
||||||
|
/// Bearer token for authentication
|
||||||
|
#[arg(long)]
|
||||||
|
token: Option<String>,
|
||||||
|
/// Session ID to resume
|
||||||
|
#[arg(long)]
|
||||||
|
session: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
@ -1325,6 +1339,18 @@ async fn main() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[cfg(feature = "tui")]
|
||||||
|
Commands::Tui {
|
||||||
|
gateway_url,
|
||||||
|
token,
|
||||||
|
session,
|
||||||
|
} => {
|
||||||
|
let url =
|
||||||
|
gateway_url.unwrap_or_else(|| format!("http://127.0.0.1:{}", config.gateway.port));
|
||||||
|
let mut app = zeroclaw::tui::TuiApp::new(url, token, session);
|
||||||
|
app.run().await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
141
src/tui/gateway_client.rs
Normal file
141
src/tui/gateway_client.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
//! HTTP + WebSocket client for TUI <-> gateway communication.
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
|
|
||||||
|
/// Gateway HTTP client for REST API calls.
|
||||||
|
pub struct GatewayHttpClient {
|
||||||
|
base_url: String,
|
||||||
|
token: Option<String>,
|
||||||
|
client: reqwest::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GatewayHttpClient {
|
||||||
|
pub fn new(base_url: String, token: Option<String>) -> Self {
|
||||||
|
let client = reqwest::Client::builder()
|
||||||
|
.timeout(std::time::Duration::from_secs(10))
|
||||||
|
.build()
|
||||||
|
.unwrap_or_default();
|
||||||
|
Self {
|
||||||
|
base_url,
|
||||||
|
token,
|
||||||
|
client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn auth_header(&self) -> Option<String> {
|
||||||
|
self.token.as_ref().map(|t| format!("Bearer {t}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_status(&self) -> Result<serde_json::Value> {
|
||||||
|
let mut req = self.client.get(format!("{}/api/status", self.base_url));
|
||||||
|
if let Some(auth) = self.auth_header() {
|
||||||
|
req = req.header("Authorization", auth);
|
||||||
|
}
|
||||||
|
let resp = req.send().await.context("status request failed")?;
|
||||||
|
Ok(resp.json().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_health(&self) -> Result<bool> {
|
||||||
|
match self
|
||||||
|
.client
|
||||||
|
.get(format!("{}/health", self.base_url))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(resp) => Ok(resp.status().is_success()),
|
||||||
|
Err(_) => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_tools(&self) -> Result<serde_json::Value> {
|
||||||
|
let mut req = self.client.get(format!("{}/api/tools", self.base_url));
|
||||||
|
if let Some(auth) = self.auth_header() {
|
||||||
|
req = req.header("Authorization", auth);
|
||||||
|
}
|
||||||
|
let resp = req.send().await.context("tools request failed")?;
|
||||||
|
Ok(resp.json().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_memory(&self) -> Result<serde_json::Value> {
|
||||||
|
let mut req = self.client.get(format!("{}/api/memory", self.base_url));
|
||||||
|
if let Some(auth) = self.auth_header() {
|
||||||
|
req = req.header("Authorization", auth);
|
||||||
|
}
|
||||||
|
let resp = req.send().await.context("memory request failed")?;
|
||||||
|
Ok(resp.json().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_cron(&self) -> Result<serde_json::Value> {
|
||||||
|
let mut req = self.client.get(format!("{}/api/cron", self.base_url));
|
||||||
|
if let Some(auth) = self.auth_header() {
|
||||||
|
req = req.header("Authorization", auth);
|
||||||
|
}
|
||||||
|
let resp = req.send().await.context("cron request failed")?;
|
||||||
|
Ok(resp.json().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_config(&self) -> Result<serde_json::Value> {
|
||||||
|
let mut req = self.client.get(format!("{}/api/config", self.base_url));
|
||||||
|
if let Some(auth) = self.auth_header() {
|
||||||
|
req = req.header("Authorization", auth);
|
||||||
|
}
|
||||||
|
let resp = req.send().await.context("config request failed")?;
|
||||||
|
Ok(resp.json().await?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gateway WebSocket client for real-time chat.
|
||||||
|
pub struct GatewayWsClient {
|
||||||
|
gateway_url: String,
|
||||||
|
token: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GatewayWsClient {
|
||||||
|
pub fn new(gateway_url: String, token: Option<String>) -> Self {
|
||||||
|
Self { gateway_url, token }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect to the gateway WebSocket chat endpoint.
|
||||||
|
pub async fn connect(
|
||||||
|
&self,
|
||||||
|
) -> Result<(
|
||||||
|
futures_util::stream::SplitSink<
|
||||||
|
tokio_tungstenite::WebSocketStream<
|
||||||
|
tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
|
||||||
|
>,
|
||||||
|
Message,
|
||||||
|
>,
|
||||||
|
futures_util::stream::SplitStream<
|
||||||
|
tokio_tungstenite::WebSocketStream<
|
||||||
|
tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
)> {
|
||||||
|
use futures_util::StreamExt;
|
||||||
|
|
||||||
|
let ws_url = self
|
||||||
|
.gateway_url
|
||||||
|
.replace("http://", "ws://")
|
||||||
|
.replace("https://", "wss://");
|
||||||
|
let url = format!("{ws_url}/ws/chat");
|
||||||
|
|
||||||
|
let mut request = tokio_tungstenite::tungstenite::http::Request::builder()
|
||||||
|
.uri(&url)
|
||||||
|
.header("Sec-WebSocket-Protocol", "zeroclaw.v1");
|
||||||
|
|
||||||
|
if let Some(ref token) = self.token {
|
||||||
|
request = request.header("Authorization", format!("Bearer {token}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = request
|
||||||
|
.body(())
|
||||||
|
.context("failed to build WebSocket request")?;
|
||||||
|
|
||||||
|
let (ws_stream, _) = tokio_tungstenite::connect_async(request)
|
||||||
|
.await
|
||||||
|
.context("WebSocket connection failed")?;
|
||||||
|
|
||||||
|
Ok(ws_stream.split())
|
||||||
|
}
|
||||||
|
}
|
||||||
220
src/tui/mod.rs
Normal file
220
src/tui/mod.rs
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
//! Terminal User Interface for ZeroClaw.
|
||||||
|
//!
|
||||||
|
//! Provides a rich terminal dashboard with tabs for status, channels,
|
||||||
|
//! chat, logs, and configuration. Enable with `--features tui`.
|
||||||
|
|
||||||
|
pub mod gateway_client;
|
||||||
|
pub mod tabs;
|
||||||
|
pub mod theme;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
/// Which tab is currently active.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ActiveTab {
|
||||||
|
Dashboard,
|
||||||
|
Channels,
|
||||||
|
Chat,
|
||||||
|
Logs,
|
||||||
|
Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveTab {
|
||||||
|
pub fn label(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Dashboard => "Dashboard",
|
||||||
|
Self::Channels => "Channels",
|
||||||
|
Self::Chat => "Chat",
|
||||||
|
Self::Logs => "Logs",
|
||||||
|
Self::Config => "Config",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all() -> &'static [ActiveTab] {
|
||||||
|
&[
|
||||||
|
Self::Dashboard,
|
||||||
|
Self::Channels,
|
||||||
|
Self::Chat,
|
||||||
|
Self::Logs,
|
||||||
|
Self::Config,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Dashboard => Self::Channels,
|
||||||
|
Self::Channels => Self::Chat,
|
||||||
|
Self::Chat => Self::Logs,
|
||||||
|
Self::Logs => Self::Config,
|
||||||
|
Self::Config => Self::Dashboard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prev(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Dashboard => Self::Config,
|
||||||
|
Self::Channels => Self::Dashboard,
|
||||||
|
Self::Chat => Self::Channels,
|
||||||
|
Self::Logs => Self::Chat,
|
||||||
|
Self::Config => Self::Logs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Main TUI application state.
|
||||||
|
pub struct TuiApp {
|
||||||
|
active_tab: ActiveTab,
|
||||||
|
gateway_url: String,
|
||||||
|
token: Option<String>,
|
||||||
|
session: Option<String>,
|
||||||
|
should_quit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TuiApp {
|
||||||
|
pub fn new(gateway_url: String, token: Option<String>, session: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
active_tab: ActiveTab::Dashboard,
|
||||||
|
gateway_url,
|
||||||
|
token,
|
||||||
|
session,
|
||||||
|
should_quit: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the TUI event loop.
|
||||||
|
#[allow(clippy::unused_async)]
|
||||||
|
pub async fn run(&mut self) -> Result<()> {
|
||||||
|
use crossterm::{
|
||||||
|
event::{self, Event, KeyCode, KeyModifiers},
|
||||||
|
execute,
|
||||||
|
terminal::{
|
||||||
|
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use ratatui::prelude::*;
|
||||||
|
|
||||||
|
enable_raw_mode()?;
|
||||||
|
let mut stdout = std::io::stdout();
|
||||||
|
execute!(stdout, EnterAlternateScreen)?;
|
||||||
|
let backend = CrosstermBackend::new(stdout);
|
||||||
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
|
||||||
|
while !self.should_quit {
|
||||||
|
terminal.draw(|frame| {
|
||||||
|
self.render(frame);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if event::poll(std::time::Duration::from_millis(100))? {
|
||||||
|
if let Event::Key(key) = event::read()? {
|
||||||
|
match (key.modifiers, key.code) {
|
||||||
|
(KeyModifiers::CONTROL, KeyCode::Char('c')) | (_, KeyCode::Char('q')) => {
|
||||||
|
self.should_quit = true
|
||||||
|
}
|
||||||
|
(_, KeyCode::Tab) => self.active_tab = self.active_tab.next(),
|
||||||
|
(KeyModifiers::SHIFT, KeyCode::BackTab) => {
|
||||||
|
self.active_tab = self.active_tab.prev();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_raw_mode()?;
|
||||||
|
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
||||||
|
terminal.show_cursor()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, frame: &mut ratatui::Frame) {
|
||||||
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::{Block, Borders, Paragraph, Tabs},
|
||||||
|
};
|
||||||
|
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Length(3), // Tab bar
|
||||||
|
Constraint::Min(0), // Content
|
||||||
|
Constraint::Length(1), // Status bar
|
||||||
|
])
|
||||||
|
.split(frame.area());
|
||||||
|
|
||||||
|
// Tab bar
|
||||||
|
let titles: Vec<Line> = ActiveTab::all()
|
||||||
|
.iter()
|
||||||
|
.map(|t| {
|
||||||
|
let style = if *t == self.active_tab {
|
||||||
|
Style::default()
|
||||||
|
.fg(theme::ACCENT)
|
||||||
|
.add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(theme::FG_DIM)
|
||||||
|
};
|
||||||
|
Line::from(t.label()).style(style)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let tabs = Tabs::new(titles)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.title(" ZeroClaw TUI "),
|
||||||
|
)
|
||||||
|
.highlight_style(Style::default().fg(theme::ACCENT))
|
||||||
|
.select(
|
||||||
|
ActiveTab::all()
|
||||||
|
.iter()
|
||||||
|
.position(|t| *t == self.active_tab)
|
||||||
|
.unwrap_or(0),
|
||||||
|
);
|
||||||
|
frame.render_widget(tabs, chunks[0]);
|
||||||
|
|
||||||
|
// Content area -- dispatch to tab renderer
|
||||||
|
let content_area = chunks[1];
|
||||||
|
match self.active_tab {
|
||||||
|
ActiveTab::Dashboard => tabs::dashboard::render(frame, content_area),
|
||||||
|
ActiveTab::Channels => tabs::channels::render(frame, content_area),
|
||||||
|
ActiveTab::Chat => tabs::chat::render(frame, content_area),
|
||||||
|
ActiveTab::Logs => tabs::logs::render(frame, content_area),
|
||||||
|
ActiveTab::Config => tabs::config::render(frame, content_area),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status bar
|
||||||
|
let status = Paragraph::new(" Tab/Shift+Tab: switch tabs | q: quit | Ctrl+C: force quit")
|
||||||
|
.style(Style::default().fg(theme::FG_DIM));
|
||||||
|
frame.render_widget(status, chunks[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tab_cycling() {
|
||||||
|
let tab = ActiveTab::Dashboard;
|
||||||
|
assert_eq!(tab.next(), ActiveTab::Channels);
|
||||||
|
assert_eq!(tab.next().next(), ActiveTab::Chat);
|
||||||
|
assert_eq!(ActiveTab::Config.next(), ActiveTab::Dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tab_prev() {
|
||||||
|
assert_eq!(ActiveTab::Dashboard.prev(), ActiveTab::Config);
|
||||||
|
assert_eq!(ActiveTab::Channels.prev(), ActiveTab::Dashboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tab_labels() {
|
||||||
|
assert_eq!(ActiveTab::Dashboard.label(), "Dashboard");
|
||||||
|
assert_eq!(ActiveTab::Chat.label(), "Chat");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_all_tabs() {
|
||||||
|
assert_eq!(ActiveTab::all().len(), 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/tui/tabs/channels.rs
Normal file
35
src/tui/tabs/channels.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//! Channels tab -- table of configured channels with status.
|
||||||
|
|
||||||
|
use crate::tui::theme;
|
||||||
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::{Block, Borders, Row, Table},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn render(frame: &mut Frame, area: Rect) {
|
||||||
|
let header = Row::new(vec!["Channel", "Status", "Type", "Last Message"])
|
||||||
|
.style(Style::default().fg(theme::FG).add_modifier(Modifier::BOLD))
|
||||||
|
.bottom_margin(1);
|
||||||
|
|
||||||
|
let rows = vec![Row::new(vec!["CLI", "Active", "built-in", "---"])
|
||||||
|
.style(Style::default().fg(theme::SUCCESS))];
|
||||||
|
|
||||||
|
let widths = [
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
Constraint::Percentage(15),
|
||||||
|
Constraint::Percentage(20),
|
||||||
|
Constraint::Percentage(40),
|
||||||
|
];
|
||||||
|
|
||||||
|
let table = Table::new(rows, widths)
|
||||||
|
.header(header)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(" Channels "),
|
||||||
|
)
|
||||||
|
.row_highlight_style(Style::default().bg(theme::SURFACE));
|
||||||
|
|
||||||
|
frame.render_widget(table, area);
|
||||||
|
}
|
||||||
45
src/tui/tabs/chat.rs
Normal file
45
src/tui/tabs/chat.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//! Chat tab -- message list and text input.
|
||||||
|
|
||||||
|
use crate::tui::theme;
|
||||||
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::{Block, Borders, Paragraph, Wrap},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn render(frame: &mut Frame, area: Rect) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Min(0), // Messages
|
||||||
|
Constraint::Length(3), // Input
|
||||||
|
])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
// Messages area
|
||||||
|
let messages = vec![Line::from(vec![
|
||||||
|
Span::styled("system", Style::default().fg(theme::FG_DIM)),
|
||||||
|
Span::raw(": Connect to gateway to start chatting."),
|
||||||
|
])];
|
||||||
|
|
||||||
|
let messages_widget = Paragraph::new(messages)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(" Chat "),
|
||||||
|
)
|
||||||
|
.wrap(Wrap { trim: false })
|
||||||
|
.style(Style::default().fg(theme::FG));
|
||||||
|
frame.render_widget(messages_widget, chunks[0]);
|
||||||
|
|
||||||
|
// Input area
|
||||||
|
let input = Paragraph::new(" Type a message... (not connected)")
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(" Input "),
|
||||||
|
)
|
||||||
|
.style(Style::default().fg(theme::FG_DIM));
|
||||||
|
frame.render_widget(input, chunks[1]);
|
||||||
|
}
|
||||||
45
src/tui/tabs/config.rs
Normal file
45
src/tui/tabs/config.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//! Config tab -- TOML viewer with editor launch.
|
||||||
|
|
||||||
|
use crate::tui::theme;
|
||||||
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::{Block, Borders, Paragraph},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn render(frame: &mut Frame, area: Rect) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Min(0), // Config display
|
||||||
|
Constraint::Length(3), // Help bar
|
||||||
|
])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
let config_text = vec![
|
||||||
|
Line::from("Configuration will be loaded from the gateway."),
|
||||||
|
Line::from(""),
|
||||||
|
Line::from(vec![
|
||||||
|
Span::styled(
|
||||||
|
"Tip: ",
|
||||||
|
Style::default()
|
||||||
|
.fg(theme::ACCENT)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
),
|
||||||
|
Span::raw("Press 'e' to open config in $EDITOR"),
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let config_widget = Paragraph::new(config_text)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(" Config (TOML) "),
|
||||||
|
)
|
||||||
|
.style(Style::default().fg(theme::FG));
|
||||||
|
frame.render_widget(config_widget, chunks[0]);
|
||||||
|
|
||||||
|
let help = Paragraph::new(" e: open in $EDITOR | r: refresh")
|
||||||
|
.style(Style::default().fg(theme::FG_DIM));
|
||||||
|
frame.render_widget(help, chunks[1]);
|
||||||
|
}
|
||||||
76
src/tui/tabs/dashboard.rs
Normal file
76
src/tui/tabs/dashboard.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//! Dashboard tab -- status cards and memory gauge.
|
||||||
|
|
||||||
|
use crate::tui::theme;
|
||||||
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::{Block, Borders, Gauge, Paragraph},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn render(frame: &mut Frame, area: Rect) {
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Length(5), // Status cards row
|
||||||
|
Constraint::Length(5), // Memory gauge
|
||||||
|
Constraint::Min(0), // Info area
|
||||||
|
])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
// Status cards
|
||||||
|
let card_chunks = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
Constraint::Percentage(25),
|
||||||
|
])
|
||||||
|
.split(chunks[0]);
|
||||||
|
|
||||||
|
let cards = [
|
||||||
|
("Gateway", "Connecting...", theme::WARNING),
|
||||||
|
("Provider", "---", theme::FG_DIM),
|
||||||
|
("Memory", "---", theme::FG_DIM),
|
||||||
|
("Uptime", "---", theme::FG_DIM),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (i, (title, value, color)) in cards.iter().enumerate() {
|
||||||
|
let block = Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(format!(" {title} "));
|
||||||
|
let paragraph = Paragraph::new(*value)
|
||||||
|
.style(Style::default().fg(*color).add_modifier(Modifier::BOLD))
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.block(block);
|
||||||
|
frame.render_widget(paragraph, card_chunks[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory gauge
|
||||||
|
let gauge = Gauge::default()
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(" Memory Usage "),
|
||||||
|
)
|
||||||
|
.gauge_style(Style::default().fg(theme::ACCENT_SECONDARY))
|
||||||
|
.percent(0)
|
||||||
|
.label("No data");
|
||||||
|
frame.render_widget(gauge, chunks[1]);
|
||||||
|
|
||||||
|
// Info area
|
||||||
|
let info = Paragraph::new(vec![
|
||||||
|
Line::from("Connect to gateway to see live data."),
|
||||||
|
Line::from(""),
|
||||||
|
Line::from("Use Tab/Shift+Tab to switch between tabs."),
|
||||||
|
])
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(" Info "),
|
||||||
|
)
|
||||||
|
.style(Style::default().fg(theme::FG_DIM));
|
||||||
|
frame.render_widget(info, chunks[2]);
|
||||||
|
}
|
||||||
25
src/tui/tabs/logs.rs
Normal file
25
src/tui/tabs/logs.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//! Logs tab -- scrollable log viewer.
|
||||||
|
|
||||||
|
use crate::tui::theme;
|
||||||
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::{Block, Borders, List, ListItem},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn render(frame: &mut Frame, area: Rect) {
|
||||||
|
let items: Vec<ListItem> = vec![ListItem::new(Line::from(vec![
|
||||||
|
Span::styled("[INFO] ", Style::default().fg(theme::SUCCESS)),
|
||||||
|
Span::raw("TUI started. Waiting for log stream..."),
|
||||||
|
]))];
|
||||||
|
|
||||||
|
let list = List::new(items)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.border_style(Style::default().fg(theme::BORDER))
|
||||||
|
.title(" Logs (SSE) "),
|
||||||
|
)
|
||||||
|
.style(Style::default().fg(theme::FG));
|
||||||
|
|
||||||
|
frame.render_widget(list, area);
|
||||||
|
}
|
||||||
5
src/tui/tabs/mod.rs
Normal file
5
src/tui/tabs/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub mod channels;
|
||||||
|
pub mod chat;
|
||||||
|
pub mod config;
|
||||||
|
pub mod dashboard;
|
||||||
|
pub mod logs;
|
||||||
33
src/tui/theme.rs
Normal file
33
src/tui/theme.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//! ZeroClaw TUI color theme (Lobster palette).
|
||||||
|
|
||||||
|
use ratatui::style::Color;
|
||||||
|
|
||||||
|
/// Primary accent color (lobster red)
|
||||||
|
pub const ACCENT: Color = Color::Rgb(220, 50, 47);
|
||||||
|
|
||||||
|
/// Secondary accent
|
||||||
|
pub const ACCENT_SECONDARY: Color = Color::Rgb(38, 139, 210);
|
||||||
|
|
||||||
|
/// Success / healthy
|
||||||
|
pub const SUCCESS: Color = Color::Rgb(133, 153, 0);
|
||||||
|
|
||||||
|
/// Warning
|
||||||
|
pub const WARNING: Color = Color::Rgb(181, 137, 0);
|
||||||
|
|
||||||
|
/// Error / danger
|
||||||
|
pub const ERROR: Color = Color::Rgb(220, 50, 47);
|
||||||
|
|
||||||
|
/// Primary foreground
|
||||||
|
pub const FG: Color = Color::Rgb(253, 246, 227);
|
||||||
|
|
||||||
|
/// Dimmed foreground
|
||||||
|
pub const FG_DIM: Color = Color::Rgb(147, 161, 161);
|
||||||
|
|
||||||
|
/// Primary background
|
||||||
|
pub const BG: Color = Color::Rgb(0, 43, 54);
|
||||||
|
|
||||||
|
/// Surface (slightly lighter than background)
|
||||||
|
pub const SURFACE: Color = Color::Rgb(7, 54, 66);
|
||||||
|
|
||||||
|
/// Border color
|
||||||
|
pub const BORDER: Color = Color::Rgb(88, 110, 117);
|
||||||
Loading…
Reference in New Issue
Block a user