diff --git a/plugin/Cargo.lock b/plugin/Cargo.lock index d5a71f1..ffbddba 100644 --- a/plugin/Cargo.lock +++ b/plugin/Cargo.lock @@ -23,64 +23,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "async-broadcast" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "bitflags" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - [[package]] name = "cc" version = "1.2.61" @@ -155,15 +97,6 @@ dependencies = [ "unicode-width 0.2.2", ] -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "convert_case" version = "0.6.0" @@ -173,12 +106,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "ctor" version = "0.13.1" @@ -355,76 +282,12 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "endi" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" - -[[package]] -name = "enumflags2" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -437,49 +300,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -509,12 +329,6 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "indexmap" version = "2.14.0" @@ -550,18 +364,6 @@ dependencies = [ "libc", ] -[[package]] -name = "js-sys" -version = "0.3.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -604,12 +406,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44cd706ff0d503ee32b2071166510ca27e281228de10cd3aa8d35ff94560f81" -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - [[package]] name = "log" version = "0.4.29" @@ -631,26 +427,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "wasi", - "windows-sys", -] - [[package]] name = "nova-plugin" version = "0.1.0" @@ -664,10 +440,8 @@ dependencies = [ "libc", "serde", "serde_json", - "tokio", "tracing", "tracing-subscriber", - "zbus", ] [[package]] @@ -691,37 +465,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -787,19 +536,6 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - [[package]] name = "rustversion" version = "1.0.22" @@ -861,17 +597,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -887,38 +612,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -942,19 +641,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -1013,64 +699,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "tokio" -version = "1.52.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "tracing", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.25.11+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" -dependencies = [ - "indexmap", - "toml_datetime", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow", -] - [[package]] name = "tracing" version = "0.1.44" @@ -1132,17 +760,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "uds_windows" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" -dependencies = [ - "memoffset", - "tempfile", - "windows-sys", -] - [[package]] name = "unicode-ident" version = "1.0.24" @@ -1167,17 +784,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "uuid" -version = "1.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" -dependencies = [ - "js-sys", - "serde_core", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -1199,51 +805,6 @@ dependencies = [ "wit-bindgen", ] -[[package]] -name = "wasm-bindgen" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" -dependencies = [ - "unicode-ident", -] - [[package]] name = "winapi-util" version = "0.1.11" @@ -1268,119 +829,14 @@ dependencies = [ "windows-link", ] -[[package]] -name = "winnow" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "zbus" -version = "5.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" -dependencies = [ - "async-broadcast", - "async-recursion", - "async-trait", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "libc", - "ordered-stream", - "rustix", - "serde", - "serde_repr", - "tokio", - "tracing", - "uds_windows", - "uuid", - "windows-sys", - "winnow", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" -dependencies = [ - "serde", - "winnow", - "zvariant", -] - [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zvariant" -version = "5.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" -dependencies = [ - "endi", - "enumflags2", - "serde", - "winnow", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn", - "winnow", -] diff --git a/plugin/Cargo.toml b/plugin/Cargo.toml index 3a1cce9..f2fed37 100644 --- a/plugin/Cargo.toml +++ b/plugin/Cargo.toml @@ -23,14 +23,6 @@ serde_json = "1.0.149" dirs = "6.0.0" tracing = "0.1.44" tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } -zbus = { version = "5.15.0", default-features = false, features = ["tokio"] } -tokio = { version = "1.52.2", features = [ - "macros", - "net", - "rt", - "sync", - "time", -] } [build-dependencies] cxx-qt-build = "0.8.1" diff --git a/plugin/build.rs b/plugin/build.rs index d975722..4f4ed35 100644 --- a/plugin/build.rs +++ b/plugin/build.rs @@ -31,7 +31,6 @@ fn main() { "src/system_stats.rs", "src/cpu_service.rs", "src/modules_service.rs", - "src/systemd_service.rs", "src/theme_service.rs", ]; if !bridge_files.iter().all(|p| manifest.join(p).exists()) { diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index 79a8759..7531222 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -2,7 +2,6 @@ pub mod cpu_service; pub mod modules_service; pub mod stats; pub mod system_stats; -pub mod systemd_service; pub mod theme_service; #[ctor::ctor(unsafe)] diff --git a/plugin/src/modules_service.rs b/plugin/src/modules_service.rs index 05e33ad..aea0e77 100644 --- a/plugin/src/modules_service.rs +++ b/plugin/src/modules_service.rs @@ -101,9 +101,11 @@ pub mod qobject { #[qproperty(bool, dock_applet_mpris, cxx_name = "dockAppletMpris")] #[qproperty(bool, dock_applet_notifications, cxx_name = "dockAppletNotifications")] #[qproperty(bool, dock_applet_power, cxx_name = "dockAppletPower")] - // Unified systemd bar module (covers local + nspawn containers, and later remotes). + // Systemd / machinectl bar modules. #[qproperty(bool, systemd_enable, cxx_name = "systemdEnable")] #[qproperty(i32, systemd_interval, cxx_name = "systemdInterval")] + #[qproperty(bool, machinectl_enable, cxx_name = "machinectlEnable")] + #[qproperty(i32, machinectl_interval, cxx_name = "machinectlInterval")] type ModulesService = super::ModulesServiceRust; } @@ -520,6 +522,8 @@ mod data { #[serde(default)] pub systemd: WithInterval, #[serde(default)] + pub machinectl: WithInterval, + #[serde(default)] pub stats_daemon: StatsDaemon, } } @@ -595,6 +599,8 @@ pub struct ModulesServiceRust { dock_applet_power: bool, systemd_enable: bool, systemd_interval: i32, + machinectl_enable: bool, + machinectl_interval: i32, } impl Default for ModulesServiceRust { @@ -678,6 +684,8 @@ impl ModulesServiceRust { dock_applet_power: d.dock.applets.power, systemd_enable: d.systemd.enable, systemd_interval: d.systemd.interval, + machinectl_enable: d.machinectl.enable, + machinectl_interval: d.machinectl.interval, } } } @@ -738,6 +746,7 @@ fn parse_modules(raw: &str, path: &std::path::Path) -> ModulesData { "lock", "dock", "systemd", + "machinectl", "statsDaemon", ]; for key in map.keys() { diff --git a/plugin/src/systemd_service.rs b/plugin/src/systemd_service.rs deleted file mode 100644 index 53021d0..0000000 --- a/plugin/src/systemd_service.rs +++ /dev/null @@ -1,273 +0,0 @@ -// In-process systemd state for nova-shell. -// -// Lists of units and machines are exposed to QML as JSON-encoded QString props -// (`failedUnitsJson`, `containersJson`) rather than QList. cxx-qt -// 0.8.1 does not implement QVariantValue for QVariantMap/QVariantList, and -// cxx-qt main on git regressed qt-build-utils to require QuickControls2.prl -// files that nixpkgs strips. Switch to QList when a release ships -// with both fixes. - -use core::pin::Pin; -use cxx_qt_lib::QString; -use serde::Serialize; -use std::sync::OnceLock; -use tokio::runtime::Runtime; -use zbus::{Connection, proxy}; - -#[cxx_qt::bridge] -pub mod qobject { - unsafe extern "C++" { - include!("cxx-qt-lib/qstring.h"); - type QString = cxx_qt_lib::QString; - } - - extern "RustQt" { - #[qobject] - #[qml_element] - #[qml_singleton] - #[qproperty(QString, hostname)] - #[qproperty(QString, system_state, cxx_name = "systemState")] - #[qproperty(QString, user_state, cxx_name = "userState")] - #[qproperty(i32, failed_count, cxx_name = "failedCount")] - // JSON array: [{ name, description, subState, scope: "system"|"user", machine: "" | name }] - #[qproperty(QString, failed_units_json, cxx_name = "failedUnitsJson")] - // JSON array: [{ name, class, service, systemState, failedUnits: [...] }] - #[qproperty(QString, containers_json, cxx_name = "containersJson")] - type SystemdService = super::SystemdServiceRust; - - #[qinvokable] - fn poll(self: Pin<&mut Self>); - - #[qinvokable] - #[cxx_name = "restartUnit"] - fn restart_unit(self: Pin<&mut Self>, name: QString, scope: QString, machine: QString); - } - - impl cxx_qt::Initialize for SystemdService {} -} - -// systemd1.Manager.ListUnitsFiltered returns a(ssssssouso): name, description, -// load_state, active_state, sub_state, follower, unit_path, job_id, job_type, job_path. -type UnitTuple = ( - String, - String, - String, - String, - String, - String, - zbus::zvariant::OwnedObjectPath, - u32, - String, - zbus::zvariant::OwnedObjectPath, -); - -#[proxy( - interface = "org.freedesktop.systemd1.Manager", - default_service = "org.freedesktop.systemd1", - default_path = "/org/freedesktop/systemd1" -)] -trait SystemdManager { - #[zbus(property)] - fn system_state(&self) -> zbus::Result; - - fn list_units_filtered(&self, states: Vec<&str>) -> zbus::Result>; - - fn restart_unit(&self, name: &str, mode: &str) - -> zbus::Result; -} - -#[proxy( - interface = "org.freedesktop.machine1.Manager", - default_service = "org.freedesktop.machine1", - default_path = "/org/freedesktop/machine1" -)] -trait Machined { - fn list_machines( - &self, - ) -> zbus::Result>; -} - -#[derive(Serialize)] -struct UnitJson<'a> { - name: &'a str, - description: &'a str, - #[serde(rename = "subState")] - sub_state: &'a str, - scope: &'a str, - machine: &'a str, -} - -#[derive(Serialize)] -struct ContainerJson<'a> { - name: &'a str, - class: &'a str, - service: &'a str, - #[serde(rename = "systemState")] - system_state: &'a str, - #[serde(rename = "failedUnits")] - failed_units: Vec>, -} - -pub struct SystemdServiceRust { - hostname: QString, - system_state: QString, - user_state: QString, - failed_count: i32, - failed_units_json: QString, - containers_json: QString, -} - -impl Default for SystemdServiceRust { - fn default() -> Self { - Self { - hostname: QString::from(read_hostname()), - system_state: QString::from("unknown"), - user_state: QString::from("unknown"), - failed_count: 0, - failed_units_json: QString::from("[]"), - containers_json: QString::from("[]"), - } - } -} - -fn read_hostname() -> String { - std::fs::read_to_string("/etc/hostname") - .map(|s| s.trim().to_string()) - .unwrap_or_else(|_| "localhost".to_string()) -} - -fn rt() -> &'static Runtime { - static RT: OnceLock = OnceLock::new(); - RT.get_or_init(|| { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("tokio runtime") - }) -} - -async fn fetch_failed(bus: &Connection) -> (String, Vec) { - let mut state = String::from("unknown"); - let mut units = Vec::new(); - if let Ok(mgr) = SystemdManagerProxy::new(bus).await { - if let Ok(s) = mgr.system_state().await { - state = s; - } - if let Ok(u) = mgr.list_units_filtered(vec!["failed"]).await { - units = u; - } - } - (state, units) -} - -async fn poll_async() -> ( - String, - String, - Vec, - Vec, - Vec<(String, String, String)>, -) { - let mut sys_state = String::from("unknown"); - let mut sys_units = Vec::new(); - let mut user_state = String::from("unknown"); - let mut user_units = Vec::new(); - let mut machines = Vec::new(); - - if let Ok(c) = Connection::system().await { - let (s, u) = fetch_failed(&c).await; - sys_state = s; - sys_units = u; - if let Ok(m) = MachinedProxy::new(&c).await { - if let Ok(list) = m.list_machines().await { - machines = list - .into_iter() - .filter(|(name, _, _, _)| name != ".host") - .map(|(n, c, s, _)| (n, c, s)) - .collect(); - } - } - } - if let Ok(c) = Connection::session().await { - let (s, u) = fetch_failed(&c).await; - user_state = s; - user_units = u; - } - - (sys_state, user_state, sys_units, user_units, machines) -} - -fn unit_jsons<'a>(units: &'a [UnitTuple], scope: &'a str, machine: &'a str) -> Vec> { - units - .iter() - .map(|u| UnitJson { - name: &u.0, - description: &u.1, - sub_state: &u.4, - scope, - machine, - }) - .collect() -} - -impl cxx_qt::Initialize for qobject::SystemdService { - fn initialize(self: Pin<&mut Self>) { - self.poll(); - } -} - -impl qobject::SystemdService { - fn poll(mut self: Pin<&mut Self>) { - let (sys_state, user_state, sys_units, user_units, machines) = rt().block_on(poll_async()); - - let mut all_failed: Vec = Vec::with_capacity(sys_units.len() + user_units.len()); - all_failed.extend(unit_jsons(&sys_units, "system", "")); - all_failed.extend(unit_jsons(&user_units, "user", "")); - let count = all_failed.len() as i32; - let failed_json = serde_json::to_string(&all_failed).unwrap_or_else(|_| "[]".into()); - - let containers: Vec = machines - .iter() - .map(|(n, c, s)| ContainerJson { - name: n, - class: c, - service: s, - system_state: "unknown", - failed_units: Vec::new(), - }) - .collect(); - let containers_json = serde_json::to_string(&containers).unwrap_or_else(|_| "[]".into()); - - self.as_mut().set_system_state(QString::from(sys_state)); - self.as_mut().set_user_state(QString::from(user_state)); - self.as_mut() - .set_failed_units_json(QString::from(failed_json)); - self.as_mut().set_failed_count(count); - self.as_mut() - .set_containers_json(QString::from(containers_json)); - } - - fn restart_unit(self: Pin<&mut Self>, name: QString, scope: QString, machine: QString) { - let name = name.to_string(); - let scope = scope.to_string(); - let machine = machine.to_string(); - let _ = self; - rt().block_on(async move { - // Local-only restart for now. Container/remote restart comes later. - if !machine.is_empty() { - tracing::warn!(target: "nova_plugin", machine = %machine, "container restart not yet implemented"); - return; - } - let conn = match scope.as_str() { - "user" => Connection::session().await, - _ => Connection::system().await, - }; - let Ok(conn) = conn else { return }; - let Ok(mgr) = SystemdManagerProxy::new(&conn).await else { - return; - }; - if let Err(e) = mgr.restart_unit(&name, "replace").await { - tracing::warn!(target: "nova_plugin", unit = %name, error = %e, "restart_unit failed"); - } - }); - } -} diff --git a/shell/applets/MachinectlApplet.qml b/shell/applets/MachinectlApplet.qml new file mode 100644 index 0000000..62c7e0e --- /dev/null +++ b/shell/applets/MachinectlApplet.qml @@ -0,0 +1,354 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import "../services" as S +import NovaStats as NS + +Column { + id: root + + required property color accentColor + property bool active: true + + property bool _localExpanded: true + + // Localhost section header + Item { + width: root.width + height: 32 + + Rectangle { + anchors.fill: parent + anchors.leftMargin: 4 + anchors.rightMargin: 4 + color: _localHdrHover.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius + z: -1 + } + + HoverHandler { + id: _localHdrHover + } + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: " localhost" + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily + } + + Rectangle { + id: _localStateChip + anchors.right: _localChevron.left + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + visible: S.SystemdService.systemState !== "unknown" + color: { + const st = S.SystemdService.systemState; + if (st === "running") + return NS.ThemeService.base0B; + if (st === "degraded") + return NS.ThemeService.base0A; + return NS.ThemeService.base08; + } + opacity: 0.85 + radius: 3 + width: _localStateLbl.width + 8 + height: 14 + + Text { + id: _localStateLbl + anchors.centerIn: parent + text: S.SystemdService.systemState + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + } + } + + Text { + id: _localChevron + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: root._localExpanded ? "" : "" + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.iconFontFamily + } + + TapHandler { + onTapped: root._localExpanded = !root._localExpanded + } + } + + // Localhost expanded content + Column { + visible: root._localExpanded + width: root.width + + // System sub-section + Item { + width: root.width + height: 22 + + Text { + anchors.left: parent.left + anchors.leftMargin: 24 + anchors.verticalCenter: parent.verticalCenter + text: "SYSTEM" + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + font.letterSpacing: 1 + } + } + + Repeater { + model: S.SystemdService.systemUnits + delegate: SystemdUnitRow { + required property var modelData + unitName: modelData.name + description: modelData.description + subState: modelData.subState + isUser: false + machineName: "" + accentColor: root.accentColor + } + } + + Item { + visible: S.SystemdService.systemUnits.length === 0 + width: root.width + height: 22 + Text { + anchors.centerIn: parent + text: "no failures" + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily + } + } + + // User sub-section + Item { + width: root.width + height: 22 + + Text { + anchors.left: parent.left + anchors.leftMargin: 24 + anchors.verticalCenter: parent.verticalCenter + text: "USER" + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + font.letterSpacing: 1 + } + } + + Repeater { + model: S.SystemdService.userUnits + delegate: SystemdUnitRow { + required property var modelData + unitName: modelData.name + description: modelData.description + subState: modelData.subState + isUser: true + machineName: "" + accentColor: root.accentColor + } + } + + Item { + visible: S.SystemdService.userUnits.length === 0 + width: root.width + height: 22 + Text { + anchors.centerIn: parent + text: "no failures" + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily + } + } + } + + // Containers + Repeater { + model: S.MachinectlService.machines + + delegate: Column { + id: _machineSection + required property var modelData + required property int index + + property bool _expanded: false + property bool _loading: false + + width: root.width + + Connections { + target: S.MachinectlService + function onMachineReady(machineName) { + if (machineName === _machineSection.modelData.name) + _machineSection._loading = false; + } + } + + Separator {} + + // Machine header + Item { + width: _machineSection.width + height: 32 + + Rectangle { + anchors.fill: parent + anchors.leftMargin: 4 + anchors.rightMargin: 4 + color: _mHdrHover.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius + z: -1 + } + + HoverHandler { + id: _mHdrHover + } + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: " " + _machineSection.modelData.name + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily + elide: Text.ElideRight + width: parent.width - 100 + } + + Rectangle { + id: _mStateChip + anchors.right: _mChevron.left + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + visible: _machineSection._expanded && !_machineSection._loading + color: { + const st = S.MachinectlService.machineState(_machineSection.modelData.name); + if (st === "running") + return NS.ThemeService.base0B; + if (st === "degraded") + return NS.ThemeService.base0A; + return st === "unknown" ? "transparent" : NS.ThemeService.base08; + } + opacity: 0.85 + radius: 3 + width: _mStateLbl.width + 8 + height: 14 + + Text { + id: _mStateLbl + anchors.centerIn: parent + text: S.MachinectlService.machineState(_machineSection.modelData.name) + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + } + } + + Text { + id: _mChevron + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: _machineSection._expanded ? "" : "" + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.iconFontFamily + } + + TapHandler { + onTapped: { + _machineSection._expanded = !_machineSection._expanded; + if (_machineSection._expanded) { + _machineSection._loading = true; + S.MachinectlService.fetchMachine(_machineSection.modelData.name); + } + } + } + } + + // Machine expanded content + Column { + visible: _machineSection._expanded + width: _machineSection.width + + Item { + visible: _machineSection._loading + width: _machineSection.width + height: 28 + Text { + anchors.centerIn: parent + text: "loading..." + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily + } + } + + // System units inside the container + Item { + visible: !_machineSection._loading + width: _machineSection.width + height: 22 + + Text { + anchors.left: parent.left + anchors.leftMargin: 24 + anchors.verticalCenter: parent.verticalCenter + text: "SYSTEM" + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + font.letterSpacing: 1 + } + } + + Repeater { + model: !_machineSection._loading ? S.MachinectlService.machineUnits(_machineSection.modelData.name) : [] + delegate: SystemdUnitRow { + required property var modelData + unitName: modelData.name + description: modelData.description + subState: modelData.subState + isUser: false + machineName: _machineSection.modelData.name + accentColor: root.accentColor + } + } + + Item { + visible: !_machineSection._loading && S.MachinectlService.machineUnits(_machineSection.modelData.name).length === 0 + width: _machineSection.width + height: 22 + Text { + anchors.centerIn: parent + text: "no failures" + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily + } + } + } + } + } + + Item { + width: 1 + height: 4 + } +} diff --git a/shell/applets/SystemdApplet.qml b/shell/applets/SystemdApplet.qml index bd2f1aa..6462ed3 100644 --- a/shell/applets/SystemdApplet.qml +++ b/shell/applets/SystemdApplet.qml @@ -9,43 +9,156 @@ Column { required property color accentColor property bool active: true + // Emitted when content resizes; parent can connect to extend panel-close grace. signal contentResized - onHeightChanged: root.contentResized() - - // Local machine header + units - SystemdMachineSection { + // Section header: state label + unit count + Item { width: root.width - accentColor: root.accentColor - machineName: "" - title: S.SystemdService.hostname - marker: " this machine" - systemState: S.SystemdService.systemState - units: S.SystemdService.failedUnits - startExpanded: true + height: 28 + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: "SYSTEM" + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + font.letterSpacing: 1 + } + + Rectangle { + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + visible: S.SystemdService.systemState !== "unknown" + color: { + const st = S.SystemdService.systemState; + if (st === "running") + return NS.ThemeService.base0B; + if (st === "degraded") + return NS.ThemeService.base0A; + return NS.ThemeService.base08; + } + opacity: 0.85 + radius: 3 + width: _sysStateLbl.width + 8 + height: 14 + + Text { + id: _sysStateLbl + anchors.centerIn: parent + text: S.SystemdService.systemState + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + } + } } - // Containers Repeater { - model: S.SystemdService.containers + model: S.SystemdService.systemUnits - delegate: Column { - id: _row + delegate: SystemdUnitRow { required property var modelData - width: root.width + unitName: modelData.name + description: modelData.description + subState: modelData.subState + isUser: false + machineName: "" + accentColor: root.accentColor + onHeightChanged: root.contentResized() + } + } - Separator {} + Item { + visible: S.SystemdService.systemUnits.length === 0 + width: root.width + height: 24 - SystemdMachineSection { - width: _row.width - accentColor: root.accentColor - machineName: _row.modelData.name - title: _row.modelData.name - marker: "" - systemState: _row.modelData.systemState ?? "unknown" - units: _row.modelData.failedUnits ?? [] - startExpanded: (_row.modelData.failedUnits ?? []).length > 0 + Text { + anchors.centerIn: parent + text: "no failed system units" + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily + } + } + + Separator {} + + // User section + Item { + width: root.width + height: 28 + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: "USER" + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + font.letterSpacing: 1 + } + + Rectangle { + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + visible: S.SystemdService.userState !== "unknown" + color: { + const st = S.SystemdService.userState; + if (st === "running") + return NS.ThemeService.base0B; + if (st === "degraded") + return NS.ThemeService.base0A; + return NS.ThemeService.base08; } + opacity: 0.85 + radius: 3 + width: _userStateLbl.width + 8 + height: 14 + + Text { + id: _userStateLbl + anchors.centerIn: parent + text: S.SystemdService.userState + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + } + } + } + + Repeater { + model: S.SystemdService.userUnits + + delegate: SystemdUnitRow { + required property var modelData + unitName: modelData.name + description: modelData.description + subState: modelData.subState + isUser: true + machineName: "" + accentColor: root.accentColor + onHeightChanged: root.contentResized() + } + } + + Item { + visible: S.SystemdService.userUnits.length === 0 + width: root.width + height: 24 + + Text { + anchors.centerIn: parent + text: "no failed user units" + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } diff --git a/shell/applets/SystemdMachineSection.qml b/shell/applets/SystemdMachineSection.qml deleted file mode 100644 index 632c9db..0000000 --- a/shell/applets/SystemdMachineSection.qml +++ /dev/null @@ -1,111 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import "../services" as S -import NovaStats as NS - -// One section of the systemd applet: a header with title + state chip, -// expandable list of failed units underneath. -Column { - id: root - - required property color accentColor - required property string machineName - required property string title - required property string marker - required property string systemState - required property var units - property bool startExpanded: false - - width: parent?.width ?? 0 - - property bool _expanded: startExpanded - - Item { - width: root.width - height: 32 - - Rectangle { - anchors.fill: parent - anchors.leftMargin: 4 - anchors.rightMargin: 4 - color: _hdrHover.hovered ? NS.ThemeService.base02 : "transparent" - radius: NS.ThemeService.radius - z: -1 - } - - HoverHandler { - id: _hdrHover - } - - Text { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: _stateChip.left - anchors.rightMargin: 6 - anchors.verticalCenter: parent.verticalCenter - text: root.title + (root.marker !== "" ? " " + root.marker : "") - color: NS.ThemeService.base05 - font.pixelSize: NS.ThemeService.fontSize - font.family: NS.ThemeService.fontFamily - elide: Text.ElideRight - } - - Rectangle { - id: _stateChip - anchors.right: _chevron.left - anchors.rightMargin: 8 - anchors.verticalCenter: parent.verticalCenter - visible: root.systemState !== "unknown" - color: { - const st = root.systemState; - if (st === "running") - return NS.ThemeService.base0B; - if (st === "degraded") - return NS.ThemeService.base0A; - return NS.ThemeService.base08; - } - opacity: 0.85 - radius: 3 - width: _stateLbl.width + 8 - height: 14 - - Text { - id: _stateLbl - anchors.centerIn: parent - text: root.systemState - color: NS.ThemeService.base00 - font.pixelSize: NS.ThemeService.fontSize - 3 - font.family: NS.ThemeService.fontFamily - } - } - - Text { - id: _chevron - anchors.right: parent.right - anchors.rightMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: root._expanded ? "" : "" - color: NS.ThemeService.base04 - font.pixelSize: NS.ThemeService.fontSize - 2 - font.family: NS.ThemeService.iconFontFamily - } - - TapHandler { - onTapped: root._expanded = !root._expanded - } - } - - Repeater { - model: root._expanded ? root.units : [] - delegate: SystemdUnitRow { - required property var modelData - unitName: modelData.name - description: modelData.description ?? "" - subState: modelData.subState ?? "" - scope: modelData.scope ?? "system" - machineName: root.machineName - accentColor: root.accentColor - } - } -} diff --git a/shell/applets/SystemdUnitRow.qml b/shell/applets/SystemdUnitRow.qml index 4c4f2a0..e05d49f 100644 --- a/shell/applets/SystemdUnitRow.qml +++ b/shell/applets/SystemdUnitRow.qml @@ -8,95 +8,233 @@ Item { required property string unitName required property string description required property string subState - required property string scope - required property string machineName + required property bool isUser + property string machineName: "" required property color accentColor + property string _jText: "" + property bool _expanded: false + property bool _loading: false + width: parent?.width ?? 0 - height: 28 + height: _row.height + (_expanded ? _journalArea.height : 0) - HoverHandler { - id: _rowHover - } - - Rectangle { - anchors.fill: parent - anchors.leftMargin: 4 - anchors.rightMargin: 4 - color: _rowHover.hovered ? NS.ThemeService.base02 : "transparent" - radius: NS.ThemeService.radius - z: -1 - } - - Text { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: _subStateTag.left - anchors.rightMargin: 6 - anchors.verticalCenter: parent.verticalCenter - text: root.unitName - color: NS.ThemeService.base05 - font.pixelSize: NS.ThemeService.fontSize - 1 - font.family: NS.ThemeService.fontFamily - elide: Text.ElideRight - } - - Rectangle { - id: _subStateTag - anchors.right: _restartBtn.left - anchors.rightMargin: 6 - anchors.verticalCenter: parent.verticalCenter - color: NS.ThemeService.base08 - opacity: 0.85 - radius: 3 - width: _subStateLbl.width + 8 - height: 14 - - Text { - id: _subStateLbl - anchors.centerIn: parent - text: root.subState - color: NS.ThemeService.base00 - font.pixelSize: NS.ThemeService.fontSize - 3 - font.family: NS.ThemeService.fontFamily + Behavior on height { + NumberAnimation { + duration: 150 } } - // Restart button only visible while hovering the row. = fa-refresh + Connections { + target: S.SystemdService + enabled: root.machineName === "" + function onJournalReady(unitName, isUser, text) { + if (unitName === root.unitName && isUser === root.isUser) { + root._jText = text; + root._loading = false; + } + } + } + + Connections { + target: S.MachinectlService + enabled: root.machineName !== "" + function onMachineJournalReady(mname, unitName, text) { + if (mname === root.machineName && unitName === root.unitName) { + root._jText = text; + root._loading = false; + } + } + } + + function _fetchJournal() { + root._loading = true; + root._jText = ""; + if (root.machineName === "") + S.SystemdService.fetchJournal(root.unitName, root.isUser); + else + S.MachinectlService.fetchMachineJournal(root.machineName, root.unitName); + } + + function _doRestart() { + if (root.machineName === "") + S.SystemdService.restartUnit(root.unitName, root.isUser); + else + S.MachinectlService.restartMachineUnit(root.machineName, root.unitName); + } + + // Row Item { - id: _restartBtn - anchors.right: parent.right - anchors.rightMargin: 8 - anchors.verticalCenter: parent.verticalCenter - width: 24 - height: 24 - opacity: _rowHover.hovered ? 1 : 0 - Behavior on opacity { - NumberAnimation { - duration: 80 + id: _row + width: parent.width + height: 32 + + Rectangle { + anchors.fill: parent + anchors.leftMargin: 4 + anchors.rightMargin: 4 + color: _rowHover.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius + z: -1 + } + + HoverHandler { + id: _rowHover + } + + Text { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.right: _subStateTag.left + anchors.rightMargin: 6 + anchors.verticalCenter: parent.verticalCenter + text: root.unitName + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily + elide: Text.ElideRight + } + + Rectangle { + id: _subStateTag + anchors.right: _restartBtn.left + anchors.rightMargin: 6 + anchors.verticalCenter: parent.verticalCenter + color: NS.ThemeService.base08 + opacity: 0.85 + radius: 3 + width: _subStateLbl.width + 8 + height: 14 + + Text { + id: _subStateLbl + anchors.centerIn: parent + text: root.subState + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } - Text { - anchors.centerIn: parent - text: "" - color: _rHover.hovered ? root.accentColor : NS.ThemeService.base04 - font.pixelSize: NS.ThemeService.fontSize - font.family: NS.ThemeService.iconFontFamily - Behavior on color { - ColorAnimation { - duration: 80 + // Restart button -  = fa-refresh + Item { + id: _restartBtn + anchors.right: _expandBtn.left + anchors.rightMargin: 2 + anchors.verticalCenter: parent.verticalCenter + width: 24 + height: 24 + + Text { + anchors.centerIn: parent + text: "" + color: _rHover.hovered ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily + Behavior on color { + ColorAnimation { + duration: 80 + } + } + } + + HoverHandler { + id: _rHover + cursorShape: Qt.PointingHandCursor + } + TapHandler { + onTapped: { + root._doRestart(); + if (root._expanded) { + root._jText = ""; + Qt.callLater(root._fetchJournal); + } } } } - HoverHandler { - id: _rHover - cursorShape: Qt.PointingHandCursor + // Expand chevron -  = fa-chevron-down,  = fa-chevron-up + Item { + id: _expandBtn + anchors.right: parent.right + anchors.rightMargin: 4 + anchors.verticalCenter: parent.verticalCenter + width: 24 + height: 24 + + Text { + anchors.centerIn: parent + text: root._expanded ? "" : "" + color: _expHover.hovered ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.iconFontFamily + Behavior on color { + ColorAnimation { + duration: 80 + } + } + } + + HoverHandler { + id: _expHover + cursorShape: Qt.PointingHandCursor + } + TapHandler { + onTapped: { + root._expanded = !root._expanded; + if (root._expanded && root._jText === "") + root._fetchJournal(); + } + } } - TapHandler { - enabled: _rowHover.hovered - onTapped: S.SystemdService.restartUnit(root.unitName, root.scope, root.machineName) + } + + // Journal area + Item { + id: _journalArea + anchors.top: _row.bottom + width: parent.width + height: 120 + visible: root._expanded + clip: true + + Rectangle { + anchors.fill: parent + anchors.leftMargin: 8 + anchors.rightMargin: 8 + anchors.bottomMargin: 4 + color: NS.ThemeService.base01 + radius: NS.ThemeService.radius + + Text { + anchors.centerIn: parent + visible: root._loading + text: "loading..." + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily + } + + Flickable { + id: _flick + anchors.fill: parent + anchors.margins: 6 + visible: !root._loading + contentHeight: _jContent.height + clip: true + + Text { + id: _jContent + width: _flick.width + text: root._jText + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily + wrapMode: Text.WrapAnywhere + } + + onContentHeightChanged: contentY = Math.max(0, contentHeight - height) + } } } } diff --git a/shell/applets/qmldir b/shell/applets/qmldir index d1908e6..355dd6a 100644 --- a/shell/applets/qmldir +++ b/shell/applets/qmldir @@ -10,6 +10,7 @@ GpuApplet 1.0 GpuApplet.qml HexWaveBackground 1.0 HexWaveBackground.qml HoverableListItem 1.0 HoverableListItem.qml InfoRow 1.0 InfoRow.qml +MachinectlApplet 1.0 MachinectlApplet.qml MemoryApplet 1.0 MemoryApplet.qml MprisApplet 1.0 MprisApplet.qml NetworkApplet 1.0 NetworkApplet.qml @@ -18,7 +19,6 @@ PowerApplet 1.0 PowerApplet.qml Separator 1.0 Separator.qml SparklineCanvas 1.0 SparklineCanvas.qml SystemdApplet 1.0 SystemdApplet.qml -SystemdMachineSection 1.0 SystemdMachineSection.qml SystemdUnitRow 1.0 SystemdUnitRow.qml TemperatureApplet 1.0 TemperatureApplet.qml VolumeApplet 1.0 VolumeApplet.qml diff --git a/shell/modules/Bar.qml b/shell/modules/Bar.qml index 21a9009..36af284 100644 --- a/shell/modules/Bar.qml +++ b/shell/modules/Bar.qml @@ -187,6 +187,7 @@ PanelWindow { M.WeatherModule {} M.DiskModule {} M.SystemdModule {} + M.MachinectlModule {} } // Power + Dock diff --git a/shell/modules/MachinectlModule.qml b/shell/modules/MachinectlModule.qml new file mode 100644 index 0000000..6fa8197 --- /dev/null +++ b/shell/modules/MachinectlModule.qml @@ -0,0 +1,47 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell +import "." as M +import "../services" as S +import "../applets" as C +import NovaStats as NS + +M.BarModule { + id: root + active: NS.ModulesService.machinectlEnable + tooltip: { + const n = S.MachinectlService.machines.length; + return n === 0 ? "no containers" : n + " container" + (n === 1 ? "" : "s"); + } + panelNamespace: "nova-machinectl" + panelContentWidth: 320 + panelComponent: Component { + C.MachinectlApplet { + width: parent.width + accentColor: root.accentColor + active: root._showPanel + } + } + + Connections { + target: S.MachinectlService + function onMachineReady() { + root.keepPanelOpen(300); + } + } + + readonly property color _stateColor: S.MachinectlService.anyUnhealthy ? NS.ThemeService.base0A : root.accentColor + + M.BarIcon { + icon: "" + color: root._stateColor + anchors.verticalCenter: parent.verticalCenter + } + M.BarLabel { + label: S.MachinectlService.machines.length.toString() + minText: "9" + color: root._stateColor + anchors.verticalCenter: parent.verticalCenter + } +} diff --git a/shell/modules/SystemdModule.qml b/shell/modules/SystemdModule.qml index 582bcaf..b558592 100644 --- a/shell/modules/SystemdModule.qml +++ b/shell/modules/SystemdModule.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import QtQuick +import Quickshell import "." as M import "../services" as S import "../applets" as C @@ -10,11 +11,12 @@ M.BarModule { id: root active: NS.ModulesService.systemdEnable tooltip: { - const fc = S.SystemdService.failedCount; - return fc === 0 ? "systemd: ok" : "systemd: " + fc + " failed"; + const sys = S.SystemdService.systemState; + const fc = S.SystemdService.totalFailedCount; + return "systemd: " + sys + (fc > 0 ? " (" + fc + " failed)" : ""); } panelNamespace: "nova-systemd" - panelContentWidth: 320 + panelContentWidth: 300 panelComponent: Component { C.SystemdApplet { width: parent.width @@ -26,12 +28,22 @@ M.BarModule { Connections { target: S.SystemdService - function onFailedUnitsChanged() { + function onSystemUnitsChanged() { + root.keepPanelOpen(300); + } + function onUserUnitsChanged() { root.keepPanelOpen(300); } } - readonly property color _stateColor: S.SystemdService.failedCount > 0 ? NS.ThemeService.base0A : root.accentColor + readonly property color _stateColor: { + const st = S.SystemdService.systemState; + if (st === "running") + return root.accentColor; + if (st === "degraded") + return NS.ThemeService.base0A; + return NS.ThemeService.base08; + } M.BarIcon { icon: "" @@ -39,8 +51,8 @@ M.BarModule { anchors.verticalCenter: parent.verticalCenter } M.BarLabel { - label: S.SystemdService.failedCount > 0 ? S.SystemdService.failedCount + " failed" : "ok" - minText: "0 failed" + label: S.SystemdService.totalFailedCount > 0 ? S.SystemdService.totalFailedCount + " failed" : S.SystemdService.systemState + minText: "degraded" color: root._stateColor anchors.verticalCenter: parent.verticalCenter } diff --git a/shell/modules/qmldir b/shell/modules/qmldir index 87c86df..fbdec44 100644 --- a/shell/modules/qmldir +++ b/shell/modules/qmldir @@ -16,6 +16,7 @@ DockModule 1.0 DockModule.qml GpuModule 1.0 GpuModule.qml HoverPanel 1.0 HoverPanel.qml IdleInhibitorModule 1.0 IdleInhibitorModule.qml +MachinectlModule 1.0 MachinectlModule.qml MemoryModule 1.0 MemoryModule.qml MprisModule 1.0 MprisModule.qml NetworkModule 1.0 NetworkModule.qml diff --git a/shell/services/MachinectlService.qml b/shell/services/MachinectlService.qml new file mode 100644 index 0000000..30a39f0 --- /dev/null +++ b/shell/services/MachinectlService.qml @@ -0,0 +1,152 @@ +pragma Singleton + +import QtQuick +import Quickshell.Io +import "." as S +import NovaStats as NS + +QtObject { + id: root + + property var machines: [] + + // cache: machineName -> {state, units, loading} + property var _cache: ({}) + + readonly property bool anyUnhealthy: { + for (const k of Object.keys(_cache)) { + if ((_cache[k]?.units?.length ?? 0) > 0) + return true; + } + return false; + } + + function machineState(name) { + return _cache[name]?.state ?? "unknown"; + } + function machineUnits(name) { + return _cache[name]?.units ?? []; + } + function machineLoading(name) { + return _cache[name]?.loading ?? false; + } + + signal machineReady(string machineName, string state, var units) + signal machineJournalReady(string machineName, string unitName, string text) + + // Per-call state: kept on root (not on the inline Process objects) so qmllint can see them. + property string _machineName: "" + property string _journalMachine: "" + property string _journalUnit: "" + property string _restartMachine: "" + + function fetchMachine(name) { + const c = Object.assign({}, _cache); + c[name] = Object.assign({}, c[name] ?? {}, { + loading: true + }); + _cache = c; + + root._machineName = name; + _machineProc.command = ["sh", "-c", "busctl get-property --json=short --machine=" + name + " org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SystemState 2>/dev/null || echo '{}'; " + "busctl call --json=short --machine=" + name + " org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager ListUnitsFiltered as 1 failed 2>/dev/null || echo '{}'"]; + if (_machineProc.running) + _machineProc.running = false; + _machineProc.running = true; + } + + function fetchMachineJournal(machineName, unitName) { + root._journalMachine = machineName; + root._journalUnit = unitName; + _machineJournalProc.command = ["journalctl", "-M", machineName, "-u", unitName, "-n", "80", "--no-pager", "--output=short-precise"]; + if (_machineJournalProc.running) + _machineJournalProc.running = false; + _machineJournalProc.running = true; + } + + function restartMachineUnit(machineName, unitName) { + root._restartMachine = machineName; + _machineRestartProc.command = ["pkexec", "systemctl", "-M", machineName, "restart", unitName]; + if (_machineRestartProc.running) + _machineRestartProc.running = false; + _machineRestartProc.running = true; + } + + property Timer _poll: Timer { + interval: NS.ModulesService.machinectlInterval ?? 15000 + running: NS.ModulesService.machinectlEnable + repeat: true + triggeredOnStart: true + onTriggered: if (!root._listProc.running) + root._listProc.running = true + } + + property Process _listProc: Process { + command: ["busctl", "call", "--json=short", "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "ListMachines"] + stdout: StdioCollector { + onStreamFinished: { + try { + const parsed = JSON.parse(text.trim()); + const newMachines = (parsed.data?.[0] || []).map(m => ({ + name: m[0], + class: m[1], + service: m[2] + })); + root.machines = newMachines; + // drop stale cache entries + const names = new Set(newMachines.map(m => m.name)); + const c = {}; + for (const k of Object.keys(root._cache)) { + if (names.has(k)) + c[k] = root._cache[k]; + } + root._cache = c; + } catch (e) { + root.machines = []; + } + } + } + } + + // 2 lines of output: state, failed-units + property Process _machineProc: Process { + stdout: StdioCollector { + onStreamFinished: { + const lines = text.trim().split("\n"); + let state = "unknown"; + let units = []; + try { + state = JSON.parse(lines[0] ?? "").data || "unknown"; + } catch (e) {} + try { + const parsed = JSON.parse(lines[1] ?? ""); + units = (parsed.data?.[0] || []).map(u => ({ + name: u[0], + description: u[1], + loadState: u[2], + activeState: u[3], + subState: u[4] + })); + } catch (e) {} + const c = Object.assign({}, root._cache); + c[root._machineName] = { + state: state, + units: units, + loading: false + }; + root._cache = c; + root.machineReady(root._machineName, state, units); + } + } + } + + property Process _machineJournalProc: Process { + stdout: StdioCollector { + onStreamFinished: root.machineJournalReady(root._journalMachine, root._journalUnit, text) + } + } + + property Process _machineRestartProc: Process { + onRunningChanged: if (!running && root._restartMachine !== "") + root.fetchMachine(root._restartMachine) + } +} diff --git a/shell/services/SystemdService.qml b/shell/services/SystemdService.qml index 4ba5f38..afe9fcc 100644 --- a/shell/services/SystemdService.qml +++ b/shell/services/SystemdService.qml @@ -1,53 +1,100 @@ pragma Singleton import QtQuick +import Quickshell.Io +import "." as S import NovaStats as NS -// Thin wrapper around NS.SystemdService: drives the poll Timer and parses the -// JSON-encoded list properties into JS arrays for QML consumers. Restart helper -// proxies through to the Rust singleton. QtObject { id: root - readonly property string hostname: NS.SystemdService.hostname - readonly property string systemState: NS.SystemdService.systemState - readonly property string userState: NS.SystemdService.userState - readonly property int failedCount: NS.SystemdService.failedCount + property string systemState: "unknown" + property string userState: "unknown" + property var systemUnits: [] + property var userUnits: [] + readonly property int totalFailedCount: systemUnits.length + userUnits.length - // Parsed [{ name, description, subState, scope, machine }, ...] - readonly property var failedUnits: { - try { - return JSON.parse(NS.SystemdService.failedUnitsJson); - } catch (e) { - return []; - } - } - - // Parsed [{ name, class, service, systemState, failedUnits: [...] }, ...] - readonly property var containers: { - try { - return JSON.parse(NS.SystemdService.containersJson); - } catch (e) { - return []; - } - } - - function restartUnit(name, scope, machine) { - NS.SystemdService.restartUnit(name, scope ?? "system", machine ?? ""); - } + signal journalReady(string unitName, bool isUser, string text) function refresh() { - NS.SystemdService.poll(); + if (!_pollProc.running) + _pollProc.running = true; } - property Timer _pollTimer: Timer { - interval: { - const ms = NS.ModulesService.systemdInterval; - return ms > 0 ? ms : 15000; + // Per-fetch journal state: kept here (not on the Process) so qmllint can see them. + property string _journalUnitName: "" + property bool _journalIsUser: false + + function fetchJournal(unitName, isUser) { + _journalProc.command = isUser ? ["journalctl", "--user", "-u", unitName, "-n", "80", "--no-pager", "--output=short-precise"] : ["journalctl", "-u", unitName, "-n", "80", "--no-pager", "--output=short-precise"]; + root._journalUnitName = unitName; + root._journalIsUser = isUser; + if (_journalProc.running) + _journalProc.running = false; + _journalProc.running = true; + } + + function restartUnit(unitName, isUser) { + _restartProc.command = isUser ? ["systemctl", "--user", "restart", unitName] : ["pkexec", "systemctl", "restart", unitName]; + if (_restartProc.running) + _restartProc.running = false; + _restartProc.running = true; + } + + function _parseState(json) { + try { + return JSON.parse(json).data || "unknown"; + } catch (e) { + return "unknown"; } + } + + function _parseUnits(json) { + try { + const parsed = JSON.parse(json); + return (parsed.data?.[0] || []).map(u => ({ + name: u[0], + description: u[1], + loadState: u[2], + activeState: u[3], + subState: u[4] + })); + } catch (e) { + return []; + } + } + + property Timer _poll: Timer { + interval: NS.ModulesService.systemdInterval ?? 15000 running: NS.ModulesService.systemdEnable repeat: true - triggeredOnStart: false - onTriggered: NS.SystemdService.poll() + triggeredOnStart: true + onTriggered: if (!root._pollProc.running) + root._pollProc.running = true + } + + // 4 lines of output: systemState, systemUnits, userState, userUnits + property Process _pollProc: Process { + command: ["sh", "-c", "busctl get-property --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SystemState 2>/dev/null || echo '{}'; " + "busctl call --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager ListUnitsFiltered as 1 failed 2>/dev/null || echo '{}'; " + "busctl --user get-property --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SystemState 2>/dev/null || echo '{}'; " + "busctl --user call --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager ListUnitsFiltered as 1 failed 2>/dev/null || echo '{}'"] + stdout: StdioCollector { + onStreamFinished: { + const lines = text.trim().split("\n"); + root.systemState = root._parseState(lines[0] ?? ""); + root.systemUnits = root._parseUnits(lines[1] ?? ""); + root.userState = root._parseState(lines[2] ?? ""); + root.userUnits = root._parseUnits(lines[3] ?? ""); + } + } + } + + property Process _journalProc: Process { + stdout: StdioCollector { + onStreamFinished: root.journalReady(root._journalUnitName, root._journalIsUser, text) + } + } + + property Process _restartProc: Process { + onRunningChanged: if (!running) + root.refresh() } } diff --git a/shell/services/qmldir b/shell/services/qmldir index 59bfa3d..6169b75 100644 --- a/shell/services/qmldir +++ b/shell/services/qmldir @@ -8,6 +8,7 @@ singleton CpuService 1.0 CpuService.qml singleton DockState 1.0 DockState.qml singleton IdleInhibitService 1.0 IdleInhibitService.qml singleton LockService 1.0 LockService.qml +singleton MachinectlService 1.0 MachinectlService.qml singleton MprisService 1.0 MprisService.qml singleton NetworkService 1.0 NetworkService.qml singleton NiriIpc 1.0 NiriIpc.qml diff --git a/test/qmllint-baseline.txt b/test/qmllint-baseline.txt index 0baf41e..79d3638 100644 --- a/test/qmllint-baseline.txt +++ b/test/qmllint-baseline.txt @@ -36,6 +36,14 @@ shell/applets/HexWaveBackground.qml: Type "QColor" of property "base0E" not foun shell/applets/HoverableListItem.qml: Type "QColor" of property "base02" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/InfoRow.qml: Type "QColor" of property "base04" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/InfoRow.qml: Type "QColor" of property "base05" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base00" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base02" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base03" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base04" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base05" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base08" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base0A" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/MachinectlApplet.qml: Type "QColor" of property "base0B" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/MemoryApplet.qml: Type "QColor" of property "base02" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/MemoryApplet.qml: Type "QColor" of property "base03" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/MemoryApplet.qml: Type "QColor" of property "base0D" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] @@ -70,14 +78,13 @@ shell/applets/PowerApplet.qml: Type "QColor" of property "base0D" not found. Thi shell/applets/PowerApplet.qml: Type "QColor" of property "base0E" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/PowerApplet.qml: Unqualified access [unqualified] shell/applets/Separator.qml: Type "QColor" of property "base03" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] -shell/applets/SystemdMachineSection.qml: Type "QColor" of property "base00" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] -shell/applets/SystemdMachineSection.qml: Type "QColor" of property "base02" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] -shell/applets/SystemdMachineSection.qml: Type "QColor" of property "base04" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] -shell/applets/SystemdMachineSection.qml: Type "QColor" of property "base05" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] -shell/applets/SystemdMachineSection.qml: Type "QColor" of property "base08" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] -shell/applets/SystemdMachineSection.qml: Type "QColor" of property "base0A" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] -shell/applets/SystemdMachineSection.qml: Type "QColor" of property "base0B" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/SystemdApplet.qml: Type "QColor" of property "base00" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/SystemdApplet.qml: Type "QColor" of property "base03" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/SystemdApplet.qml: Type "QColor" of property "base08" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/SystemdApplet.qml: Type "QColor" of property "base0A" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/SystemdApplet.qml: Type "QColor" of property "base0B" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/SystemdUnitRow.qml: Type "QColor" of property "base00" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/SystemdUnitRow.qml: Type "QColor" of property "base01" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/SystemdUnitRow.qml: Type "QColor" of property "base02" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/SystemdUnitRow.qml: Type "QColor" of property "base04" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/applets/SystemdUnitRow.qml: Type "QColor" of property "base05" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] @@ -175,6 +182,7 @@ shell/modules/HoverPanel.qml: Type PanelWindow is not creatable. [uncreatable-ty shell/modules/HoverPanel.qml: Type margins is used but it is not resolved [unresolved-type] shell/modules/HoverPanel.qml: unknown grouped property scope margins. [unqualified] shell/modules/IdleInhibitorModule.qml: Type "QColor" of property "base09" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/modules/MachinectlModule.qml: Type "QColor" of property "base0A" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/modules/MemoryModule.qml: Unqualified access [unqualified] shell/modules/MprisModule.qml: Unqualified access [unqualified] shell/modules/NetworkModule.qml: Type "QColor" of property "base08" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] @@ -210,6 +218,7 @@ shell/modules/ScreenCorners.qml: Type PanelWindow is not creatable. [uncreatable shell/modules/ScreenCorners.qml: Type margins is used but it is not resolved [unresolved-type] shell/modules/ScreenCorners.qml: Unqualified access [unqualified] shell/modules/ScreenCorners.qml: unknown grouped property scope margins. [unqualified] +shell/modules/SystemdModule.qml: Type "QColor" of property "base08" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/modules/SystemdModule.qml: Type "QColor" of property "base0A" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/modules/TemperatureModule.qml: Type "QColor" of property "base08" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/modules/TemperatureModule.qml: Type "QColor" of property "base0A" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type]