From f34f3f2f4e24dd5850d1a2a9ad752cf80ec45174 Mon Sep 17 00:00:00 2001 From: Damocles Date: Mon, 4 May 2026 22:58:12 +0200 Subject: [PATCH] plugin: rust-side modules + theme services with serde-typed config --- flake.nix | 8 +- nix/plugin.nix | 3 +- plugin/Cargo.lock | 258 ++++++++- plugin/Cargo.toml | 5 + plugin/build.rs | 7 +- plugin/src/lib.rs | 12 +- plugin/src/modules_service.rs | 771 +++++++++++++++++++++++++ plugin/src/theme_service.rs | 414 +++++++++++++ shell/applets/AppletActionBar.qml | 3 +- shell/applets/BacklightApplet.qml | 13 +- shell/applets/BatteryApplet.qml | 33 +- shell/applets/BluetoothApplet.qml | 33 +- shell/applets/ClockApplet.qml | 29 +- shell/applets/CpuApplet.qml | 49 +- shell/applets/DiskApplet.qml | 17 +- shell/applets/GpuApplet.qml | 33 +- shell/applets/HexWaveBackground.qml | 9 +- shell/applets/HoverableListItem.qml | 5 +- shell/applets/InfoRow.qml | 13 +- shell/applets/MachinectlApplet.qml | 99 ++-- shell/applets/MemoryApplet.qml | 31 +- shell/applets/MprisApplet.qml | 61 +- shell/applets/NetworkApplet.qml | 31 +- shell/applets/NotifApplet.qml | 57 +- shell/applets/PowerApplet.qml | 55 +- shell/applets/Separator.qml | 3 +- shell/applets/SystemdApplet.qml | 49 +- shell/applets/SystemdUnitRow.qml | 47 +- shell/applets/TemperatureApplet.qml | 41 +- shell/applets/VolumeApplet.qml | 51 +- shell/applets/WeatherApplet.qml | 11 +- shell/dock/AppletDock.qml | 62 +- shell/dock/DockCard.qml | 29 +- shell/lock/LockClock.qml | 21 +- shell/lock/LockInput.qml | 21 +- shell/lock/LockNotifPills.qml | 19 +- shell/lock/LockSurface.qml | 33 +- shell/lock/LockWidgets.qml | 41 +- shell/modules/BackgroundOverlay.qml | 43 +- shell/modules/BacklightModule.qml | 5 +- shell/modules/Bar.qml | 33 +- shell/modules/BarGroup.qml | 21 +- shell/modules/BarIcon.qml | 7 +- shell/modules/BarLabel.qml | 7 +- shell/modules/BarModule.qml | 3 +- shell/modules/BatteryModule.qml | 7 +- shell/modules/BluetoothModule.qml | 7 +- shell/modules/ClockModule.qml | 7 +- shell/modules/CpuModule.qml | 5 +- shell/modules/DiskModule.qml | 11 +- shell/modules/DockModule.qml | 3 +- shell/modules/GpuModule.qml | 5 +- shell/modules/HoverPanel.qml | 9 +- shell/modules/IdleInhibitorModule.qml | 5 +- shell/modules/MachinectlModule.qml | 5 +- shell/modules/MemoryModule.qml | 5 +- shell/modules/MprisModule.qml | 5 +- shell/modules/NetworkModule.qml | 9 +- shell/modules/NotifCard.qml | 65 ++- shell/modules/NotifPopup.qml | 9 +- shell/modules/NotificationsModule.qml | 11 +- shell/modules/PopupBackground.qml | 9 +- shell/modules/PowerModule.qml | 3 +- shell/modules/PowerProfileModule.qml | 5 +- shell/modules/PrivacyModule.qml | 21 +- shell/modules/PulseAnimation.qml | 2 +- shell/modules/ScreenCorners.qml | 3 +- shell/modules/SystemdModule.qml | 7 +- shell/modules/TemperatureModule.qml | 13 +- shell/modules/Tooltip.qml | 15 +- shell/modules/TooltipState.qml | 3 +- shell/modules/TrayMenu.qml | 33 +- shell/modules/TrayModule.qml | 13 +- shell/modules/VolumeModule.qml | 7 +- shell/modules/WeatherModule.qml | 5 +- shell/modules/WindowTitleModule.qml | 5 +- shell/modules/WorkspacesModule.qml | 15 +- shell/services/BacklightService.qml | 3 +- shell/services/BatteryService.qml | 7 +- shell/services/BluetoothService.qml | 7 +- shell/services/CpuService.qml | 2 +- shell/services/LockService.qml | 3 +- shell/services/MachinectlService.qml | 5 +- shell/services/Modules.qml | 180 ------ shell/services/NetworkService.qml | 7 +- shell/services/NotifService.qml | 7 +- shell/services/PowerProfileService.qml | 7 +- shell/services/SystemStats.qml | 4 +- shell/services/SystemdService.qml | 5 +- shell/services/Theme.qml | 95 --- shell/services/ThemeUtil.qml | 23 + shell/services/WeatherService.qml | 9 +- shell/services/qmldir | 3 +- shell/shell.qml | 15 +- test/qmllint-baseline.txt | 183 ++++++ 95 files changed, 2477 insertions(+), 1011 deletions(-) create mode 100644 plugin/src/modules_service.rs create mode 100644 plugin/src/theme_service.rs delete mode 100644 shell/services/Modules.qml delete mode 100644 shell/services/Theme.qml create mode 100644 shell/services/ThemeUtil.qml diff --git a/flake.nix b/flake.nix index f9a60e1..a4d69af 100644 --- a/flake.nix +++ b/flake.nix @@ -69,7 +69,12 @@ formatter = forAllSystems ({ treefmt-eval, ... }: treefmt-eval.config.build.wrapper); packages = forAllSystems ( - { pkgs, rawPkgs, craneLib, ... }: + { + pkgs, + rawPkgs, + craneLib, + ... + }: let # Rebuild quickshell against patched Qt via its overlay qs = (pkgs.extend quickshell.overlays.default).quickshell.override { @@ -161,6 +166,7 @@ { pkgs, rawPkgs, + craneLib, treefmt-eval, }: { diff --git a/nix/plugin.nix b/nix/plugin.nix index c9ca355..331c0e3 100644 --- a/nix/plugin.nix +++ b/nix/plugin.nix @@ -56,8 +56,7 @@ let src = ../plugin; name = "nova-plugin-source"; filter = - path: type: - (baseNameOf path == "qt_plugin_exports.txt") || (craneLib.filterCargoSources path type); + path: type: (baseNameOf path == "qt_plugin_exports.txt") || (craneLib.filterCargoSources path type); }; commonArgs = { diff --git a/plugin/Cargo.lock b/plugin/Cargo.lock index 134d244..ffbddba 100644 --- a/plugin/Cargo.lock +++ b/plugin/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "anstyle" version = "1.0.14" @@ -38,7 +47,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "696283b40e1a39d208ee614b92e5f6521d16962edeb47c48372585ec92419943" dependencies = [ - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -161,7 +170,7 @@ dependencies = [ "cxx-qt-macro", "qt-build-utils", "static_assertions", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -252,6 +261,27 @@ dependencies = [ "syn", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -270,6 +300,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -319,16 +360,31 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom", + "getrandom 0.3.4", "libc", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "libc", +] + [[package]] name = "link-cplusplus" version = "1.0.12" @@ -350,6 +406,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44cd706ff0d503ee32b2071166510ca27e281228de10cd3aa8d35ff94560f81" +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.8.0" @@ -365,9 +436,41 @@ dependencies = [ "cxx-qt", "cxx-qt-build", "cxx-qt-lib", + "dirs", "libc", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "proc-macro2" version = "1.0.106" @@ -387,7 +490,7 @@ dependencies = [ "cc", "semver", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -405,6 +508,34 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + [[package]] name = "rustversion" version = "1.0.22" @@ -466,12 +597,27 @@ dependencies = [ "zmij", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "static_assertions" version = "1.1.0" @@ -510,7 +656,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -524,6 +679,87 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.24" @@ -548,6 +784,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" diff --git a/plugin/Cargo.toml b/plugin/Cargo.toml index cdf4ef5..f2fed37 100644 --- a/plugin/Cargo.toml +++ b/plugin/Cargo.toml @@ -18,6 +18,11 @@ cxx-qt = "0.8.1" cxx-qt-lib = { version = "0.8.1", features = ["qt_full"] } ctor = "0.13" libc = "0.2" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.149" +dirs = "6.0.0" +tracing = "0.1.44" +tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } [build-dependencies] cxx-qt-build = "0.8.1" diff --git a/plugin/build.rs b/plugin/build.rs index 064e486..4f4ed35 100644 --- a/plugin/build.rs +++ b/plugin/build.rs @@ -27,7 +27,12 @@ fn main() { // build a dummy crate that only compiles dependencies. Skip cxx-qt codegen in // that case - we just want cxx-qt-lib (the heavy dep) cached, not our own glue. let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); - let bridge_files = ["src/system_stats.rs", "src/cpu_service.rs"]; + let bridge_files = [ + "src/system_stats.rs", + "src/cpu_service.rs", + "src/modules_service.rs", + "src/theme_service.rs", + ]; if !bridge_files.iter().all(|p| manifest.join(p).exists()) { return; } diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index 6c837e5..7531222 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -1,8 +1,18 @@ pub mod cpu_service; +pub mod modules_service; pub mod stats; pub mod system_stats; +pub mod theme_service; #[ctor::ctor(unsafe)] fn on_dylib_load() { - eprintln!("[nova-plugin] dylib loaded (libNovaStats.so)"); + // Init tracing once at .so load. RUST_LOG-style filter via env, default WARN. + let _ = tracing_subscriber::fmt() + .with_writer(std::io::stderr) + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("warn")), + ) + .try_init(); + tracing::info!(target: "nova_plugin", "dylib loaded (libNovaStats.so)"); } diff --git a/plugin/src/modules_service.rs b/plugin/src/modules_service.rs new file mode 100644 index 0000000..aea0e77 --- /dev/null +++ b/plugin/src/modules_service.rs @@ -0,0 +1,771 @@ +//! Settings service: parses `$XDG_CONFIG_HOME/nova-shell/modules.json` once at +//! plugin load and exposes per-leaf primitive Q_PROPERTYs to QML. Defaults +//! mirror the previous QML implementation; a missing file yields all defaults +//! silently. Read errors / malformed JSON crash so misconfigurations are loud. +//! Unknown fields warn via `tracing` and are otherwise ignored. + +use core::pin::Pin; +use cxx_qt::CxxQtType; +use cxx_qt_lib::{QList, QString}; +use serde::Deserialize; +use std::path::PathBuf; + +#[cxx_qt::bridge] +pub mod qobject { + unsafe extern "C++" { + include!("cxx-qt-lib/qstring.h"); + type QString = cxx_qt_lib::QString; + + include!("cxx-qt-lib/core/qlist/qlist_QString.h"); + type QList_QString = cxx_qt_lib::QList; + } + + extern "RustQt" { + #[qobject] + #[qml_element] + #[qml_singleton] + // Stats polling interval (-1 = use default). + #[qproperty(i32, stats_daemon_interval, cxx_name = "statsDaemonInterval")] + // Per-module enables (mirrors `.enable`). + #[qproperty(bool, workspaces_enable, cxx_name = "workspacesEnable")] + #[qproperty(bool, tray_enable, cxx_name = "trayEnable")] + #[qproperty(bool, window_title_enable, cxx_name = "windowTitleEnable")] + #[qproperty(bool, clock_enable, cxx_name = "clockEnable")] + #[qproperty(bool, mpris_enable, cxx_name = "mprisEnable")] + #[qproperty(bool, volume_enable, cxx_name = "volumeEnable")] + #[qproperty(bool, bluetooth_enable, cxx_name = "bluetoothEnable")] + #[qproperty(bool, network_enable, cxx_name = "networkEnable")] + #[qproperty(bool, power_profile_enable, cxx_name = "powerProfileEnable")] + #[qproperty(bool, idle_inhibitor_enable, cxx_name = "idleInhibitorEnable")] + #[qproperty(bool, cpu_enable, cxx_name = "cpuEnable")] + #[qproperty(bool, memory_enable, cxx_name = "memoryEnable")] + #[qproperty(bool, privacy_enable, cxx_name = "privacyEnable")] + #[qproperty(bool, screen_corners_enable, cxx_name = "screenCornersEnable")] + #[qproperty(bool, power_enable, cxx_name = "powerEnable")] + #[qproperty(bool, background_overlay_enable, cxx_name = "backgroundOverlayEnable")] + #[qproperty(bool, overview_backdrop_enable, cxx_name = "overviewBackdropEnable")] + // Notifications. + #[qproperty(bool, notifications_enable, cxx_name = "notificationsEnable")] + #[qproperty(i32, notifications_timeout, cxx_name = "notificationsTimeout")] + #[qproperty(i32, notifications_max_popups, cxx_name = "notificationsMaxPopups")] + #[qproperty(i32, notifications_max_visible, cxx_name = "notificationsMaxVisible")] + #[qproperty(i32, notifications_max_history, cxx_name = "notificationsMaxHistory")] + // Backlight. + #[qproperty(bool, backlight_enable, cxx_name = "backlightEnable")] + #[qproperty(i32, backlight_step, cxx_name = "backlightStep")] + // Weather. + #[qproperty(bool, weather_enable, cxx_name = "weatherEnable")] + #[qproperty(QList_QString, weather_args, cxx_name = "weatherArgs")] + #[qproperty(i32, weather_interval, cxx_name = "weatherInterval")] + // Temperature. + #[qproperty(bool, temperature_enable, cxx_name = "temperatureEnable")] + #[qproperty(i32, temperature_warm, cxx_name = "temperatureWarm")] + #[qproperty(i32, temperature_hot, cxx_name = "temperatureHot")] + #[qproperty(QString, temperature_device, cxx_name = "temperatureDevice")] + // GPU. + #[qproperty(bool, gpu_enable, cxx_name = "gpuEnable")] + #[qproperty(i32, gpu_warm, cxx_name = "gpuWarm")] + #[qproperty(i32, gpu_hot, cxx_name = "gpuHot")] + // Disk. + #[qproperty(bool, disk_enable, cxx_name = "diskEnable")] + #[qproperty(i32, disk_interval, cxx_name = "diskInterval")] + #[qproperty(i32, disk_warn_threshold, cxx_name = "diskWarnThreshold")] + // Battery. + #[qproperty(bool, battery_enable, cxx_name = "batteryEnable")] + #[qproperty(bool, battery_poweralertd, cxx_name = "batteryPoweralertd")] + #[qproperty(i32, battery_warning, cxx_name = "batteryWarning")] + #[qproperty(i32, battery_critical, cxx_name = "batteryCritical")] + // Lock screen. + #[qproperty(bool, lock_enable, cxx_name = "lockEnable")] + #[qproperty(bool, lock_mpris, cxx_name = "lockMpris")] + #[qproperty(bool, lock_notifications, cxx_name = "lockNotifications")] + #[qproperty(bool, lock_screenshot, cxx_name = "lockScreenshot")] + #[qproperty(bool, lock_threat_effect, cxx_name = "lockThreatEffect")] + #[qproperty(bool, lock_volume, cxx_name = "lockVolume")] + #[qproperty(bool, lock_weather, cxx_name = "lockWeather")] + // Dock. + #[qproperty(bool, dock_enable, cxx_name = "dockEnable")] + #[qproperty(i32, dock_width, cxx_name = "dockWidth")] + #[qproperty(bool, dock_applet_clock, cxx_name = "dockAppletClock")] + #[qproperty(bool, dock_applet_cpu, cxx_name = "dockAppletCpu")] + #[qproperty(bool, dock_applet_gpu, cxx_name = "dockAppletGpu")] + #[qproperty(bool, dock_applet_memory, cxx_name = "dockAppletMemory")] + #[qproperty(bool, dock_applet_temperature, cxx_name = "dockAppletTemperature")] + #[qproperty(bool, dock_applet_disk, cxx_name = "dockAppletDisk")] + #[qproperty(bool, dock_applet_battery, cxx_name = "dockAppletBattery")] + #[qproperty(bool, dock_applet_network, cxx_name = "dockAppletNetwork")] + #[qproperty(bool, dock_applet_bluetooth, cxx_name = "dockAppletBluetooth")] + #[qproperty(bool, dock_applet_volume, cxx_name = "dockAppletVolume")] + #[qproperty(bool, dock_applet_backlight, cxx_name = "dockAppletBacklight")] + #[qproperty(bool, dock_applet_weather, cxx_name = "dockAppletWeather")] + #[qproperty(bool, dock_applet_mpris, cxx_name = "dockAppletMpris")] + #[qproperty(bool, dock_applet_notifications, cxx_name = "dockAppletNotifications")] + #[qproperty(bool, dock_applet_power, cxx_name = "dockAppletPower")] + // 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; + } + + impl cxx_qt::Initialize for ModulesService {} +} + +// ── Pure Rust data layer ───────────────────────────────────────────────────── +// One struct per group, plus a top-level `ModulesData`. Defaults mirror the +// previous QML implementation, NOT serde's type defaults. `#[serde(default)]` +// on every leaf so partial JSON objects get filled in from `Default`. + +mod data { + use super::Deserialize; + + fn t() -> bool { + true + } + fn weather_args_default() -> Vec { + vec!["--nerd".to_owned()] + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Plain { + #[serde(default = "t")] + pub enable: bool, + } + impl Default for Plain { + fn default() -> Self { + Self { enable: true } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Notifications { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "Notifications::d_timeout")] + pub timeout: i32, + #[serde(default = "Notifications::d_max_popups")] + pub max_popups: i32, + #[serde(default = "Notifications::d_max_visible")] + pub max_visible: i32, + #[serde(default = "Notifications::d_max_history")] + pub max_history: i32, + } + impl Notifications { + fn d_timeout() -> i32 { + 3000 + } + fn d_max_popups() -> i32 { + 4 + } + fn d_max_visible() -> i32 { + 10 + } + fn d_max_history() -> i32 { + -1 + } + } + impl Default for Notifications { + fn default() -> Self { + Self { + enable: true, + timeout: Self::d_timeout(), + max_popups: Self::d_max_popups(), + max_visible: Self::d_max_visible(), + max_history: Self::d_max_history(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Backlight { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "Backlight::d_step")] + pub step: i32, + } + impl Backlight { + fn d_step() -> i32 { + 5 + } + } + impl Default for Backlight { + fn default() -> Self { + Self { + enable: true, + step: Self::d_step(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Weather { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "weather_args_default")] + pub args: Vec, + #[serde(default = "Weather::d_interval")] + pub interval: i32, + } + impl Weather { + fn d_interval() -> i32 { + 3_600_000 + } + } + impl Default for Weather { + fn default() -> Self { + Self { + enable: true, + args: weather_args_default(), + interval: Self::d_interval(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Temperature { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "Temperature::d_warm")] + pub warm: i32, + #[serde(default = "Temperature::d_hot")] + pub hot: i32, + #[serde(default)] + pub device: String, + } + impl Temperature { + fn d_warm() -> i32 { + 80 + } + fn d_hot() -> i32 { + 90 + } + } + impl Default for Temperature { + fn default() -> Self { + Self { + enable: true, + warm: Self::d_warm(), + hot: Self::d_hot(), + device: String::new(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Gpu { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "Gpu::d_warm")] + pub warm: i32, + #[serde(default = "Gpu::d_hot")] + pub hot: i32, + } + impl Gpu { + fn d_warm() -> i32 { + 70 + } + fn d_hot() -> i32 { + 85 + } + } + impl Default for Gpu { + fn default() -> Self { + Self { + enable: true, + warm: Self::d_warm(), + hot: Self::d_hot(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Disk { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "Disk::d_interval")] + pub interval: i32, + #[serde(default = "Disk::d_warn_threshold")] + pub warn_threshold: i32, + } + impl Disk { + fn d_interval() -> i32 { + 30_000 + } + fn d_warn_threshold() -> i32 { + 85 + } + } + impl Default for Disk { + fn default() -> Self { + Self { + enable: true, + interval: Self::d_interval(), + warn_threshold: Self::d_warn_threshold(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Battery { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "t")] + pub poweralertd: bool, + #[serde(default = "Battery::d_warning")] + pub warning: i32, + #[serde(default = "Battery::d_critical")] + pub critical: i32, + } + impl Battery { + fn d_warning() -> i32 { + 25 + } + fn d_critical() -> i32 { + 15 + } + } + impl Default for Battery { + fn default() -> Self { + Self { + enable: true, + poweralertd: true, + warning: Self::d_warning(), + critical: Self::d_critical(), + } + } + } + + #[derive(Deserialize, Debug, Default)] + #[serde(rename_all = "camelCase")] + pub struct Lock { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "t")] + pub mpris: bool, + #[serde(default = "t")] + pub notifications: bool, + #[serde(default = "t")] + pub screenshot: bool, + #[serde(default = "t")] + pub threat_effect: bool, + #[serde(default = "t")] + pub volume: bool, + #[serde(default = "t")] + pub weather: bool, + } + + #[derive(Deserialize, Debug, Default)] + #[serde(rename_all = "camelCase")] + pub struct DockApplets { + #[serde(default = "t")] + pub clock: bool, + #[serde(default = "t")] + pub cpu: bool, + #[serde(default = "t")] + pub gpu: bool, + #[serde(default = "t")] + pub memory: bool, + #[serde(default = "t")] + pub temperature: bool, + #[serde(default = "t")] + pub disk: bool, + #[serde(default = "t")] + pub battery: bool, + #[serde(default = "t")] + pub network: bool, + #[serde(default = "t")] + pub bluetooth: bool, + #[serde(default = "t")] + pub volume: bool, + #[serde(default = "t")] + pub backlight: bool, + #[serde(default = "t")] + pub weather: bool, + #[serde(default = "t")] + pub mpris: bool, + #[serde(default = "t")] + pub notifications: bool, + #[serde(default = "t")] + pub power: bool, + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Dock { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "Dock::d_width")] + pub width: i32, + #[serde(default)] + pub applets: DockApplets, + } + impl Dock { + fn d_width() -> i32 { + 300 + } + } + impl Default for Dock { + fn default() -> Self { + Self { + enable: true, + width: Self::d_width(), + applets: DockApplets::default(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct WithInterval { + #[serde(default = "t")] + pub enable: bool, + #[serde(default = "WithInterval::d_interval")] + pub interval: i32, + } + impl WithInterval { + fn d_interval() -> i32 { + 15_000 + } + } + impl Default for WithInterval { + fn default() -> Self { + Self { + enable: true, + interval: Self::d_interval(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct StatsDaemon { + #[serde(default = "StatsDaemon::d_interval")] + pub interval: i32, + } + impl StatsDaemon { + fn d_interval() -> i32 { + -1 + } + } + impl Default for StatsDaemon { + fn default() -> Self { + Self { + interval: Self::d_interval(), + } + } + } + + #[derive(Deserialize, Debug, Default)] + #[serde(rename_all = "camelCase")] + pub struct ModulesData { + #[serde(default)] + pub workspaces: Plain, + #[serde(default)] + pub tray: Plain, + #[serde(default)] + pub window_title: Plain, + #[serde(default)] + pub clock: Plain, + #[serde(default)] + pub mpris: Plain, + #[serde(default)] + pub volume: Plain, + #[serde(default)] + pub bluetooth: Plain, + #[serde(default)] + pub network: Plain, + #[serde(default)] + pub power_profile: Plain, + #[serde(default)] + pub idle_inhibitor: Plain, + #[serde(default)] + pub cpu: Plain, + #[serde(default)] + pub memory: Plain, + #[serde(default)] + pub privacy: Plain, + #[serde(default)] + pub screen_corners: Plain, + #[serde(default)] + pub power: Plain, + #[serde(default)] + pub background_overlay: Plain, + #[serde(default)] + pub overview_backdrop: Plain, + #[serde(default)] + pub notifications: Notifications, + #[serde(default)] + pub backlight: Backlight, + #[serde(default)] + pub weather: Weather, + #[serde(default)] + pub temperature: Temperature, + #[serde(default)] + pub gpu: Gpu, + #[serde(default)] + pub disk: Disk, + #[serde(default)] + pub battery: Battery, + #[serde(default)] + pub lock: Lock, + #[serde(default)] + pub dock: Dock, + #[serde(default)] + pub systemd: WithInterval, + #[serde(default)] + pub machinectl: WithInterval, + #[serde(default)] + pub stats_daemon: StatsDaemon, + } +} + +use data::ModulesData; + +pub struct ModulesServiceRust { + stats_daemon_interval: i32, + workspaces_enable: bool, + tray_enable: bool, + window_title_enable: bool, + clock_enable: bool, + mpris_enable: bool, + volume_enable: bool, + bluetooth_enable: bool, + network_enable: bool, + power_profile_enable: bool, + idle_inhibitor_enable: bool, + cpu_enable: bool, + memory_enable: bool, + privacy_enable: bool, + screen_corners_enable: bool, + power_enable: bool, + background_overlay_enable: bool, + overview_backdrop_enable: bool, + notifications_enable: bool, + notifications_timeout: i32, + notifications_max_popups: i32, + notifications_max_visible: i32, + notifications_max_history: i32, + backlight_enable: bool, + backlight_step: i32, + weather_enable: bool, + weather_args: QList, + weather_interval: i32, + temperature_enable: bool, + temperature_warm: i32, + temperature_hot: i32, + temperature_device: QString, + gpu_enable: bool, + gpu_warm: i32, + gpu_hot: i32, + disk_enable: bool, + disk_interval: i32, + disk_warn_threshold: i32, + battery_enable: bool, + battery_poweralertd: bool, + battery_warning: i32, + battery_critical: i32, + lock_enable: bool, + lock_mpris: bool, + lock_notifications: bool, + lock_screenshot: bool, + lock_threat_effect: bool, + lock_volume: bool, + lock_weather: bool, + dock_enable: bool, + dock_width: i32, + dock_applet_clock: bool, + dock_applet_cpu: bool, + dock_applet_gpu: bool, + dock_applet_memory: bool, + dock_applet_temperature: bool, + dock_applet_disk: bool, + dock_applet_battery: bool, + dock_applet_network: bool, + dock_applet_bluetooth: bool, + dock_applet_volume: bool, + dock_applet_backlight: bool, + dock_applet_weather: bool, + dock_applet_mpris: bool, + dock_applet_notifications: bool, + dock_applet_power: bool, + systemd_enable: bool, + systemd_interval: i32, + machinectl_enable: bool, + machinectl_interval: i32, +} + +impl Default for ModulesServiceRust { + fn default() -> Self { + Self::from_data(load_modules_data()) + } +} + +impl ModulesServiceRust { + fn from_data(d: ModulesData) -> Self { + let mut weather_args = QList::::default(); + for s in &d.weather.args { + weather_args.append(QString::from(s.as_str())); + } + Self { + stats_daemon_interval: d.stats_daemon.interval, + workspaces_enable: d.workspaces.enable, + tray_enable: d.tray.enable, + window_title_enable: d.window_title.enable, + clock_enable: d.clock.enable, + mpris_enable: d.mpris.enable, + volume_enable: d.volume.enable, + bluetooth_enable: d.bluetooth.enable, + network_enable: d.network.enable, + power_profile_enable: d.power_profile.enable, + idle_inhibitor_enable: d.idle_inhibitor.enable, + cpu_enable: d.cpu.enable, + memory_enable: d.memory.enable, + privacy_enable: d.privacy.enable, + screen_corners_enable: d.screen_corners.enable, + power_enable: d.power.enable, + background_overlay_enable: d.background_overlay.enable, + overview_backdrop_enable: d.overview_backdrop.enable, + notifications_enable: d.notifications.enable, + notifications_timeout: d.notifications.timeout, + notifications_max_popups: d.notifications.max_popups, + notifications_max_visible: d.notifications.max_visible, + notifications_max_history: d.notifications.max_history, + backlight_enable: d.backlight.enable, + backlight_step: d.backlight.step, + weather_enable: d.weather.enable, + weather_args, + weather_interval: d.weather.interval, + temperature_enable: d.temperature.enable, + temperature_warm: d.temperature.warm, + temperature_hot: d.temperature.hot, + temperature_device: QString::from(d.temperature.device.as_str()), + gpu_enable: d.gpu.enable, + gpu_warm: d.gpu.warm, + gpu_hot: d.gpu.hot, + disk_enable: d.disk.enable, + disk_interval: d.disk.interval, + disk_warn_threshold: d.disk.warn_threshold, + battery_enable: d.battery.enable, + battery_poweralertd: d.battery.poweralertd, + battery_warning: d.battery.warning, + battery_critical: d.battery.critical, + lock_enable: d.lock.enable, + lock_mpris: d.lock.mpris, + lock_notifications: d.lock.notifications, + lock_screenshot: d.lock.screenshot, + lock_threat_effect: d.lock.threat_effect, + lock_volume: d.lock.volume, + lock_weather: d.lock.weather, + dock_enable: d.dock.enable, + dock_width: d.dock.width, + dock_applet_clock: d.dock.applets.clock, + dock_applet_cpu: d.dock.applets.cpu, + dock_applet_gpu: d.dock.applets.gpu, + dock_applet_memory: d.dock.applets.memory, + dock_applet_temperature: d.dock.applets.temperature, + dock_applet_disk: d.dock.applets.disk, + dock_applet_battery: d.dock.applets.battery, + dock_applet_network: d.dock.applets.network, + dock_applet_bluetooth: d.dock.applets.bluetooth, + dock_applet_volume: d.dock.applets.volume, + dock_applet_backlight: d.dock.applets.backlight, + dock_applet_weather: d.dock.applets.weather, + dock_applet_mpris: d.dock.applets.mpris, + dock_applet_notifications: d.dock.applets.notifications, + 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, + } + } +} + +pub(crate) fn config_path(file: &str) -> PathBuf { + let base = dirs::config_dir().unwrap_or_else(|| { + // Treat a missing HOME/XDG as a user error, not a silent default; matches + // the rule that perm/format issues should surface immediately. + panic!("[nova-plugin] could not resolve $XDG_CONFIG_HOME or $HOME for config lookup"); + }); + base.join("nova-shell").join(file) +} + +fn load_modules_data() -> ModulesData { + let path = config_path("modules.json"); + let raw = match std::fs::read_to_string(&path) { + Ok(s) => s, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + tracing::info!( + target: "nova_plugin::config", + "modules.json not found at {}, using defaults", path.display() + ); + return ModulesData::default(); + } + Err(e) => panic!("[nova-plugin] cannot read {}: {e}", path.display()), + }; + parse_modules(&raw, &path) +} + +fn parse_modules(raw: &str, path: &std::path::Path) -> ModulesData { + // First pass: catch unknown keys via a generic Value, warn but keep going. + if let Ok(serde_json::Value::Object(map)) = serde_json::from_str::(raw) { + let known: &[&str] = &[ + "workspaces", + "tray", + "windowTitle", + "clock", + "mpris", + "volume", + "bluetooth", + "network", + "powerProfile", + "idleInhibitor", + "cpu", + "memory", + "privacy", + "screenCorners", + "power", + "backgroundOverlay", + "overviewBackdrop", + "notifications", + "backlight", + "weather", + "temperature", + "gpu", + "disk", + "battery", + "lock", + "dock", + "systemd", + "machinectl", + "statsDaemon", + ]; + for key in map.keys() { + if !known.contains(&key.as_str()) { + tracing::warn!( + target: "nova_plugin::config", + "{}: unknown top-level key `{key}` ignored", path.display() + ); + } + } + } + + serde_json::from_str(raw) + .unwrap_or_else(|e| panic!("[nova-plugin] {}: malformed JSON: {e}", path.display())) +} + +impl cxx_qt::Initialize for qobject::ModulesService { + fn initialize(self: Pin<&mut Self>) { + // Default-construct populates from disk; nothing else to do. + let _ = self; + } +} diff --git a/plugin/src/theme_service.rs b/plugin/src/theme_service.rs new file mode 100644 index 0000000..a7327a4 --- /dev/null +++ b/plugin/src/theme_service.rs @@ -0,0 +1,414 @@ +//! Theme service: parses `$XDG_CONFIG_HOME/nova-shell/theme.json` once at plugin +//! load. Same file-handling rules as `modules_service`. Color values are exposed +//! as `QString` (#RRGGBB) which QML auto-coerces to `color` at the binding site. +//! +//! `reducedMotion` (which combines this config with PowerProfileService) and +//! `loadColor()` (a pure JS gradient helper) live in `services/ThemeUtil.qml` - +//! they need the QML-side PowerProfileService and don't fit a Rust singleton. + +use core::pin::Pin; +use cxx_qt::CxxQtType; +use cxx_qt_lib::{QColor, QString}; +use serde::Deserialize; + +#[cxx_qt::bridge] +pub mod qobject { + unsafe extern "C++" { + include!("cxx-qt-lib/qstring.h"); + type QString = cxx_qt_lib::QString; + + include!("cxx-qt-lib/qcolor.h"); + type QColor = cxx_qt_lib::QColor; + } + + extern "RustQt" { + #[qobject] + #[qml_element] + #[qml_singleton] + // base16 palette as proper QColor so QML gets `.r/.g/.b` access without Qt.color(). + #[qproperty(QColor, base00)] + #[qproperty(QColor, base01)] + #[qproperty(QColor, base02)] + #[qproperty(QColor, base03)] + #[qproperty(QColor, base04)] + #[qproperty(QColor, base05)] + #[qproperty(QColor, base06)] + #[qproperty(QColor, base07)] + #[qproperty(QColor, base08)] + #[qproperty(QColor, base09)] + #[qproperty(QColor, base0_a, cxx_name = "base0A")] + #[qproperty(QColor, base0_b, cxx_name = "base0B")] + #[qproperty(QColor, base0_c, cxx_name = "base0C")] + #[qproperty(QColor, base0_d, cxx_name = "base0D")] + #[qproperty(QColor, base0_e, cxx_name = "base0E")] + #[qproperty(QColor, base0_f, cxx_name = "base0F")] + // Typography. + #[qproperty(QString, font_family, cxx_name = "fontFamily")] + #[qproperty(QString, icon_font_family, cxx_name = "iconFontFamily")] + #[qproperty(i32, font_size, cxx_name = "fontSize")] + // Layout / decoration. + #[qproperty(f64, bar_opacity, cxx_name = "barOpacity")] + #[qproperty(i32, bar_height, cxx_name = "barHeight")] + #[qproperty(i32, bar_padding, cxx_name = "barPadding")] + #[qproperty(i32, module_spacing, cxx_name = "moduleSpacing")] + #[qproperty(i32, group_spacing, cxx_name = "groupSpacing")] + #[qproperty(i32, group_padding, cxx_name = "groupPadding")] + #[qproperty(i32, radius)] + #[qproperty(i32, screen_radius, cxx_name = "screenRadius")] + // Reduced-motion config flag (the effective value combines this with + // PowerProfileService.powerSaver - that combination lives in ThemeUtil.qml). + #[qproperty(bool, reduced_motion_config, cxx_name = "reducedMotionConfig")] + type ThemeService = super::ThemeServiceRust; + } + + impl cxx_qt::Initialize for ThemeService {} +} + +mod data { + use super::Deserialize; + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct Colors { + #[serde(default = "Colors::d_base00")] + pub base00: String, + #[serde(default = "Colors::d_base01")] + pub base01: String, + #[serde(default = "Colors::d_base02")] + pub base02: String, + #[serde(default = "Colors::d_base03")] + pub base03: String, + #[serde(default = "Colors::d_base04")] + pub base04: String, + #[serde(default = "Colors::d_base05")] + pub base05: String, + #[serde(default = "Colors::d_base06")] + pub base06: String, + #[serde(default = "Colors::d_base07")] + pub base07: String, + #[serde(default = "Colors::d_base08")] + pub base08: String, + #[serde(default = "Colors::d_base09")] + pub base09: String, + #[serde(default = "Colors::d_base0a", rename = "base0A")] + pub base0_a: String, + #[serde(default = "Colors::d_base0b", rename = "base0B")] + pub base0_b: String, + #[serde(default = "Colors::d_base0c", rename = "base0C")] + pub base0_c: String, + #[serde(default = "Colors::d_base0d", rename = "base0D")] + pub base0_d: String, + #[serde(default = "Colors::d_base0e", rename = "base0E")] + pub base0_e: String, + #[serde(default = "Colors::d_base0f", rename = "base0F")] + pub base0_f: String, + } + impl Colors { + // Defaults match Theme.qml's catppuccin-style starting palette. + fn d_base00() -> String { + "#1e1e2e".to_owned() + } + fn d_base01() -> String { + "#181825".to_owned() + } + fn d_base02() -> String { + "#313244".to_owned() + } + fn d_base03() -> String { + "#45475a".to_owned() + } + fn d_base04() -> String { + "#585b70".to_owned() + } + fn d_base05() -> String { + "#cdd6f4".to_owned() + } + fn d_base06() -> String { + "#f5e0dc".to_owned() + } + fn d_base07() -> String { + "#b4befe".to_owned() + } + fn d_base08() -> String { + "#f38ba8".to_owned() + } + fn d_base09() -> String { + "#fab387".to_owned() + } + fn d_base0a() -> String { + "#f9e2af".to_owned() + } + fn d_base0b() -> String { + "#a6e3a1".to_owned() + } + fn d_base0c() -> String { + "#94e2d5".to_owned() + } + fn d_base0d() -> String { + "#89b4fa".to_owned() + } + fn d_base0e() -> String { + "#cba6f7".to_owned() + } + fn d_base0f() -> String { + "#f2cdcd".to_owned() + } + } + impl Default for Colors { + fn default() -> Self { + Self { + base00: Self::d_base00(), + base01: Self::d_base01(), + base02: Self::d_base02(), + base03: Self::d_base03(), + base04: Self::d_base04(), + base05: Self::d_base05(), + base06: Self::d_base06(), + base07: Self::d_base07(), + base08: Self::d_base08(), + base09: Self::d_base09(), + base0_a: Self::d_base0a(), + base0_b: Self::d_base0b(), + base0_c: Self::d_base0c(), + base0_d: Self::d_base0d(), + base0_e: Self::d_base0e(), + base0_f: Self::d_base0f(), + } + } + } + + #[derive(Deserialize, Debug)] + #[serde(rename_all = "camelCase")] + pub struct ThemeData { + #[serde(default)] + pub colors: Colors, + #[serde(default = "ThemeData::d_font_family")] + pub font_family: String, + #[serde(default = "ThemeData::d_icon_font_family")] + pub icon_font_family: String, + #[serde(default = "ThemeData::d_font_size")] + pub font_size: i32, + #[serde(default = "ThemeData::d_bar_opacity")] + pub bar_opacity: f64, + #[serde(default = "ThemeData::d_bar_height")] + pub bar_height: i32, + #[serde(default = "ThemeData::d_bar_padding")] + pub bar_padding: i32, + #[serde(default = "ThemeData::d_module_spacing")] + pub module_spacing: i32, + #[serde(default = "ThemeData::d_group_spacing")] + pub group_spacing: i32, + #[serde(default = "ThemeData::d_group_padding")] + pub group_padding: i32, + #[serde(default = "ThemeData::d_radius")] + pub radius: i32, + #[serde(default = "ThemeData::d_screen_radius")] + pub screen_radius: i32, + #[serde(default)] + pub reduced_motion: bool, + } + impl ThemeData { + fn d_font_family() -> String { + "sans-serif".to_owned() + } + fn d_icon_font_family() -> String { + "Symbols Nerd Font".to_owned() + } + fn d_font_size() -> i32 { + 12 + } + fn d_bar_opacity() -> f64 { + 0.9 + } + fn d_bar_height() -> i32 { + 32 + } + fn d_bar_padding() -> i32 { + 8 + } + fn d_module_spacing() -> i32 { + 4 + } + fn d_group_spacing() -> i32 { + 6 + } + fn d_group_padding() -> i32 { + 8 + } + fn d_radius() -> i32 { + 4 + } + fn d_screen_radius() -> i32 { + 15 + } + } + impl Default for ThemeData { + fn default() -> Self { + Self { + colors: Colors::default(), + font_family: Self::d_font_family(), + icon_font_family: Self::d_icon_font_family(), + font_size: Self::d_font_size(), + bar_opacity: Self::d_bar_opacity(), + bar_height: Self::d_bar_height(), + bar_padding: Self::d_bar_padding(), + module_spacing: Self::d_module_spacing(), + group_spacing: Self::d_group_spacing(), + group_padding: Self::d_group_padding(), + radius: Self::d_radius(), + screen_radius: Self::d_screen_radius(), + reduced_motion: false, + } + } + } +} + +use data::ThemeData; + +pub struct ThemeServiceRust { + base00: QColor, + base01: QColor, + base02: QColor, + base03: QColor, + base04: QColor, + base05: QColor, + base06: QColor, + base07: QColor, + base08: QColor, + base09: QColor, + base0_a: QColor, + base0_b: QColor, + base0_c: QColor, + base0_d: QColor, + base0_e: QColor, + base0_f: QColor, + font_family: QString, + icon_font_family: QString, + font_size: i32, + bar_opacity: f64, + bar_height: i32, + bar_padding: i32, + module_spacing: i32, + group_spacing: i32, + group_padding: i32, + radius: i32, + screen_radius: i32, + reduced_motion_config: bool, +} + +impl Default for ThemeServiceRust { + fn default() -> Self { + Self::from_data(load_theme_data()) + } +} + +/// Parse a `#RRGGBB` (or `#AARRGGBB`) hex string into a `QColor`. Panics on +/// malformed input - file-level malformed JSON already does this; reaching +/// here with a bad hex means the user wrote `"colors": {"base00": "garbage"}` +/// which we'd rather flag immediately than render as default magenta. +fn parse_hex(s: &str) -> QColor { + let h = s.trim_start_matches('#'); + let v = u32::from_str_radix(h, 16) + .unwrap_or_else(|_| panic!("[nova-plugin] theme.json: invalid color `{s}`")); + match h.len() { + 6 => QColor::from_rgb( + ((v >> 16) & 0xff) as i32, + ((v >> 8) & 0xff) as i32, + (v & 0xff) as i32, + ), + 8 => QColor::from_rgba( + ((v >> 16) & 0xff) as i32, + ((v >> 8) & 0xff) as i32, + (v & 0xff) as i32, + ((v >> 24) & 0xff) as i32, + ), + _ => panic!("[nova-plugin] theme.json: color `{s}` must be #RRGGBB or #AARRGGBB"), + } +} + +impl ThemeServiceRust { + fn from_data(d: ThemeData) -> Self { + let s = |x: &str| QString::from(x); + let c = parse_hex; + Self { + base00: c(&d.colors.base00), + base01: c(&d.colors.base01), + base02: c(&d.colors.base02), + base03: c(&d.colors.base03), + base04: c(&d.colors.base04), + base05: c(&d.colors.base05), + base06: c(&d.colors.base06), + base07: c(&d.colors.base07), + base08: c(&d.colors.base08), + base09: c(&d.colors.base09), + base0_a: c(&d.colors.base0_a), + base0_b: c(&d.colors.base0_b), + base0_c: c(&d.colors.base0_c), + base0_d: c(&d.colors.base0_d), + base0_e: c(&d.colors.base0_e), + base0_f: c(&d.colors.base0_f), + font_family: s(&d.font_family), + icon_font_family: s(&d.icon_font_family), + font_size: d.font_size, + bar_opacity: d.bar_opacity, + bar_height: d.bar_height, + bar_padding: d.bar_padding, + module_spacing: d.module_spacing, + group_spacing: d.group_spacing, + group_padding: d.group_padding, + radius: d.radius, + screen_radius: d.screen_radius, + reduced_motion_config: d.reduced_motion, + } + } +} + +fn load_theme_data() -> ThemeData { + let path = crate::modules_service::config_path("theme.json"); + let raw = match std::fs::read_to_string(&path) { + Ok(s) => s, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + tracing::info!( + target: "nova_plugin::config", + "theme.json not found at {}, using defaults", path.display() + ); + return ThemeData::default(); + } + Err(e) => panic!("[nova-plugin] cannot read {}: {e}", path.display()), + }; + + if let Ok(serde_json::Value::Object(map)) = serde_json::from_str::(&raw) { + let known: &[&str] = &[ + "colors", + "fontFamily", + "iconFontFamily", + "fontSize", + "barOpacity", + "barHeight", + "barPadding", + "moduleSpacing", + "groupSpacing", + "groupPadding", + "radius", + "screenRadius", + "reducedMotion", + ]; + for key in map.keys() { + if !known.contains(&key.as_str()) { + tracing::warn!( + target: "nova_plugin::config", + "{}: unknown top-level key `{key}` ignored", path.display() + ); + } + } + } + + serde_json::from_str(&raw) + .unwrap_or_else(|e| panic!("[nova-plugin] {}: malformed JSON: {e}", path.display())) +} + +impl cxx_qt::Initialize for qobject::ThemeService { + fn initialize(self: Pin<&mut Self>) { + let _ = self; + } +} diff --git a/shell/applets/AppletActionBar.qml b/shell/applets/AppletActionBar.qml index 0852a51..9b2581e 100644 --- a/shell/applets/AppletActionBar.qml +++ b/shell/applets/AppletActionBar.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS // Action bar for applet-level controls (power toggles, DND, clear-all, etc.) // Place at the top of an applet Column. Children are right-aligned action buttons. @@ -26,6 +27,6 @@ Item { anchors.right: parent.right anchors.bottom: parent.bottom height: 1 - color: S.Theme.base03 + color: NS.ThemeService.base03 } } diff --git a/shell/applets/BacklightApplet.qml b/shell/applets/BacklightApplet.qml index c9a60f5..708ed78 100644 --- a/shell/applets/BacklightApplet.qml +++ b/shell/applets/BacklightApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Item { id: root @@ -18,8 +19,8 @@ Item { anchors.verticalCenter: parent.verticalCenter text: "\uF185" color: root.accentColor - font.pixelSize: S.Theme.fontSize + 2 - font.family: S.Theme.iconFontFamily + font.pixelSize: NS.ThemeService.fontSize + 2 + font.family: NS.ThemeService.iconFontFamily } Item { @@ -33,7 +34,7 @@ Item { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 3 } @@ -71,9 +72,9 @@ Item { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root.percent + "%" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily width: 30 } } diff --git a/shell/applets/BatteryApplet.qml b/shell/applets/BatteryApplet.qml index 66800a4..d7cbce6 100644 --- a/shell/applets/BatteryApplet.qml +++ b/shell/applets/BatteryApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -8,7 +9,7 @@ Column { property bool active: true - readonly property color _stateColor: S.BatteryService.charging ? S.Theme.base0B : S.BatteryService.critical ? S.Theme.base09 : S.BatteryService.percent < S.BatteryService.warnThresh ? S.Theme.base0A : root.accentColor + readonly property color _stateColor: S.BatteryService.charging ? NS.ThemeService.base0B : S.BatteryService.critical ? NS.ThemeService.base09 : S.BatteryService.percent < S.BatteryService.warnThresh ? NS.ThemeService.base0A : root.accentColor // Header - pct + time Item { @@ -25,8 +26,8 @@ Column { return Math.round(S.BatteryService.percent) + "%" + (ts ? " " + ts : ""); } color: root._stateColor - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true } } @@ -46,7 +47,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 3 } @@ -70,7 +71,7 @@ Column { width: 1 height: parent.height + 4 anchors.verticalCenter: parent.verticalCenter - color: S.Theme.base0A + color: NS.ThemeService.base0A opacity: 0.6 } @@ -80,7 +81,7 @@ Column { width: 1 height: parent.height + 4 anchors.verticalCenter: parent.verticalCenter - color: S.Theme.base08 + color: NS.ThemeService.base08 opacity: 0.6 } } @@ -99,11 +100,11 @@ Column { thresholds: [ { value: S.BatteryService.warnThresh, - color: S.Theme.base0A + color: NS.ThemeService.base0A }, { value: S.BatteryService.critThresh, - color: S.Theme.base08 + color: NS.ThemeService.base08 } ] } @@ -118,9 +119,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "warn " + S.BatteryService.warnThresh + "% crit " + S.BatteryService.critThresh + "%" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 0.5 } @@ -129,9 +130,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "24h" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } @@ -159,7 +160,7 @@ Column { height: 24 history: S.BatteryService.rateHistory strokeColor: root._stateColor - colorAt: v => v >= 0 ? S.Theme.base0B : root._stateColor + colorAt: v => v >= 0 ? NS.ThemeService.base0B : root._stateColor active: root.active maxValue: null minValue: null @@ -173,7 +174,7 @@ Column { value: Math.round(S.BatteryService.healthPercent) + "%" valueColor: { const h = S.BatteryService.healthPercent; - return h < 50 ? S.Theme.base08 : h < 75 ? S.Theme.base0A : S.Theme.base0B; + return h < 50 ? NS.ThemeService.base08 : h < 75 ? NS.ThemeService.base0A : NS.ThemeService.base0B; } } diff --git a/shell/applets/BluetoothApplet.qml b/shell/applets/BluetoothApplet.qml index dfc6f18..9742a39 100644 --- a/shell/applets/BluetoothApplet.qml +++ b/shell/applets/BluetoothApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS // NOT safe for lock screen - can toggle bluetooth power and connect/disconnect devices Column { @@ -20,9 +21,9 @@ Column { Text { anchors.centerIn: parent text: "\uF011" - color: S.BluetoothService.enabled ? root.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily + color: S.BluetoothService.enabled ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily Behavior on color { ColorAnimation { @@ -57,9 +58,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "\uF294" - color: entry._pending || entry.modelData.connected ? root.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize + 1 - font.family: S.Theme.iconFontFamily + color: entry._pending || entry.modelData.connected ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + 1 + font.family: NS.ThemeService.iconFontFamily } Text { @@ -69,9 +70,9 @@ Column { anchors.rightMargin: 4 anchors.verticalCenter: parent.verticalCenter text: entry.modelData.name - color: entry._pending || entry.modelData.connected ? root.accentColor : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: entry._pending || entry.modelData.connected ? root.accentColor : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: entry.modelData.connected || entry._pending elide: Text.ElideRight } @@ -82,9 +83,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: entry._pending ? (entry.modelData.connected ? "disconnecting..." : "connecting...") : (entry.modelData.battery >= 0 ? entry.modelData.battery + "%" : "") - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily font.italic: entry._pending width: text ? implicitWidth : 0 } @@ -92,7 +93,7 @@ Column { // Pulse animation while pending SequentialAnimation on opacity { loops: Animation.Infinite - running: entry._pending && !S.Theme.reducedMotion + running: entry._pending && !S.ThemeUtil.reducedMotion NumberAnimation { to: 0.5 duration: 400 @@ -121,8 +122,8 @@ Column { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: S.BluetoothService.enabled ? "No paired devices" : "Bluetooth is off" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } } diff --git a/shell/applets/ClockApplet.qml b/shell/applets/ClockApplet.qml index ac46380..30bc0e8 100644 --- a/shell/applets/ClockApplet.qml +++ b/shell/applets/ClockApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -64,8 +65,8 @@ Column { anchors.verticalCenter: parent.verticalCenter text: root._locale.standaloneMonthName(root._month, Locale.LongFormat) + " " + root._year color: root.accentColor - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true } @@ -74,9 +75,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "W" + root._weekNumber(root.currentDate) - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } @@ -91,9 +92,9 @@ Column { height: 18 horizontalAlignment: Text.AlignHCenter text: modelData - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.bold: true } } @@ -121,9 +122,9 @@ Column { Text { anchors.centerIn: parent text: modelData.day - color: modelData.isToday ? S.Theme.base00 : modelData.isCurrentMonth ? S.Theme.base05 : S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: modelData.isToday ? NS.ThemeService.base00 : modelData.isCurrentMonth ? NS.ThemeService.base05 : NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily font.bold: modelData.isToday } } @@ -136,9 +137,9 @@ Column { anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter text: root.currentDate.toLocaleDateString(root._locale, Locale.LongFormat) - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } Item { diff --git a/shell/applets/CpuApplet.qml b/shell/applets/CpuApplet.qml index 6b2123e..61e297a 100644 --- a/shell/applets/CpuApplet.qml +++ b/shell/applets/CpuApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -32,7 +33,7 @@ Column { readonly property int _u: S.CpuService.cores[index]?.usage ?? 0 readonly property real _f: S.CpuService.cores[index]?.freq_ghz ?? 0 - readonly property color _barColor: S.Theme.loadColor(_u) + readonly property color _barColor: S.ThemeUtil.loadColor(_u) readonly property bool _throttled: { const maxF = S.CpuService.coreMaxFreq[index] ?? 0; return maxF > 0 && _f < maxF * 0.85 && _u >= 60; @@ -56,7 +57,7 @@ Column { anchors.horizontalCenter: parent.horizontalCenter width: parent.width - 16 height: 1 - color: S.Theme.base03 + color: NS.ThemeService.base03 } // Row content pinned to bottom of delegate @@ -71,9 +72,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: index - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily width: 16 } @@ -88,7 +89,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 2 } @@ -115,7 +116,7 @@ Column { height: 10 history: S.CpuService.cores[parent.parent.index]?.history ?? [] strokeColor: parent.parent._barColor - colorAt: v => S.Theme.loadColor(v) + colorAt: v => S.ThemeUtil.loadColor(v) active: root.active } @@ -125,9 +126,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: parent.parent._f.toFixed(2) - color: parent.parent._throttled ? S.Theme.base08 : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: parent.parent._throttled ? NS.ThemeService.base08 : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily width: 34 horizontalAlignment: Text.AlignRight } @@ -141,7 +142,7 @@ Column { InfoRow { label: "Total" value: S.CpuService.usage + "% @ " + S.CpuService.freqGhz.toFixed(2) + " GHz" - valueColor: S.Theme.loadColor(S.CpuService.usage) + valueColor: S.ThemeUtil.loadColor(S.CpuService.usage) } SparklineCanvas { @@ -152,7 +153,7 @@ Column { height: 32 history: S.CpuService.history strokeColor: root.accentColor - colorAt: v => S.Theme.loadColor(v) + colorAt: v => S.ThemeUtil.loadColor(v) active: root.active } @@ -172,9 +173,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "PROCESS" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } @@ -183,9 +184,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "CPU" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } } @@ -203,9 +204,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.cmd - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight width: parent.width - 80 } @@ -215,9 +216,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.cpu.toFixed(1) + "%" - color: S.Theme.loadColor(modelData.cpu) - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: S.ThemeUtil.loadColor(modelData.cpu) + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily width: 36 horizontalAlignment: Text.AlignRight } diff --git a/shell/applets/DiskApplet.qml b/shell/applets/DiskApplet.qml index 40ca390..792bc13 100644 --- a/shell/applets/DiskApplet.qml +++ b/shell/applets/DiskApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -31,9 +32,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.target - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight width: 72 } @@ -49,14 +50,14 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 2 } Rectangle { width: parent.width * (modelData.pct / 100) height: parent.height - color: S.Theme.loadColor(modelData.pct) + color: S.ThemeUtil.loadColor(modelData.pct) radius: 2 Behavior on width { NumberAnimation { @@ -72,9 +73,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._fmt(modelData.usedBytes) + "/" + root._fmt(modelData.totalBytes) - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily width: 72 horizontalAlignment: Text.AlignRight } diff --git a/shell/applets/GpuApplet.qml b/shell/applets/GpuApplet.qml index 09fab4d..afd49c1 100644 --- a/shell/applets/GpuApplet.qml +++ b/shell/applets/GpuApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -22,9 +23,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: S.SystemStats.gpuVendor.toUpperCase() - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } @@ -33,9 +34,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: S.SystemStats.gpuUsage + "%" - color: S.Theme.loadColor(S.SystemStats.gpuUsage) - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: S.ThemeUtil.loadColor(S.SystemStats.gpuUsage) + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true } } @@ -55,14 +56,14 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 3 } Rectangle { width: parent.width * Math.min(1, S.SystemStats.gpuUsage / 100) height: parent.height - color: S.Theme.loadColor(S.SystemStats.gpuUsage) + color: S.ThemeUtil.loadColor(S.SystemStats.gpuUsage) radius: 3 Behavior on width { enabled: root.active @@ -84,7 +85,7 @@ Column { height: 32 history: S.SystemStats.gpuHistory strokeColor: root.accentColor - colorAt: v => S.Theme.loadColor(v) + colorAt: v => S.ThemeUtil.loadColor(v) active: root.active } @@ -100,9 +101,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "VRAM" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } @@ -112,8 +113,8 @@ Column { anchors.verticalCenter: parent.verticalCenter text: root._fmt(S.SystemStats.gpuVramUsedGb) + " / " + root._fmt(S.SystemStats.gpuVramTotalGb) color: root.accentColor - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily font.bold: true } } @@ -132,7 +133,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 2 } @@ -158,7 +159,7 @@ Column { height: 22 label: "Temp" value: S.SystemStats.gpuTempC + "\u00B0C" - valueColor: S.SystemStats.gpuTempC > S.Modules.gpu.hot ? S.Theme.base08 : S.SystemStats.gpuTempC > S.Modules.gpu.warm ? S.Theme.base0A : S.Theme.base05 + valueColor: S.SystemStats.gpuTempC > NS.ModulesService.gpuHot ? NS.ThemeService.base08 : S.SystemStats.gpuTempC > NS.ModulesService.gpuWarm ? NS.ThemeService.base0A : NS.ThemeService.base05 } Item { diff --git a/shell/applets/HexWaveBackground.qml b/shell/applets/HexWaveBackground.qml index 7d61a58..bdee619 100644 --- a/shell/applets/HexWaveBackground.qml +++ b/shell/applets/HexWaveBackground.qml @@ -1,12 +1,13 @@ import QtQuick import Quickshell import "../services" as S +import NovaStats as NS Item { id: root property bool running: false - property bool reducedMotion: S.Theme.reducedMotion + property bool reducedMotion: S.ThemeUtil.reducedMotion readonly property real wavePhase: fx.uWavePhase ShaderEffect { @@ -22,9 +23,9 @@ Item { property real uGlitch: 0 property real uGlitchSeed: 0.0 property vector4d uResolution: Qt.vector4d(width, height, 0, 0) - property color uC0: S.Theme.base0C - property color uC1: S.Theme.base0E - property color uC2: S.Theme.base09 + property color uC0: NS.ThemeService.base0C + property color uC1: NS.ThemeService.base0E + property color uC2: NS.ThemeService.base09 Connections { target: root diff --git a/shell/applets/HoverableListItem.qml b/shell/applets/HoverableListItem.qml index 2bb7262..894ed46 100644 --- a/shell/applets/HoverableListItem.qml +++ b/shell/applets/HoverableListItem.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Item { id: root @@ -14,8 +15,8 @@ Item { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: root.hovered ? S.Theme.base02 : "transparent" - radius: S.Theme.radius + color: root.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius z: -1 } diff --git a/shell/applets/InfoRow.qml b/shell/applets/InfoRow.qml index 68047ca..67dfeee 100644 --- a/shell/applets/InfoRow.qml +++ b/shell/applets/InfoRow.qml @@ -1,12 +1,13 @@ import QtQuick import "../services" as S +import NovaStats as NS Item { id: root required property string label required property string value - property color valueColor: S.Theme.base05 + property color valueColor: NS.ThemeService.base05 width: parent?.width ?? 0 height: 18 @@ -16,9 +17,9 @@ Item { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root.label - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } Text { @@ -27,7 +28,7 @@ Item { anchors.verticalCenter: parent.verticalCenter text: root.value color: root.valueColor - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } diff --git a/shell/applets/MachinectlApplet.qml b/shell/applets/MachinectlApplet.qml index 68d5296..62c7e0e 100644 --- a/shell/applets/MachinectlApplet.qml +++ b/shell/applets/MachinectlApplet.qml @@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -20,8 +21,8 @@ Column { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: _localHdrHover.hovered ? S.Theme.base02 : "transparent" - radius: S.Theme.radius + color: _localHdrHover.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius z: -1 } @@ -34,9 +35,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: " localhost" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } Rectangle { @@ -48,10 +49,10 @@ Column { color: { const st = S.SystemdService.systemState; if (st === "running") - return S.Theme.base0B; + return NS.ThemeService.base0B; if (st === "degraded") - return S.Theme.base0A; - return S.Theme.base08; + return NS.ThemeService.base0A; + return NS.ThemeService.base08; } opacity: 0.85 radius: 3 @@ -62,9 +63,9 @@ Column { id: _localStateLbl anchors.centerIn: parent text: S.SystemdService.systemState - color: S.Theme.base00 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } @@ -74,9 +75,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._localExpanded ? "" : "" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.iconFontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.iconFontFamily } TapHandler { @@ -99,9 +100,9 @@ Column { anchors.leftMargin: 24 anchors.verticalCenter: parent.verticalCenter text: "SYSTEM" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } } @@ -126,9 +127,9 @@ Column { Text { anchors.centerIn: parent text: "no failures" - color: S.Theme.base0B - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } @@ -142,9 +143,9 @@ Column { anchors.leftMargin: 24 anchors.verticalCenter: parent.verticalCenter text: "USER" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } } @@ -169,9 +170,9 @@ Column { Text { anchors.centerIn: parent text: "no failures" - color: S.Theme.base0B - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } } @@ -209,8 +210,8 @@ Column { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: _mHdrHover.hovered ? S.Theme.base02 : "transparent" - radius: S.Theme.radius + color: _mHdrHover.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius z: -1 } @@ -223,9 +224,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: " " + _machineSection.modelData.name - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight width: parent.width - 100 } @@ -239,10 +240,10 @@ Column { color: { const st = S.MachinectlService.machineState(_machineSection.modelData.name); if (st === "running") - return S.Theme.base0B; + return NS.ThemeService.base0B; if (st === "degraded") - return S.Theme.base0A; - return st === "unknown" ? "transparent" : S.Theme.base08; + return NS.ThemeService.base0A; + return st === "unknown" ? "transparent" : NS.ThemeService.base08; } opacity: 0.85 radius: 3 @@ -253,9 +254,9 @@ Column { id: _mStateLbl anchors.centerIn: parent text: S.MachinectlService.machineState(_machineSection.modelData.name) - color: S.Theme.base00 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } @@ -265,9 +266,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: _machineSection._expanded ? "" : "" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.iconFontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.iconFontFamily } TapHandler { @@ -293,9 +294,9 @@ Column { Text { anchors.centerIn: parent text: "loading..." - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } @@ -310,9 +311,9 @@ Column { anchors.leftMargin: 24 anchors.verticalCenter: parent.verticalCenter text: "SYSTEM" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } } @@ -337,9 +338,9 @@ Column { Text { anchors.centerIn: parent text: "no failures" - color: S.Theme.base0B - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } } diff --git a/shell/applets/MemoryApplet.qml b/shell/applets/MemoryApplet.qml index bb9af55..133de9f 100644 --- a/shell/applets/MemoryApplet.qml +++ b/shell/applets/MemoryApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -35,7 +36,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 3 } @@ -43,7 +44,7 @@ Column { Rectangle { width: parent.width * Math.min(1, (root.usedGb + root.cachedGb) / Math.max(root.totalGb, 0.001)) height: parent.height - color: S.Theme.base0D + color: NS.ThemeService.base0D opacity: 0.4 radius: 3 Behavior on width { @@ -97,7 +98,7 @@ Column { height: 32 history: S.SystemStats.memHistory strokeColor: root.accentColor - colorAt: v => S.Theme.loadColor(v) + colorAt: v => S.ThemeUtil.loadColor(v) active: root.active } @@ -117,9 +118,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "PROCESS" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } @@ -128,9 +129,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "MEM" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } } @@ -148,9 +149,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.cmd - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight width: parent.width - 80 } @@ -160,9 +161,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.mem.toFixed(1) + "%" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily width: 36 horizontalAlignment: Text.AlignRight } diff --git a/shell/applets/MprisApplet.qml b/shell/applets/MprisApplet.qml index c4c6ea9..a395e53 100644 --- a/shell/applets/MprisApplet.qml +++ b/shell/applets/MprisApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -15,7 +16,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 } // Outgoing art - snaps to current opacity, then fades out @@ -132,7 +133,7 @@ Column { } GradientStop { position: 1 - color: S.Theme.base01 + color: NS.ThemeService.base01 } } } @@ -140,9 +141,9 @@ Column { Text { anchors.centerIn: parent text: "\uF001" - color: S.Theme.base04 + color: NS.ThemeService.base04 font.pixelSize: 28 - font.family: S.Theme.iconFontFamily + font.family: NS.ThemeService.iconFontFamily visible: !_artImg._hasArt } } @@ -164,9 +165,9 @@ Column { Text { width: parent.width text: S.MprisService.player?.trackTitle || "No track" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize + 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + 1 + font.family: NS.ThemeService.fontFamily font.bold: true elide: Text.ElideRight } @@ -180,9 +181,9 @@ Column { const artist = Array.isArray(p.trackArtists) ? p.trackArtists.join(", ") : (p.trackArtists || ""); return [artist, p.trackAlbum].filter(s => s).join(" \u2014 "); } - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight visible: text !== "" } @@ -209,18 +210,18 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: parent._fmtTime(parent.pos) - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: parent._fmtTime(parent.dur) - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } Item { @@ -231,7 +232,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 2 } Rectangle { @@ -254,9 +255,9 @@ Column { Text { text: "\uF048" - color: S.MprisService.player?.canGoPrevious ? S.Theme.base05 : S.Theme.base03 - font.pixelSize: S.Theme.fontSize + 4 - font.family: S.Theme.iconFontFamily + color: S.MprisService.player?.canGoPrevious ? NS.ThemeService.base05 : NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize + 4 + font.family: NS.ThemeService.iconFontFamily anchors.verticalCenter: parent.verticalCenter HoverHandler { cursorShape: Qt.PointingHandCursor @@ -270,8 +271,8 @@ Column { Text { text: S.MprisService.playing ? "\uF04C" : "\uF04B" color: root.accentColor - font.pixelSize: S.Theme.fontSize + 8 - font.family: S.Theme.iconFontFamily + font.pixelSize: NS.ThemeService.fontSize + 8 + font.family: NS.ThemeService.iconFontFamily anchors.verticalCenter: parent.verticalCenter HoverHandler { cursorShape: Qt.PointingHandCursor @@ -283,9 +284,9 @@ Column { Text { text: "\uF051" - color: S.MprisService.player?.canGoNext ? S.Theme.base05 : S.Theme.base03 - font.pixelSize: S.Theme.fontSize + 4 - font.family: S.Theme.iconFontFamily + color: S.MprisService.player?.canGoNext ? NS.ThemeService.base05 : NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize + 4 + font.family: NS.ThemeService.iconFontFamily anchors.verticalCenter: parent.verticalCenter HoverHandler { cursorShape: Qt.PointingHandCursor @@ -323,17 +324,17 @@ Column { width: _pLabel.implicitWidth + 12 height: 18 radius: 9 - color: _active ? S.Theme.base02 : (pHover.hovered ? S.Theme.base02 : "transparent") - border.color: _active ? root.accentColor : S.Theme.base03 + color: _active ? NS.ThemeService.base02 : (pHover.hovered ? NS.ThemeService.base02 : "transparent") + border.color: _active ? root.accentColor : NS.ThemeService.base03 border.width: _active ? 1 : 0 Text { id: _pLabel anchors.centerIn: parent text: modelData.identity ?? "Player" - color: _active ? root.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: _active ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily font.bold: _active } diff --git a/shell/applets/NetworkApplet.qml b/shell/applets/NetworkApplet.qml index 47453ef..28ba4f1 100644 --- a/shell/applets/NetworkApplet.qml +++ b/shell/applets/NetworkApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS // NOT safe for lock screen - can toggle wifi and connect/disconnect networks Column { @@ -20,9 +21,9 @@ Column { Text { anchors.centerIn: parent text: "\uF011" - color: S.NetworkService.wifiEnabled ? root.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily + color: S.NetworkService.wifiEnabled ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily Behavior on color { ColorAnimation { @@ -55,9 +56,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: entry.modelData.isWifi ? "\uF1EB" : "\uDB80\uDE00" - color: entry.modelData.active ? root.accentColor : S.Theme.base05 - font.pixelSize: S.Theme.fontSize + 1 - font.family: S.Theme.iconFontFamily + color: entry.modelData.active ? root.accentColor : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + 1 + font.family: NS.ThemeService.iconFontFamily } Text { @@ -67,9 +68,9 @@ Column { anchors.rightMargin: 4 anchors.verticalCenter: parent.verticalCenter text: entry.modelData.name - color: entry.modelData.active ? root.accentColor : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: entry.modelData.active ? root.accentColor : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: entry.modelData.active elide: Text.ElideRight } @@ -80,9 +81,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: entry.modelData.signal >= 0 ? entry.modelData.signal + "%" : "" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily width: entry.modelData.signal >= 0 ? implicitWidth : 0 } @@ -102,8 +103,8 @@ Column { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: S.NetworkService.wifiEnabled ? "No networks available" : "Wi-Fi is off" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } } diff --git a/shell/applets/NotifApplet.qml b/shell/applets/NotifApplet.qml index 5280810..89ffd00 100644 --- a/shell/applets/NotifApplet.qml +++ b/shell/applets/NotifApplet.qml @@ -1,6 +1,7 @@ import QtQuick import "../services" as S import "../modules" as M +import NovaStats as NS // NOT safe for lock screen - notification contents may be sensitive, // can dismiss/clear notifications and toggle DND @@ -21,9 +22,9 @@ Column { // Clear all Text { text: "\uF1F8" - color: _clearHover.hovered ? S.Theme.base08 : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily + color: _clearHover.hovered ? NS.ThemeService.base08 : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily visible: S.NotifService.count > 0 HoverHandler { @@ -38,9 +39,9 @@ Column { // DND toggle Text { text: S.NotifService.dnd ? "\uDB82\uDE93" : "\uDB80\uDC9C" - color: S.NotifService.dnd ? S.Theme.base09 : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily + color: S.NotifService.dnd ? NS.ThemeService.base09 : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily HoverHandler { cursorShape: Qt.PointingHandCursor @@ -201,7 +202,7 @@ Column { ListView { id: _notifList width: root.contentWidth - height: Math.min(contentHeight, 60 * (S.Modules.notifications.maxVisible || 10)) + height: Math.min(contentHeight, 60 * (NS.ModulesService.notificationsMaxVisible || 10)) clip: true boundsBehavior: Flickable.StopAtBounds model: root._flatModel @@ -228,7 +229,7 @@ Column { readonly property real _targetHeight: { if (_type === "header") - return modelData.collapsed ? (28 + modelData.count * (S.Theme.fontSize + 4)) : 28; + return modelData.collapsed ? (28 + modelData.count * (NS.ThemeService.fontSize + 4)) : 28; return _notifCard.implicitHeight; } @@ -298,12 +299,12 @@ Column { anchors.leftMargin: 10 anchors.top: parent.top anchors.topMargin: (28 - height) / 2 - width: S.Theme.fontSize + 2 - height: S.Theme.fontSize + 2 + width: NS.ThemeService.fontSize + 2 + height: NS.ThemeService.fontSize + 2 source: notifDelegate._type === "header" ? (notifDelegate.modelData.resolvedIcon || "") : "" visible: status === Image.Ready fillMode: Image.PreserveAspectFit - sourceSize: Qt.size(S.Theme.fontSize + 2, S.Theme.fontSize + 2) + sourceSize: Qt.size(NS.ThemeService.fontSize + 2, NS.ThemeService.fontSize + 2) asynchronous: true } @@ -315,9 +316,9 @@ Column { height: 28 verticalAlignment: Text.AlignVCenter text: notifDelegate._type === "header" && notifDelegate.modelData.collapsed ? "\u25B8" : "\u25BE" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily opacity: _headerHover.hovered ? 1 : 0 } @@ -330,9 +331,9 @@ Column { height: 28 verticalAlignment: Text.AlignVCenter text: notifDelegate._type === "header" ? (notifDelegate.modelData.appName || "Unknown") : "" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily font.bold: true elide: Text.ElideRight } @@ -345,9 +346,9 @@ Column { height: 28 verticalAlignment: Text.AlignVCenter text: "\uF1F8" - color: _groupDismissHover.hovered ? S.Theme.base08 : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.iconFontFamily + color: _groupDismissHover.hovered ? NS.ThemeService.base08 : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.iconFontFamily opacity: _headerHover.hovered ? 1 : 0 HoverHandler { @@ -373,14 +374,14 @@ Column { anchors.leftMargin: 10 anchors.right: parent.right anchors.rightMargin: 10 - y: 28 + index * (S.Theme.fontSize + 4) - height: S.Theme.fontSize + 4 + y: 28 + index * (NS.ThemeService.fontSize + 4) + height: NS.ThemeService.fontSize + 4 verticalAlignment: Text.AlignVCenter text: modelData elide: Text.ElideRight - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily - color: S.Theme.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily + color: NS.ThemeService.base04 } } } @@ -442,8 +443,8 @@ Column { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "No notifications" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } } diff --git a/shell/applets/PowerApplet.qml b/shell/applets/PowerApplet.qml index efb6fa3..1955533 100644 --- a/shell/applets/PowerApplet.qml +++ b/shell/applets/PowerApplet.qml @@ -1,6 +1,7 @@ import QtQuick import Quickshell import "../services" as S +import NovaStats as NS // NOT safe for lock screen - executes system commands (shutdown, reboot, logout, suspend) Column { @@ -42,35 +43,35 @@ Column { label: "Lock", icon: "\uF023", cmd: ["loginctl", "lock-session"], - color: S.Theme.base0D, + color: NS.ThemeService.base0D, confirm: false }, { label: "Suspend", icon: "\uF186", cmd: ["systemctl", "suspend"], - color: S.Theme.base0E, + color: NS.ThemeService.base0E, confirm: false }, { label: "Logout", icon: "\uF2F5", cmd: root._isNiri ? ["niri", "msg", "action", "quit"] : ["loginctl", "terminate-user", ""], - color: S.Theme.base0A, + color: NS.ThemeService.base0A, confirm: false }, { label: "Reboot", icon: "\uF021", cmd: ["systemctl", "reboot"], - color: S.Theme.base09, + color: NS.ThemeService.base09, confirm: true }, { label: "Shutdown", icon: "\uF011", cmd: ["systemctl", "poweroff"], - color: S.Theme.base08, + color: NS.ThemeService.base08, confirm: true } ] @@ -88,8 +89,8 @@ Column { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: entryHover.hovered ? S.Theme.base02 : "transparent" - radius: S.Theme.radius + color: entryHover.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius } Text { @@ -99,8 +100,8 @@ Column { anchors.leftMargin: 12 text: entry.modelData.icon color: entry.modelData.color - font.pixelSize: S.Theme.fontSize + 1 - font.family: S.Theme.iconFontFamily + font.pixelSize: NS.ThemeService.fontSize + 1 + font.family: NS.ThemeService.iconFontFamily } Text { @@ -108,9 +109,9 @@ Column { anchors.left: entryIcon.right anchors.leftMargin: 10 text: entry.modelData.label - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } HoverHandler { @@ -138,9 +139,9 @@ Column { Text { anchors.centerIn: parent text: root._confirmItem ? root._confirmItem.label + "?" : "" - color: root._confirmItem ? root._confirmItem.color : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: root._confirmItem ? root._confirmItem.color : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true } } @@ -155,18 +156,18 @@ Column { Rectangle { anchors.fill: parent - color: cancelHover.hovered ? S.Theme.base02 : S.Theme.base01 - radius: S.Theme.radius + color: cancelHover.hovered ? NS.ThemeService.base02 : NS.ThemeService.base01 + radius: NS.ThemeService.radius border.width: 1 - border.color: S.Theme.base03 + border.color: NS.ThemeService.base03 } Text { anchors.centerIn: parent text: "Cancel" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } HoverHandler { @@ -187,21 +188,21 @@ Column { anchors.fill: parent color: { if (!root._confirmItem) - return S.Theme.base02; + return NS.ThemeService.base02; const c = root._confirmItem.color; return confirmHover.hovered ? Qt.rgba(c.r, c.g, c.b, 0.3) : Qt.rgba(c.r, c.g, c.b, 0.15); } - radius: S.Theme.radius + radius: NS.ThemeService.radius border.width: 1 - border.color: root._confirmItem ? root._confirmItem.color : S.Theme.base03 + border.color: root._confirmItem ? root._confirmItem.color : NS.ThemeService.base03 } Text { anchors.centerIn: parent text: "Confirm" - color: root._confirmItem ? root._confirmItem.color : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: root._confirmItem ? root._confirmItem.color : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true } diff --git a/shell/applets/Separator.qml b/shell/applets/Separator.qml index 365b9fc..a1d46c7 100644 --- a/shell/applets/Separator.qml +++ b/shell/applets/Separator.qml @@ -1,9 +1,10 @@ import QtQuick import "../services" as S +import NovaStats as NS Rectangle { width: (parent?.width ?? 16) - 16 height: 1 anchors.horizontalCenter: parent?.horizontalCenter - color: S.Theme.base03 + color: NS.ThemeService.base03 } diff --git a/shell/applets/SystemdApplet.qml b/shell/applets/SystemdApplet.qml index 6412842..6462ed3 100644 --- a/shell/applets/SystemdApplet.qml +++ b/shell/applets/SystemdApplet.qml @@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -21,9 +22,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "SYSTEM" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } @@ -35,10 +36,10 @@ Column { color: { const st = S.SystemdService.systemState; if (st === "running") - return S.Theme.base0B; + return NS.ThemeService.base0B; if (st === "degraded") - return S.Theme.base0A; - return S.Theme.base08; + return NS.ThemeService.base0A; + return NS.ThemeService.base08; } opacity: 0.85 radius: 3 @@ -49,9 +50,9 @@ Column { id: _sysStateLbl anchors.centerIn: parent text: S.SystemdService.systemState - color: S.Theme.base00 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } } @@ -79,9 +80,9 @@ Column { Text { anchors.centerIn: parent text: "no failed system units" - color: S.Theme.base0B - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } @@ -97,9 +98,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "USER" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } @@ -111,10 +112,10 @@ Column { color: { const st = S.SystemdService.userState; if (st === "running") - return S.Theme.base0B; + return NS.ThemeService.base0B; if (st === "degraded") - return S.Theme.base0A; - return S.Theme.base08; + return NS.ThemeService.base0A; + return NS.ThemeService.base08; } opacity: 0.85 radius: 3 @@ -125,9 +126,9 @@ Column { id: _userStateLbl anchors.centerIn: parent text: S.SystemdService.userState - color: S.Theme.base00 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } } @@ -155,9 +156,9 @@ Column { Text { anchors.centerIn: parent text: "no failed user units" - color: S.Theme.base0B - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } diff --git a/shell/applets/SystemdUnitRow.qml b/shell/applets/SystemdUnitRow.qml index a64d34e..e05d49f 100644 --- a/shell/applets/SystemdUnitRow.qml +++ b/shell/applets/SystemdUnitRow.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Item { id: root @@ -72,8 +73,8 @@ Item { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: _rowHover.hovered ? S.Theme.base02 : "transparent" - radius: S.Theme.radius + color: _rowHover.hovered ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius z: -1 } @@ -88,9 +89,9 @@ Item { anchors.rightMargin: 6 anchors.verticalCenter: parent.verticalCenter text: root.unitName - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight } @@ -99,7 +100,7 @@ Item { anchors.right: _restartBtn.left anchors.rightMargin: 6 anchors.verticalCenter: parent.verticalCenter - color: S.Theme.base08 + color: NS.ThemeService.base08 opacity: 0.85 radius: 3 width: _subStateLbl.width + 8 @@ -109,9 +110,9 @@ Item { id: _subStateLbl anchors.centerIn: parent text: root.subState - color: S.Theme.base00 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base00 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } @@ -127,9 +128,9 @@ Item { Text { anchors.centerIn: parent text: "" - color: _rHover.hovered ? root.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily + color: _rHover.hovered ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily Behavior on color { ColorAnimation { duration: 80 @@ -164,9 +165,9 @@ Item { Text { anchors.centerIn: parent text: root._expanded ? "" : "" - color: _expHover.hovered ? root.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.iconFontFamily + 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 @@ -202,16 +203,16 @@ Item { anchors.leftMargin: 8 anchors.rightMargin: 8 anchors.bottomMargin: 4 - color: S.Theme.base01 - radius: S.Theme.radius + color: NS.ThemeService.base01 + radius: NS.ThemeService.radius Text { anchors.centerIn: parent visible: root._loading text: "loading..." - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } Flickable { @@ -226,9 +227,9 @@ Item { id: _jContent width: _flick.width text: root._jText - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily wrapMode: Text.WrapAnywhere } diff --git a/shell/applets/TemperatureApplet.qml b/shell/applets/TemperatureApplet.qml index 1e5b9a1..e3af0c4 100644 --- a/shell/applets/TemperatureApplet.qml +++ b/shell/applets/TemperatureApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -14,7 +15,7 @@ Column { property bool active: true property string deviceFilter: "" - property color stateColor: temp > hot ? S.Theme.base08 : temp > warm ? S.Theme.base0A : root.accentColor + property color stateColor: temp > hot ? NS.ThemeService.base08 : temp > warm ? NS.ThemeService.base0A : root.accentColor Behavior on stateColor { ColorAnimation { duration: 300 @@ -32,8 +33,8 @@ Column { anchors.verticalCenter: parent.verticalCenter text: root.temp + "\u00B0C" color: root.stateColor - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true width: _tempSizer.implicitWidth horizontalAlignment: Text.AlignRight @@ -63,7 +64,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 3 } @@ -87,7 +88,7 @@ Column { width: 1 height: parent.height + 4 anchors.verticalCenter: parent.verticalCenter - color: S.Theme.base0A + color: NS.ThemeService.base0A opacity: 0.6 } @@ -97,7 +98,7 @@ Column { width: 1 height: parent.height + 4 anchors.verticalCenter: parent.verticalCenter - color: S.Theme.base08 + color: NS.ThemeService.base08 opacity: 0.6 } } @@ -116,11 +117,11 @@ Column { thresholds: [ { value: root.warm, - color: S.Theme.base0A + color: NS.ThemeService.base0A }, { value: root.hot, - color: S.Theme.base08 + color: NS.ThemeService.base08 } ] } @@ -135,9 +136,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "warm " + root.warm + "\u00B0 hot " + root.hot + "\u00B0" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily font.letterSpacing: 0.5 } @@ -146,9 +147,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "10 min" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 3 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 3 + font.family: NS.ThemeService.fontFamily } } @@ -179,9 +180,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.name - color: _isActive ? root.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: _isActive ? root.accentColor : NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight width: parent.width - 80 } @@ -191,9 +192,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.celsius + "\u00B0C" - color: S.Theme.loadColor(modelData.celsius) - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: S.ThemeUtil.loadColor(modelData.celsius) + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily font.bold: _isActive } } diff --git a/shell/applets/VolumeApplet.qml b/shell/applets/VolumeApplet.qml index eb9b58a..b870b3a 100644 --- a/shell/applets/VolumeApplet.qml +++ b/shell/applets/VolumeApplet.qml @@ -1,6 +1,7 @@ import QtQuick import Quickshell.Services.Pipewire import "../services" as S +import NovaStats as NS Column { id: root @@ -11,7 +12,7 @@ Column { property real volume: sink?.audio?.volume ?? 0 property bool muted: sink?.audio?.muted ?? false readonly property string volumeIcon: muted ? "\uF026" : (volume > 0.5 ? "\uF028" : (volume > 0 ? "\uF027" : "\uF026")) - readonly property color volumeColor: muted ? S.Theme.base04 : root.accentColor + readonly property color volumeColor: muted ? NS.ThemeService.base04 : root.accentColor // Slider row Item { @@ -25,8 +26,8 @@ Column { anchors.verticalCenter: parent.verticalCenter text: root.volumeIcon color: root.volumeColor - font.pixelSize: S.Theme.fontSize + 2 - font.family: S.Theme.iconFontFamily + font.pixelSize: NS.ThemeService.fontSize + 2 + font.family: NS.ThemeService.iconFontFamily HoverHandler { cursorShape: Qt.PointingHandCursor @@ -48,7 +49,7 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 3 } Rectangle { @@ -86,9 +87,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: Math.round(root.volume * 100) + "%" - color: root.muted ? S.Theme.base04 : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: root.muted ? NS.ThemeService.base04 : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily width: 30 } } @@ -113,8 +114,8 @@ Column { leftPadding: 12 text: "Output Devices" color: root.accentColor - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily } Repeater { @@ -134,9 +135,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.description || modelData.name || "Unknown" - color: parent._active ? root.accentColor : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: parent._active ? root.accentColor : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: parent._active elide: Text.ElideRight } @@ -159,8 +160,8 @@ Column { leftPadding: 12 text: "Applications" color: root.accentColor - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily } Repeater { @@ -183,9 +184,9 @@ Column { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: streamEntry._muted ? "\uF026" : "\uF028" - color: streamEntry._muted ? S.Theme.base04 : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily + color: streamEntry._muted ? NS.ThemeService.base04 : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily HoverHandler { cursorShape: Qt.PointingHandCursor @@ -202,9 +203,9 @@ Column { anchors.leftMargin: 6 anchors.verticalCenter: parent.verticalCenter text: streamEntry._appName - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight width: 70 } @@ -220,13 +221,13 @@ Column { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 2 } Rectangle { width: parent.width * Math.min(1, Math.max(0, streamEntry._vol)) height: parent.height - color: streamEntry._muted ? S.Theme.base04 : root.accentColor + color: streamEntry._muted ? NS.ThemeService.base04 : root.accentColor radius: 2 } @@ -253,9 +254,9 @@ Column { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: Math.round(streamEntry._vol * 100) + "%" - color: streamEntry._muted ? S.Theme.base04 : S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: streamEntry._muted ? NS.ThemeService.base04 : NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily width: 28 } } diff --git a/shell/applets/WeatherApplet.qml b/shell/applets/WeatherApplet.qml index 2c716a8..f02ac44 100644 --- a/shell/applets/WeatherApplet.qml +++ b/shell/applets/WeatherApplet.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Column { id: root @@ -17,8 +18,8 @@ Column { anchors.verticalCenter: parent.verticalCenter text: S.WeatherService.icon color: root.accentColor - font.pixelSize: S.Theme.fontSize + 2 - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize + 2 + font.family: NS.ThemeService.fontFamily } } @@ -28,9 +29,9 @@ Column { anchors.horizontalCenter: parent.horizontalCenter text: S.WeatherService.tooltip.replace(/\n/g, "
") textFormat: Text.RichText - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily wrapMode: Text.WordWrap lineHeight: 1.3 } diff --git a/shell/dock/AppletDock.qml b/shell/dock/AppletDock.qml index 6febda3..18a3dc0 100644 --- a/shell/dock/AppletDock.qml +++ b/shell/dock/AppletDock.qml @@ -7,6 +7,7 @@ import "." as D import "../services" as S import "../applets" as C import "../modules" as M +import NovaStats as NS PanelWindow { id: root @@ -25,11 +26,10 @@ PanelWindow { anchors.bottom: true // Counteract the bar's exclusive zone so the dock extends to the top of the screen - margins.top: -S.Theme.barHeight + margins.top: -NS.ThemeService.barHeight - readonly property int _dockWidth: S.Modules.dock.width ?? 300 - readonly property var _applets: S.Modules.dock.applets ?? {} - readonly property color _accent: S.Theme.base0C + readonly property int _dockWidth: NS.ModulesService.dockWidth ?? 300 + readonly property color _accent: NS.ThemeService.base0C implicitWidth: _dockWidth @@ -55,14 +55,14 @@ PanelWindow { if (S.DockState.open) { root._winVisible = true; _hideAnim.stop(); - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { root._slideX = 0; } else { _showAnim.start(); } } else { _showAnim.stop(); - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { root._slideX = root._dockWidth; root._winVisible = false; } else { @@ -113,8 +113,8 @@ PanelWindow { Rectangle { id: _bg anchors.fill: parent - color: S.Theme.base00 - opacity: S.DockState.mode === "pinned" ? 1.0 : Math.max(S.Theme.barOpacity, 0.85) + color: NS.ThemeService.base00 + opacity: S.DockState.mode === "pinned" ? 1.0 : Math.max(NS.ThemeService.barOpacity, 0.85) transform: Translate { x: root._slideX @@ -128,7 +128,7 @@ PanelWindow { anchors.top: parent.top anchors.bottom: parent.bottom width: 1 - color: S.Theme.base09 + color: NS.ThemeService.base09 opacity: _bg.opacity transform: Translate { @@ -142,7 +142,7 @@ PanelWindow { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - height: S.Theme.barHeight + height: NS.ThemeService.barHeight visible: S.DockState.mode === "pinned" transform: Translate { @@ -151,7 +151,7 @@ PanelWindow { Rectangle { anchors.fill: parent - color: S.Theme.base00 + color: NS.ThemeService.base00 } // Bottom border - gradient matching bar style (base0C to base09, but just the right end) @@ -160,7 +160,7 @@ PanelWindow { anchors.right: parent.right anchors.bottom: parent.bottom height: 1 - color: S.Theme.base09 + color: NS.ThemeService.base09 } // Dock toggle @@ -185,7 +185,7 @@ PanelWindow { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - anchors.leftMargin: S.Theme.groupSpacing + 1 + anchors.leftMargin: NS.ThemeService.groupSpacing + 1 contentHeight: _column.height clip: true boundsBehavior: Flickable.StopAtBounds @@ -205,7 +205,7 @@ PanelWindow { // Clock D.DockCard { - visible: root._applets.clock ?? true + visible: NS.ModulesService.dockAppletClock icon: "\uF017" title: "Clock" accentColor: root._accent @@ -221,7 +221,7 @@ PanelWindow { // CPU D.DockCard { id: _cpuCard - visible: root._applets.cpu ?? true + visible: NS.ModulesService.dockAppletCpu icon: "\uF2DB" title: "CPU" accentColor: root._accent @@ -237,7 +237,7 @@ PanelWindow { // GPU D.DockCard { - visible: (root._applets.gpu ?? true) && S.SystemStats.gpuAvailable + visible: (NS.ModulesService.dockAppletGpu) && S.SystemStats.gpuAvailable icon: "\uEB4C" title: "GPU" accentColor: root._accent @@ -253,7 +253,7 @@ PanelWindow { // Memory D.DockCard { id: _memCard - visible: root._applets.memory ?? true + visible: NS.ModulesService.dockAppletMemory icon: "\uEFC5" title: "Memory" accentColor: root._accent @@ -275,7 +275,7 @@ PanelWindow { // Temperature D.DockCard { - visible: root._applets.temperature ?? true + visible: NS.ModulesService.dockAppletTemperature icon: "\uF2C9" title: "Temperature" accentColor: root._accent @@ -284,19 +284,19 @@ PanelWindow { C.TemperatureApplet { width: parent.width temp: S.SystemStats.tempCelsius - warm: S.Modules.temperature.warm || 80 - hot: S.Modules.temperature.hot || 90 + warm: NS.ModulesService.temperatureWarm || 80 + hot: NS.ModulesService.temperatureHot || 90 history: S.SystemStats.tempHistory devices: S.SystemStats.tempDevices accentColor: root._accent - deviceFilter: S.Modules.temperature.device || "" + deviceFilter: NS.ModulesService.temperatureDevice || "" active: parent.expanded } } // Disk D.DockCard { - visible: root._applets.disk ?? true + visible: NS.ModulesService.dockAppletDisk icon: "\uF0C9" title: "Disk" accentColor: root._accent @@ -311,7 +311,7 @@ PanelWindow { // Battery D.DockCard { - visible: (root._applets.battery ?? true) && S.BatteryService.available + visible: (NS.ModulesService.dockAppletBattery) && S.BatteryService.available icon: "\uDB80\uDC84" title: "Battery" accentColor: root._accent @@ -326,7 +326,7 @@ PanelWindow { // Network D.DockCard { - visible: root._applets.network ?? true + visible: NS.ModulesService.dockAppletNetwork icon: "\uF1EB" title: "Network" accentColor: root._accent @@ -341,7 +341,7 @@ PanelWindow { // Bluetooth D.DockCard { - visible: (root._applets.bluetooth ?? true) && S.BluetoothService.state !== "unavailable" + visible: (NS.ModulesService.dockAppletBluetooth) && S.BluetoothService.state !== "unavailable" icon: "\uF294" title: "Bluetooth" accentColor: root._accent @@ -356,7 +356,7 @@ PanelWindow { // Volume D.DockCard { - visible: root._applets.volume ?? true + visible: NS.ModulesService.dockAppletVolume icon: "\uF028" title: "Sound" accentColor: root._accent @@ -370,7 +370,7 @@ PanelWindow { // Backlight D.DockCard { - visible: (root._applets.backlight ?? true) && S.BacklightService.available + visible: (NS.ModulesService.dockAppletBacklight) && S.BacklightService.available icon: "\uF185" title: "Brightness" accentColor: root._accent @@ -386,7 +386,7 @@ PanelWindow { // Weather D.DockCard { - visible: (root._applets.weather ?? true) && S.WeatherService.available + visible: (NS.ModulesService.dockAppletWeather) && S.WeatherService.available icon: S.WeatherService.icon title: "Weather" accentColor: root._accent @@ -400,7 +400,7 @@ PanelWindow { // Now Playing D.DockCard { - visible: (root._applets.mpris ?? true) && S.MprisService.player !== null + visible: (NS.ModulesService.dockAppletMpris) && S.MprisService.player !== null icon: "\uF04B" title: "Now Playing" accentColor: root._accent @@ -414,7 +414,7 @@ PanelWindow { // Notifications D.DockCard { - visible: root._applets.notifications ?? true + visible: NS.ModulesService.dockAppletNotifications icon: "\uDB80\uDC9C" title: "Notifications" accentColor: root._accent @@ -429,7 +429,7 @@ PanelWindow { // Power D.DockCard { - visible: root._applets.power ?? true + visible: NS.ModulesService.dockAppletPower icon: "\uF011" title: "Power" accentColor: root._accent diff --git a/shell/dock/DockCard.qml b/shell/dock/DockCard.qml index d946cd1..e7dd6bc 100644 --- a/shell/dock/DockCard.qml +++ b/shell/dock/DockCard.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS // Collapsible card for the applet dock. // Set icon, title, and place applet content as children. @@ -9,20 +10,20 @@ Rectangle { property string icon: "" property string title: "" property bool expanded: false - property color accentColor: S.Theme.base0C + property color accentColor: NS.ThemeService.base0C default property alias content: _contentColumn.children width: parent?.width ?? 300 height: _header.height + (_contentColumn.visible ? _contentColumn.height : 0) - radius: S.Theme.radius + 2 - color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) - border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) + radius: NS.ThemeService.radius + 2 + color: Qt.rgba(NS.ThemeService.base01.r, NS.ThemeService.base01.g, NS.ThemeService.base01.b, 0.7) + border.color: Qt.rgba(NS.ThemeService.base03.r, NS.ThemeService.base03.g, NS.ThemeService.base03.b, 0.3) border.width: 1 clip: true Behavior on height { - enabled: !S.Theme.reducedMotion + enabled: !S.ThemeUtil.reducedMotion NumberAnimation { duration: 150 easing.type: Easing.OutCubic @@ -41,8 +42,8 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter text: root.icon color: root.accentColor - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.iconFontFamily } Text { @@ -50,10 +51,10 @@ Rectangle { anchors.leftMargin: 30 anchors.verticalCenter: parent.verticalCenter text: root.title - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - 1 + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize - 1 font.bold: true - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily } Text { @@ -61,9 +62,9 @@ Rectangle { anchors.rightMargin: 10 anchors.verticalCenter: parent.verticalCenter text: root.expanded ? "\uF078" : "\uF054" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.iconFontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.iconFontFamily Behavior on text { enabled: false @@ -84,7 +85,7 @@ Rectangle { anchors.right: parent.right anchors.bottom: parent.bottom height: 1 - color: S.Theme.base03 + color: NS.ThemeService.base03 opacity: root.expanded ? 0.5 : 0 } } diff --git a/shell/lock/LockClock.qml b/shell/lock/LockClock.qml index 05d0b40..29aa1f0 100644 --- a/shell/lock/LockClock.qml +++ b/shell/lock/LockClock.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Item { id: root @@ -15,7 +16,7 @@ Item { property bool _revealed: false on_RawProgressChanged: if (_rawProgress >= 1) _revealed = true - readonly property real _rawProgress: S.Theme.reducedMotion ? 1 : Math.max(0, Math.min(1, wavePhase / 300)) + readonly property real _rawProgress: S.ThemeUtil.reducedMotion ? 1 : Math.max(0, Math.min(1, wavePhase / 300)) readonly property real _progress: (_revealed ? 1 : _rawProgress) * unlockFade opacity: _progress property real _slideX: (1 - _progress) * -80 @@ -53,32 +54,32 @@ Item { Text { text: _clockRow._hours - color: S.Theme.base0C + color: NS.ThemeService.base0C font.pixelSize: root._fontSize - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true } Text { text: ":" - color: S.Theme.base0E + color: NS.ThemeService.base0E font.pixelSize: root._fontSize - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true } Text { text: _clockRow._minutes - color: S.Theme.base09 + color: NS.ThemeService.base09 font.pixelSize: root._fontSize - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true } } Text { text: Qt.formatDate(new Date(), "dddd, d MMMM") - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize + 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + 2 + font.family: NS.ThemeService.fontFamily Timer { interval: 60000 diff --git a/shell/lock/LockInput.qml b/shell/lock/LockInput.qml index 48cdf1e..abc3c82 100644 --- a/shell/lock/LockInput.qml +++ b/shell/lock/LockInput.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS Item { id: root @@ -15,18 +16,18 @@ Item { anchors.fill: parent color: { if (root.state === "fail" || root.state === "error") - return Qt.rgba(S.Theme.base08.r, S.Theme.base08.g, S.Theme.base08.b, 0.15); + return Qt.rgba(NS.ThemeService.base08.r, NS.ThemeService.base08.g, NS.ThemeService.base08.b, 0.15); if (root.state === "busy") - return Qt.rgba(S.Theme.base0D.r, S.Theme.base0D.g, S.Theme.base0D.b, 0.1); - return S.Theme.base02; + return Qt.rgba(NS.ThemeService.base0D.r, NS.ThemeService.base0D.g, NS.ThemeService.base0D.b, 0.1); + return NS.ThemeService.base02; } radius: height / 2 border.color: { if (root.state === "fail" || root.state === "error") - return S.Theme.base08; + return NS.ThemeService.base08; if (root.state === "busy") - return S.Theme.base0D; - return S.Theme.base03; + return NS.ThemeService.base0D; + return NS.ThemeService.base03; } border.width: 1 @@ -52,9 +53,9 @@ Item { return "Too many attempts"; return "Enter password"; } - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily opacity: root.buffer.length === 0 ? 1 : 0 Behavior on opacity { @@ -80,7 +81,7 @@ Item { width: 10 height: 10 radius: 5 - color: S.Theme.base05 + color: NS.ThemeService.base05 scale: 0 opacity: 0 diff --git a/shell/lock/LockNotifPills.qml b/shell/lock/LockNotifPills.qml index 82025bd..5410d1a 100644 --- a/shell/lock/LockNotifPills.qml +++ b/shell/lock/LockNotifPills.qml @@ -1,11 +1,12 @@ import QtQuick import "../services" as S +import NovaStats as NS Row { id: root spacing: 6 - visible: (S.Modules.lock.notifications ?? true) && _notifGroups.length > 0 + visible: (NS.ModulesService.lockNotifications ?? true) && _notifGroups.length > 0 readonly property var _notifGroups: { const notifs = S.NotifService.list.filter(n => n.state !== "dismissed"); @@ -34,8 +35,8 @@ Row { width: _pillRow.implicitWidth + 12 height: 24 radius: 12 - color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) - border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) + color: Qt.rgba(NS.ThemeService.base01.r, NS.ThemeService.base01.g, NS.ThemeService.base01.b, 0.7) + border.color: Qt.rgba(NS.ThemeService.base03.r, NS.ThemeService.base03.g, NS.ThemeService.base03.b, 0.3) border.width: 1 HoverHandler { @@ -48,9 +49,9 @@ Row { anchors.bottom: parent.top anchors.bottomMargin: 4 text: _pill.modelData.name || "" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily visible: _pillHover.hovered && text !== "" } @@ -71,9 +72,9 @@ Row { Text { anchors.verticalCenter: parent.verticalCenter text: _pill.modelData.count > 1 ? _pill.modelData.count.toString() : "" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily visible: _pill.modelData.count > 1 } } diff --git a/shell/lock/LockSurface.qml b/shell/lock/LockSurface.qml index 2d9d852..5b4a7ce 100644 --- a/shell/lock/LockSurface.qml +++ b/shell/lock/LockSurface.qml @@ -4,6 +4,7 @@ import Quickshell import Quickshell.Wayland import "../services" as S import "../applets" as C +import NovaStats as NS WlSessionLockSurface { id: root @@ -11,7 +12,7 @@ WlSessionLockSurface { required property WlSessionLock lock required property LockAuth auth - color: S.Theme.base00 + color: NS.ThemeService.base00 // Keyboard input via TextInput - engages Qt's full input pipeline including // text-input protocol, which is more reliable than Keys on a plain Item in @@ -47,7 +48,7 @@ WlSessionLockSurface { // Threat level: eased curve so fails 1-2 are subtle, 3+ ramps up. // Max ~0.6 at fail 5 (previously that was fail 3). - readonly property bool _threatEnabled: S.Modules.lock.threatEffect ?? true + readonly property bool _threatEnabled: NS.ModulesService.lockThreatEffect ?? true readonly property real _threat: { if (!_threatEnabled || root.auth.failCount <= 0) return 0; @@ -68,7 +69,7 @@ WlSessionLockSurface { layer.effect: ShaderEffect { property real uThreat: root._threat property real uPulse: root._heartbeat - property color uColor: S.Theme.base08 + property color uColor: NS.ThemeService.base08 fragmentShader: Quickshell.shellPath("modules/lock_threat.frag.qsb") } @@ -77,7 +78,7 @@ WlSessionLockSurface { // compositor fallback color (niri red) from bleeding through. Rectangle { anchors.fill: parent - color: S.Theme.base00 + color: NS.ThemeService.base00 } // Clear desktop screenshot from ScreenshotService - visible immediately. @@ -86,7 +87,7 @@ WlSessionLockSurface { Image { anchors.fill: parent source: S.ScreenshotService.get(root.screen?.name ?? "") - visible: (S.Modules.lock.screenshot ?? true) && source !== "" && !S.Theme.reducedMotion + visible: (NS.ModulesService.lockScreenshot ?? true) && source !== "" && !S.ThemeUtil.reducedMotion opacity: _unlockFade fillMode: Image.PreserveAspectCrop } @@ -118,7 +119,7 @@ WlSessionLockSurface { Image { anchors.fill: parent source: S.ScreenshotService.get(root.screen?.name ?? "") - visible: (S.Modules.lock.screenshot ?? true) && source !== "" && !S.Theme.reducedMotion + visible: (NS.ModulesService.lockScreenshot ?? true) && source !== "" && !S.ThemeUtil.reducedMotion fillMode: Image.PreserveAspectCrop layer.enabled: true @@ -186,9 +187,9 @@ WlSessionLockSurface { anchors.top: _lockInput.bottom anchors.topMargin: 16 text: root.auth.message - color: S.Theme.base08 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base08 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily opacity: root.auth.message ? 1 : 0 Behavior on opacity { @@ -206,10 +207,10 @@ WlSessionLockSurface { anchors.horizontalCenter: parent.horizontalCenter visible: root.auth.failCount > 0 text: root.auth.failCount + (root.auth.failCount === 1 ? " failed attempt" : " failed attempts") - color: S.Theme.base08 + color: NS.ThemeService.base08 opacity: Math.max(0.4, root._threat) - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } // Right column - widgets, fly in when wave exits screen @@ -261,7 +262,7 @@ WlSessionLockSurface { interval: 30000 running: _keyInput.text.length > 0 && root.auth.state !== "busy" onTriggered: { - if (!S.Theme.reducedMotion) + if (!S.ThemeUtil.reducedMotion) _shakeAnim.restart(); _keyInput.text = ""; } @@ -371,7 +372,7 @@ WlSessionLockSurface { SequentialAnimation { id: _heartbeatAnim loops: Animation.Infinite - running: root._threatEnabled && root.auth.failCount >= 3 && root.lock.secure && !S.Theme.reducedMotion + running: root._threatEnabled && root.auth.failCount >= 3 && root.lock.secure && !S.ThemeUtil.reducedMotion // Systole (sharp spike) NumberAnimation { @@ -417,8 +418,8 @@ WlSessionLockSurface { // so the bar's ScreenCorners aren't visible. Draw our own. component LockCorner: Canvas { property int corner: 0 - readonly property int _r: S.Theme.screenRadius - visible: _r > 0 && S.Modules.screenCorners.enable + readonly property int _r: NS.ThemeService.screenRadius + visible: _r > 0 && NS.ModulesService.screenCornersEnable width: _r height: _r z: 999 diff --git a/shell/lock/LockWidgets.qml b/shell/lock/LockWidgets.qml index e934d53..67c7414 100644 --- a/shell/lock/LockWidgets.qml +++ b/shell/lock/LockWidgets.qml @@ -1,6 +1,7 @@ import QtQuick import "../services" as S import "../applets" as C +import NovaStats as NS Item { id: root @@ -16,7 +17,7 @@ Item { property bool _revealed: false on_RawProgressChanged: if (_rawProgress >= 1) _revealed = true - readonly property real _rawProgress: S.Theme.reducedMotion ? 1 : (screenWidth > 0 ? Math.max(0, Math.min(1, (wavePhase - screenWidth) / 500)) : 0) + readonly property real _rawProgress: S.ThemeUtil.reducedMotion ? 1 : (screenWidth > 0 ? Math.max(0, Math.min(1, (wavePhase - screenWidth) / 500)) : 0) readonly property real _progress: (_revealed ? 1 : _rawProgress) * unlockFade opacity: _progress property real _slideX: (1 - _progress) * 80 @@ -38,11 +39,11 @@ Item { id: _weatherCard width: parent.width height: _weatherContent.implicitHeight + 16 - radius: S.Theme.radius + 2 - color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) - border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) + radius: NS.ThemeService.radius + 2 + color: Qt.rgba(NS.ThemeService.base01.r, NS.ThemeService.base01.g, NS.ThemeService.base01.b, 0.7) + border.color: Qt.rgba(NS.ThemeService.base03.r, NS.ThemeService.base03.g, NS.ThemeService.base03.b, 0.3) border.width: 1 - visible: (S.Modules.lock.weather ?? true) && S.WeatherService.available + visible: (NS.ModulesService.lockWeather ?? true) && S.WeatherService.available C.WeatherApplet { id: _weatherContent @@ -50,7 +51,7 @@ Item { anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 8 - accentColor: S.Theme.base0C + accentColor: NS.ThemeService.base0C } } @@ -65,11 +66,11 @@ Item { id: _mprisCard width: parent.width height: _mprisContent.implicitHeight + 16 - radius: S.Theme.radius + 2 - color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) - border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) + radius: NS.ThemeService.radius + 2 + color: Qt.rgba(NS.ThemeService.base01.r, NS.ThemeService.base01.g, NS.ThemeService.base01.b, 0.7) + border.color: Qt.rgba(NS.ThemeService.base03.r, NS.ThemeService.base03.g, NS.ThemeService.base03.b, 0.3) border.width: 1 - visible: (S.Modules.lock.mpris ?? true) && S.MprisService.player !== null + visible: (NS.ModulesService.lockMpris ?? true) && S.MprisService.player !== null C.MprisApplet { id: _mprisContent @@ -77,7 +78,7 @@ Item { anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 8 - accentColor: S.Theme.base0D + accentColor: NS.ThemeService.base0D } } @@ -86,11 +87,11 @@ Item { id: _volumeCard width: parent.width height: _volumeContent.implicitHeight + 16 - radius: S.Theme.radius + 2 - color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) - border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) + radius: NS.ThemeService.radius + 2 + color: Qt.rgba(NS.ThemeService.base01.r, NS.ThemeService.base01.g, NS.ThemeService.base01.b, 0.7) + border.color: Qt.rgba(NS.ThemeService.base03.r, NS.ThemeService.base03.g, NS.ThemeService.base03.b, 0.3) border.width: 1 - visible: (S.Modules.lock.volume ?? true) && S.PipewireService.sink !== null + visible: (NS.ModulesService.lockVolume ?? true) && S.PipewireService.sink !== null C.VolumeApplet { id: _volumeContent @@ -98,7 +99,7 @@ Item { anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 8 - accentColor: S.Theme.base0E + accentColor: NS.ThemeService.base0E } } @@ -107,9 +108,9 @@ Item { id: _backlightCard width: parent.width height: _backlightContent.implicitHeight + 8 - radius: S.Theme.radius + 2 - color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) - border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) + radius: NS.ThemeService.radius + 2 + color: Qt.rgba(NS.ThemeService.base01.r, NS.ThemeService.base01.g, NS.ThemeService.base01.b, 0.7) + border.color: Qt.rgba(NS.ThemeService.base03.r, NS.ThemeService.base03.g, NS.ThemeService.base03.b, 0.3) border.width: 1 visible: S.BacklightService.available @@ -120,7 +121,7 @@ Item { anchors.top: parent.top anchors.topMargin: 4 percent: S.BacklightService.percent - accentColor: S.Theme.base0A + accentColor: NS.ThemeService.base0A onSetPercent: pct => S.BacklightService.setPercent(pct) } } diff --git a/shell/modules/BackgroundOverlay.qml b/shell/modules/BackgroundOverlay.qml index 6d56000..d007d16 100644 --- a/shell/modules/BackgroundOverlay.qml +++ b/shell/modules/BackgroundOverlay.qml @@ -4,6 +4,7 @@ import Quickshell import Quickshell.Wayland import "." as M import "../services" as S +import NovaStats as NS PanelWindow { id: root @@ -43,9 +44,9 @@ PanelWindow { Text { text: Qt.formatDateTime(clock.date, "HH") - color: S.Theme.base0D + color: NS.ThemeService.base0D font.pixelSize: 72 - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true anchors.verticalCenter: parent.verticalCenter } @@ -53,22 +54,22 @@ PanelWindow { text: ":" color: colon._colors[colon._colorIdx % colon._colors.length] Behavior on color { - enabled: !S.Theme.reducedMotion + enabled: !S.ThemeUtil.reducedMotion ColorAnimation { duration: 500 } } font.pixelSize: 72 - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true opacity: colon.opacity anchors.verticalCenter: parent.verticalCenter } Text { text: Qt.formatDateTime(clock.date, "mm") - color: S.Theme.base0E + color: NS.ThemeService.base0E font.pixelSize: 72 - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true anchors.verticalCenter: parent.verticalCenter } @@ -78,7 +79,7 @@ PanelWindow { source: glowSource anchors.fill: glowSource shadowEnabled: true - shadowColor: S.Theme.base0D + shadowColor: NS.ThemeService.base0D shadowBlur: 1.0 shadowVerticalOffset: 0 shadowHorizontalOffset: 0 @@ -91,10 +92,10 @@ PanelWindow { Text { text: Qt.formatDateTime(clock.date, "HH") - color: S.Theme.base0D + color: NS.ThemeService.base0D opacity: 0.85 font.pixelSize: 72 - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true anchors.verticalCenter: parent.verticalCenter } @@ -102,16 +103,16 @@ PanelWindow { id: colon text: ":" font.pixelSize: 72 - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true opacity: 0.85 anchors.verticalCenter: parent.verticalCenter property int _colorIdx: 0 - readonly property var _colors: [S.Theme.base08, S.Theme.base09, S.Theme.base0A, S.Theme.base0B, S.Theme.base0C, S.Theme.base0D, S.Theme.base0E, S.Theme.base05] + readonly property var _colors: [NS.ThemeService.base08, NS.ThemeService.base09, NS.ThemeService.base0A, NS.ThemeService.base0B, NS.ThemeService.base0C, NS.ThemeService.base0D, NS.ThemeService.base0E, NS.ThemeService.base05] color: _colors[_colorIdx % _colors.length] Behavior on color { - enabled: !S.Theme.reducedMotion + enabled: !S.ThemeUtil.reducedMotion ColorAnimation { duration: 500 } @@ -139,7 +140,7 @@ PanelWindow { Connections { target: clock function onDateChanged() { - if (S.Theme.reducedMotion) + if (S.ThemeUtil.reducedMotion) return; colon._colorIdx++; colonAnim.restart(); @@ -148,10 +149,10 @@ PanelWindow { } Text { text: Qt.formatDateTime(clock.date, "mm") - color: S.Theme.base0E + color: NS.ThemeService.base0E opacity: 0.85 font.pixelSize: 72 - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.bold: true anchors.verticalCenter: parent.verticalCenter } @@ -163,16 +164,16 @@ PanelWindow { id: dateText anchors.horizontalCenter: parent.horizontalCenter text: Qt.formatDateTime(clock.date, "dddd, dd MMMM yyyy") - color: S.Theme.base05 + color: NS.ThemeService.base05 opacity: 0.5 font.pixelSize: 18 - font.family: S.Theme.fontFamily + font.family: NS.ThemeService.fontFamily font.letterSpacing: 4 layer.enabled: true layer.effect: MultiEffect { shadowEnabled: true - shadowColor: S.Theme.base0D + shadowColor: NS.ThemeService.base0D shadowBlur: 0.4 shadowVerticalOffset: 0 shadowHorizontalOffset: 0 @@ -187,7 +188,7 @@ PanelWindow { Rectangle { anchors.fill: parent - color: S.Theme.base02 + color: NS.ThemeService.base02 radius: 1 opacity: 0.3 } @@ -196,7 +197,7 @@ PanelWindow { height: parent.height color: colon._colors[colon._colorIdx % colon._colors.length] Behavior on color { - enabled: !S.Theme.reducedMotion + enabled: !S.ThemeUtil.reducedMotion ColorAnimation { duration: 500 } @@ -205,7 +206,7 @@ PanelWindow { opacity: 0.6 Behavior on width { - enabled: !S.Theme.reducedMotion + enabled: !S.ThemeUtil.reducedMotion NumberAnimation { duration: 50 } diff --git a/shell/modules/BacklightModule.qml b/shell/modules/BacklightModule.qml index 00d8c91..3907b10 100644 --- a/shell/modules/BacklightModule.qml +++ b/shell/modules/BacklightModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing - active: S.Modules.backlight.enable && S.BacklightService.available + spacing: NS.ThemeService.moduleSpacing + active: NS.ModulesService.backlightEnable && S.BacklightService.available tooltip: "Brightness: " + percent + "%" panelNamespace: "nova-backlight" panelContentWidth: 200 diff --git a/shell/modules/Bar.qml b/shell/modules/Bar.qml index 005a4dc..36af284 100644 --- a/shell/modules/Bar.qml +++ b/shell/modules/Bar.qml @@ -4,6 +4,7 @@ import Quickshell import Quickshell.Wayland import "." as M import "../services" as S +import NovaStats as NS PanelWindow { id: bar @@ -19,13 +20,13 @@ PanelWindow { margins.right: S.DockState.reservedWidthAnimated - implicitHeight: S.Theme.barHeight + implicitHeight: NS.ThemeService.barHeight exclusiveZone: implicitHeight Rectangle { anchors.fill: parent - color: S.Theme.base00 - opacity: S.Theme.barOpacity + color: NS.ThemeService.base00 + opacity: NS.ThemeService.barOpacity } Canvas { @@ -35,7 +36,7 @@ PanelWindow { const ctx = getContext("2d"); const w = width; const h = height; - const r = S.Theme.screenRadius; + const r = NS.ThemeService.screenRadius; const lw = 3; const hw = lw / 2; @@ -43,8 +44,8 @@ PanelWindow { // Glow wash behind the border const glowGrad = ctx.createLinearGradient(0, 0, w, 0); - glowGrad.addColorStop(0, S.Theme.base0C.toString()); - glowGrad.addColorStop(1, S.Theme.base09.toString()); + glowGrad.addColorStop(0, NS.ThemeService.base0C.toString()); + glowGrad.addColorStop(1, NS.ThemeService.base09.toString()); ctx.globalAlpha = 0.25; ctx.fillStyle = glowGrad; ctx.fillRect(0, 0, w, h); @@ -61,8 +62,8 @@ PanelWindow { // Horizontal gradient for the border const grad = ctx.createLinearGradient(0, 0, w, 0); - grad.addColorStop(0, S.Theme.base0C.toString()); - grad.addColorStop(1, S.Theme.base09.toString()); + grad.addColorStop(0, NS.ThemeService.base0C.toString()); + grad.addColorStop(1, NS.ThemeService.base09.toString()); ctx.strokeStyle = grad; ctx.lineWidth = lw; @@ -97,16 +98,16 @@ PanelWindow { Item { anchors.fill: parent - anchors.topMargin: S.Theme.groupSpacing - anchors.leftMargin: S.Theme.groupSpacing - anchors.rightMargin: S.Theme.groupSpacing + anchors.topMargin: NS.ThemeService.groupSpacing + anchors.leftMargin: NS.ThemeService.groupSpacing + anchors.rightMargin: NS.ThemeService.groupSpacing // ---- center (declared first so left/right can anchor to it) ---- RowLayout { id: centerSection anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - spacing: S.Theme.groupSpacing + spacing: NS.ThemeService.groupSpacing M.BarGroup { M.PrivacyModule {} @@ -120,7 +121,7 @@ PanelWindow { anchors.left: parent.left anchors.right: centerSection.left anchors.verticalCenter: parent.verticalCenter - spacing: S.Theme.groupSpacing + spacing: NS.ThemeService.groupSpacing M.BarGroup { id: workspacesGroup @@ -135,10 +136,10 @@ PanelWindow { id: _windowTitleGroup Layout.minimumWidth: 0 clip: true - visible: S.Modules.windowTitle.enable && S.NiriIpc.focusedTitle !== "" + visible: NS.ModulesService.windowTitleEnable && S.NiriIpc.focusedTitle !== "" M.WindowTitleModule { id: _windowTitle - readonly property real _maxWidth: Math.max(0, centerSection.x - _windowTitleGroup.x - 2 * S.Theme.groupPadding - S.Theme.groupSpacing) + readonly property real _maxWidth: Math.max(0, centerSection.x - _windowTitleGroup.x - 2 * NS.ThemeService.groupPadding - NS.ThemeService.groupSpacing) width: Math.min(naturalWidth, _maxWidth) } } @@ -152,7 +153,7 @@ PanelWindow { anchors.left: centerSection.right anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - spacing: S.Theme.groupSpacing + spacing: NS.ThemeService.groupSpacing Item { Layout.fillWidth: true diff --git a/shell/modules/BarGroup.qml b/shell/modules/BarGroup.qml index a0c00cc..c89a078 100644 --- a/shell/modules/BarGroup.qml +++ b/shell/modules/BarGroup.qml @@ -3,6 +3,7 @@ import QtQuick.Effects import Quickshell import "." as M import "../services" as S +import NovaStats as NS Item { id: root @@ -18,14 +19,14 @@ Item { const effectiveWidth = scr.width - (S.DockState.reservedWidthAnimated ?? 0); return Math.max(0, Math.min(1, gx / (effectiveWidth > 0 ? effectiveWidth : scr.width))); } - property color borderColor: Qt.rgba(S.Theme.base0C.r + (S.Theme.base09.r - S.Theme.base0C.r) * _posFrac, S.Theme.base0C.g + (S.Theme.base09.g - S.Theme.base0C.g) * _posFrac, S.Theme.base0C.b + (S.Theme.base09.b - S.Theme.base0C.b) * _posFrac, 1) + property color borderColor: Qt.rgba(NS.ThemeService.base0C.r + (NS.ThemeService.base09.r - NS.ThemeService.base0C.r) * _posFrac, NS.ThemeService.base0C.g + (NS.ThemeService.base09.g - NS.ThemeService.base0C.g) * _posFrac, NS.ThemeService.base0C.b + (NS.ThemeService.base09.b - NS.ThemeService.base0C.b) * _posFrac, 1) property bool leftEdge: false property bool rightEdge: false - readonly property real _tlr: leftEdge ? S.Theme.screenRadius : S.Theme.radius - readonly property real _trr: rightEdge ? S.Theme.screenRadius : S.Theme.radius - readonly property real _blr: S.Theme.radius - readonly property real _brr: S.Theme.radius + readonly property real _tlr: leftEdge ? NS.ThemeService.screenRadius : NS.ThemeService.radius + readonly property real _trr: rightEdge ? NS.ThemeService.screenRadius : NS.ThemeService.radius + readonly property real _blr: NS.ThemeService.radius + readonly property real _brr: NS.ThemeService.radius // Check children's `active` instead of visibleChildren to avoid circular // effectiveVisible dependency: if the BarGroup is invisible, children's @@ -41,9 +42,9 @@ Item { } implicitWidth: row.implicitWidth + _pad * 2 - implicitHeight: S.Theme.barHeight - 3 - _pad + implicitHeight: NS.ThemeService.barHeight - 3 - _pad - readonly property int _pad: S.Theme.groupPadding + readonly property int _pad: NS.ThemeService.groupPadding property bool _hovered: false HoverHandler { @@ -57,7 +58,7 @@ Item { topRightRadius: root._trr bottomLeftRadius: root._blr bottomRightRadius: root._brr - color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.55) + color: Qt.rgba(NS.ThemeService.base01.r, NS.ThemeService.base01.g, NS.ThemeService.base01.b, 0.55) } // Frost sheen — subtle white highlight, top-heavy @@ -137,7 +138,7 @@ Item { property real _pulse: 0.08 SequentialAnimation on _pulse { - running: root._hovered && !S.Theme.reducedMotion + running: root._hovered && !S.ThemeUtil.reducedMotion loops: Animation.Infinite NumberAnimation { to: 0.22 @@ -156,7 +157,7 @@ Item { id: row property color accentColor: root.borderColor anchors.centerIn: parent - spacing: S.Theme.moduleSpacing + 2 + spacing: NS.ThemeService.moduleSpacing + 2 } // Separator lines overlaid between visible row children diff --git a/shell/modules/BarIcon.qml b/shell/modules/BarIcon.qml index c60df0d..93dc5ac 100644 --- a/shell/modules/BarIcon.qml +++ b/shell/modules/BarIcon.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS // Icon element with crossfade animation on icon change. // Pure visual component - tooltip handling lives in the parent BarModule. @@ -7,7 +8,7 @@ Text { id: root property string icon: "" property string minIcon: "" - property color accentColor: parent?.accentColor ?? S.Theme.base05 + property color accentColor: parent?.accentColor ?? NS.ThemeService.base05 property string _displayIcon: icon property string _pendingIcon: "" @@ -42,8 +43,8 @@ Text { width: minIcon ? Math.max(implicitWidth, _minIconMetrics.width) : implicitWidth horizontalAlignment: minIcon ? Text.AlignHCenter : Text.AlignLeft color: root.accentColor - font.pixelSize: S.Theme.fontSize + 1 - font.family: S.Theme.iconFontFamily + font.pixelSize: NS.ThemeService.fontSize + 1 + font.family: NS.ThemeService.iconFontFamily verticalAlignment: Text.AlignVCenter TextMetrics { diff --git a/shell/modules/BarLabel.qml b/shell/modules/BarLabel.qml index b27beb4..b7236df 100644 --- a/shell/modules/BarLabel.qml +++ b/shell/modules/BarLabel.qml @@ -1,5 +1,6 @@ import QtQuick import "../services" as S +import NovaStats as NS // Label element with minimum-width support via minText. // Pure visual component - tooltip handling lives in the parent BarModule. @@ -7,14 +8,14 @@ Text { id: root property string label: "" property string minText: "" - property color accentColor: parent?.accentColor ?? S.Theme.base05 + property color accentColor: parent?.accentColor ?? NS.ThemeService.base05 text: label width: minText ? Math.max(implicitWidth, _minMetrics.width) : implicitWidth horizontalAlignment: minText ? Text.AlignHCenter : Text.AlignLeft color: root.accentColor - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily verticalAlignment: Text.AlignVCenter TextMetrics { diff --git a/shell/modules/BarModule.qml b/shell/modules/BarModule.qml index f8282a7..7939ab3 100644 --- a/shell/modules/BarModule.qml +++ b/shell/modules/BarModule.qml @@ -2,6 +2,7 @@ import QtQuick import Quickshell import "." as M import "../services" as S +import NovaStats as NS // Unified base component for all bar modules. // Provides: tooltip on hover, panel state management, OSD flash support. @@ -20,7 +21,7 @@ Row { property string tooltip: "" property bool _hovered: false - property color accentColor: parent?.accentColor ?? S.Theme.base05 + property color accentColor: parent?.accentColor ?? NS.ThemeService.base05 property int cursorShape: Qt.PointingHandCursor // Panel state diff --git a/shell/modules/BatteryModule.qml b/shell/modules/BatteryModule.qml index d3b81c7..dc707a6 100644 --- a/shell/modules/BatteryModule.qml +++ b/shell/modules/BatteryModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing - active: S.Modules.battery.enable && S.BatteryService.available + spacing: NS.ThemeService.moduleSpacing + active: NS.ModulesService.batteryEnable && S.BatteryService.available tooltip: "Battery: " + Math.round(S.BatteryService.percent) + "%" + (S.BatteryService.charging ? " (charging)" : "") panelNamespace: "nova-battery" panelContentWidth: 240 @@ -35,7 +36,7 @@ M.BarModule { } color: S.BatteryService.stateColor opacity: root._blinkOpacity - font.pixelSize: S.Theme.fontSize + 2 + font.pixelSize: NS.ThemeService.fontSize + 2 anchors.verticalCenter: parent.verticalCenter } M.BarLabel { diff --git a/shell/modules/BluetoothModule.qml b/shell/modules/BluetoothModule.qml index 4045296..5d332ea 100644 --- a/shell/modules/BluetoothModule.qml +++ b/shell/modules/BluetoothModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing - active: S.Modules.bluetooth.enable && S.BluetoothService.state !== "unavailable" + spacing: NS.ThemeService.moduleSpacing + active: NS.ModulesService.bluetoothEnable && S.BluetoothService.state !== "unavailable" tooltip: { if (S.BluetoothService.state === "connected") return "Bluetooth: " + S.BluetoothService.device; @@ -33,7 +34,7 @@ M.BarModule { M.BarIcon { icon: "\uF294" - color: S.BluetoothService.state === "off" ? S.Theme.base04 : root.accentColor + color: S.BluetoothService.state === "off" ? NS.ThemeService.base04 : root.accentColor anchors.verticalCenter: parent.verticalCenter } M.BarLabel { diff --git a/shell/modules/ClockModule.qml b/shell/modules/ClockModule.qml index 062eb12..6932055 100644 --- a/shell/modules/ClockModule.qml +++ b/shell/modules/ClockModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.clock.enable - spacing: S.Theme.moduleSpacing + active: NS.ModulesService.clockEnable + spacing: NS.ThemeService.moduleSpacing tooltip: Qt.formatDateTime(clock.date, "dddd, dd. MMMM yyyy HH:mm:ss") panelNamespace: "nova-clock" panelContentWidth: 220 @@ -25,7 +26,7 @@ M.BarModule { } M.BarLabel { - font.pixelSize: S.Theme.fontSize + 1 + font.pixelSize: NS.ThemeService.fontSize + 1 label: Qt.formatDateTime(clock.date, "ddd, dd. MMM HH:mm") minText: "Wed, 00. Sep 00:00" anchors.verticalCenter: parent.verticalCenter diff --git a/shell/modules/CpuModule.qml b/shell/modules/CpuModule.qml index 3fa14d5..da5597c 100644 --- a/shell/modules/CpuModule.qml +++ b/shell/modules/CpuModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.cpu.enable - spacing: Math.max(1, S.Theme.moduleSpacing - 2) + active: NS.ModulesService.cpuEnable + spacing: Math.max(1, NS.ThemeService.moduleSpacing - 2) tooltip: "CPU: " + S.CpuService.usage + "% @ " + S.CpuService.freqGhz.toFixed(2) + " GHz" panelNamespace: "nova-cpu" panelContentWidth: 260 diff --git a/shell/modules/DiskModule.qml b/shell/modules/DiskModule.qml index 33a99db..f508423 100644 --- a/shell/modules/DiskModule.qml +++ b/shell/modules/DiskModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.disk.enable - spacing: Math.max(1, S.Theme.moduleSpacing - 2) + active: NS.ModulesService.diskEnable + spacing: Math.max(1, NS.ThemeService.moduleSpacing - 2) tooltip: "Disk: " + _rootPct + "% used" panelNamespace: "nova-disk" panelContentWidth: 260 @@ -21,7 +22,7 @@ M.BarModule { property var _mounts: S.SystemStats.diskMounts property int _rootPct: S.SystemStats.diskRootPct - readonly property int _warnThreshold: S.Modules.disk.warnThreshold ?? 85 + readonly property int _warnThreshold: NS.ModulesService.diskWarnThreshold ?? 85 readonly property bool _anyWarn: { for (const m of _mounts) if (m.pct >= _warnThreshold) @@ -31,13 +32,13 @@ M.BarModule { M.BarIcon { icon: "\uF0C9" - color: root._anyWarn ? S.Theme.base09 : root.accentColor + color: root._anyWarn ? NS.ThemeService.base09 : root.accentColor anchors.verticalCenter: parent.verticalCenter } M.BarLabel { label: root._rootPct + "%" minText: "100%" - color: root._anyWarn ? S.Theme.base09 : root.accentColor + color: root._anyWarn ? NS.ThemeService.base09 : root.accentColor anchors.verticalCenter: parent.verticalCenter } } diff --git a/shell/modules/DockModule.qml b/shell/modules/DockModule.qml index 016ee0f..07feca7 100644 --- a/shell/modules/DockModule.qml +++ b/shell/modules/DockModule.qml @@ -1,10 +1,11 @@ import QtQuick import "." as M import "../services" as S +import NovaStats as NS M.BarModule { id: root - active: S.Modules.dock.enable && S.DockState.mode !== "pinned" + active: NS.ModulesService.dockEnable && S.DockState.mode !== "pinned" tooltip: S.DockState.open ? "Close dock" : "Open dock" onTapped: S.DockState.toggle() diff --git a/shell/modules/GpuModule.qml b/shell/modules/GpuModule.qml index af90e1f..49bc444 100644 --- a/shell/modules/GpuModule.qml +++ b/shell/modules/GpuModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - spacing: Math.max(1, S.Theme.moduleSpacing - 2) - active: S.Modules.gpu.enable && S.SystemStats.gpuAvailable + spacing: Math.max(1, NS.ThemeService.moduleSpacing - 2) + active: NS.ModulesService.gpuEnable && S.SystemStats.gpuAvailable tooltip: "GPU: " + S.SystemStats.gpuUsage + "%" panelNamespace: "nova-gpu" panelContentWidth: 240 diff --git a/shell/modules/HoverPanel.qml b/shell/modules/HoverPanel.qml index b8d2854..b476513 100644 --- a/shell/modules/HoverPanel.qml +++ b/shell/modules/HoverPanel.qml @@ -3,6 +3,7 @@ import Quickshell import Quickshell.Wayland import "." as M import "../services" as S +import NovaStats as NS // Bar panel - fullscreen transparent window so content can resize freely // without triggering Wayland surface resizes. @@ -83,7 +84,7 @@ PanelWindow { } _winVisible = true; hideAnim.stop(); - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { panelContainer.opacity = 1; panelContainer.y = 0; } else { @@ -97,7 +98,7 @@ PanelWindow { if (!_winVisible) return; showAnim.stop(); - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { _winVisible = false; dismissed(); } else { @@ -185,7 +186,7 @@ PanelWindow { y: panelContainer.y width: panelContainer.width height: panelContainer.height - opacity: panelContainer.opacity * Math.max(S.Theme.barOpacity, 0.85) + opacity: panelContainer.opacity * Math.max(NS.ThemeService.barOpacity, 0.85) accentColor: root.accentColor } @@ -228,7 +229,7 @@ PanelWindow { color: "transparent" border.color: root.accentColor border.width: 1 - radius: S.Theme.radius + radius: NS.ThemeService.radius opacity: panelContainer.opacity } } diff --git a/shell/modules/IdleInhibitorModule.qml b/shell/modules/IdleInhibitorModule.qml index b8c9b45..9334745 100644 --- a/shell/modules/IdleInhibitorModule.qml +++ b/shell/modules/IdleInhibitorModule.qml @@ -2,10 +2,11 @@ import QtQuick import Quickshell import "." as M import "../services" as S +import NovaStats as NS M.BarModule { id: root - active: S.Modules.idleInhibitor.enable + active: NS.ModulesService.idleInhibitorEnable tooltip: { const parts = ["Idle inhibition: " + (S.IdleInhibitService.active ? "active" : "inactive")]; if (S.IdleInhibitService.inhibitors) @@ -23,7 +24,7 @@ M.BarModule { } M.BarIcon { - color: S.IdleInhibitService.active ? S.Theme.base09 : root.accentColor + color: S.IdleInhibitService.active ? NS.ThemeService.base09 : root.accentColor icon: S.IdleInhibitService.active ? "\uF06E" : "\uF070" anchors.verticalCenter: parent.verticalCenter } diff --git a/shell/modules/MachinectlModule.qml b/shell/modules/MachinectlModule.qml index 5f28523..6fa8197 100644 --- a/shell/modules/MachinectlModule.qml +++ b/shell/modules/MachinectlModule.qml @@ -5,10 +5,11 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.machinectl.enable + active: NS.ModulesService.machinectlEnable tooltip: { const n = S.MachinectlService.machines.length; return n === 0 ? "no containers" : n + " container" + (n === 1 ? "" : "s"); @@ -30,7 +31,7 @@ M.BarModule { } } - readonly property color _stateColor: S.MachinectlService.anyUnhealthy ? S.Theme.base0A : root.accentColor + readonly property color _stateColor: S.MachinectlService.anyUnhealthy ? NS.ThemeService.base0A : root.accentColor M.BarIcon { icon: "" diff --git a/shell/modules/MemoryModule.qml b/shell/modules/MemoryModule.qml index 5eb3df8..8347ea4 100644 --- a/shell/modules/MemoryModule.qml +++ b/shell/modules/MemoryModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.memory.enable - spacing: Math.max(1, S.Theme.moduleSpacing - 2) + active: NS.ModulesService.memoryEnable + spacing: Math.max(1, NS.ThemeService.moduleSpacing - 2) tooltip: "Memory: " + usedGb.toFixed(1) + " / " + totalGb.toFixed(1) + " GB" panelNamespace: "nova-memory" panelContentWidth: 240 diff --git a/shell/modules/MprisModule.qml b/shell/modules/MprisModule.qml index beb11d8..853adce 100644 --- a/shell/modules/MprisModule.qml +++ b/shell/modules/MprisModule.qml @@ -5,11 +5,12 @@ import Quickshell.Services.Mpris import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing - active: S.Modules.mpris.enable && S.MprisService.player !== null + spacing: NS.ThemeService.moduleSpacing + active: NS.ModulesService.mprisEnable && S.MprisService.player !== null tooltip: S.MprisService.player ? (S.MprisService.player.trackTitle || S.MprisService.player.identity || "Media") + (S.MprisService.playing ? " (playing)" : " (paused)") : "Media" panelNamespace: "nova-mpris" panelContentWidth: 280 diff --git a/shell/modules/NetworkModule.qml b/shell/modules/NetworkModule.qml index 2c8e4f5..191baca 100644 --- a/shell/modules/NetworkModule.qml +++ b/shell/modules/NetworkModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.network.enable - spacing: S.Theme.moduleSpacing + active: NS.ModulesService.networkEnable + spacing: NS.ThemeService.moduleSpacing tooltip: { if (state === "wifi") return "Wi-Fi: " + S.NetworkService.essid; @@ -45,13 +46,13 @@ M.BarModule { return "\uDB85\uDE16"; return "\uDB82\uDCFD"; } - color: root.state === "disconnected" ? S.Theme.base08 : root.accentColor + color: root.state === "disconnected" ? NS.ThemeService.base08 : root.accentColor anchors.verticalCenter: parent.verticalCenter } M.BarLabel { visible: root.state === "wifi" label: S.NetworkService.essid - color: root.state === "disconnected" ? S.Theme.base08 : root.accentColor + color: root.state === "disconnected" ? NS.ThemeService.base08 : root.accentColor anchors.verticalCenter: parent.verticalCenter } } diff --git a/shell/modules/NotifCard.qml b/shell/modules/NotifCard.qml index 10e7630..a80643d 100644 --- a/shell/modules/NotifCard.qml +++ b/shell/modules/NotifCard.qml @@ -2,6 +2,7 @@ import QtQuick import Quickshell.Services.Notifications import "." as M import "../services" as S +import NovaStats as NS // Shared notification card: background, progress bar, urgency bar, icon, text, dismiss button. // Does NOT include dismiss animation or dismiss logic — emits dismissRequested() instead. @@ -14,7 +15,7 @@ Item { property bool dismissOnAction: true property int iconSize: 32 property int bodyMaxLines: 3 - property color accentColor: S.Theme.base0D + property color accentColor: NS.ThemeService.base0D signal dismissRequested @@ -36,9 +37,9 @@ Item { // Background: base01, base02 on hover Rectangle { anchors.fill: parent - color: _hover.hovered ? S.Theme.base02 : S.Theme.base01 - opacity: _hover.hovered ? 1.0 : Math.max(S.Theme.barOpacity, 0.9) - radius: S.Theme.radius + color: _hover.hovered ? NS.ThemeService.base02 : NS.ThemeService.base01 + opacity: _hover.hovered ? 1.0 : Math.max(NS.ThemeService.barOpacity, 0.9) + radius: NS.ThemeService.radius Behavior on color { ColorAnimation { @@ -53,7 +54,7 @@ Item { anchors.bottom: parent.bottom anchors.left: parent.left width: parent.width * Math.min(1, Math.max(0, (root.notif?.hints?.value ?? 0) / 100)) - color: S.Theme.base03 + color: NS.ThemeService.base03 radius: parent.radius Behavior on width { @@ -73,7 +74,7 @@ Item { radius: 1 color: { const u = root.notif?.urgency ?? NotificationUrgency.Normal; - return u === NotificationUrgency.Critical ? S.Theme.base08 : u === NotificationUrgency.Low ? S.Theme.base04 : S.Theme.base0D; + return u === NotificationUrgency.Critical ? NS.ThemeService.base08 : u === NotificationUrgency.Low ? NS.ThemeService.base04 : NS.ThemeService.base0D; } } @@ -101,9 +102,9 @@ Item { anchors.top: parent.top anchors.topMargin: 8 text: "\uF00D" - color: _dismissHover.hovered ? S.Theme.base08 : S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.iconFontFamily + color: _dismissHover.hovered ? NS.ThemeService.base08 : NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.iconFontFamily opacity: _hover.hovered ? 1 : 0 HoverHandler { @@ -159,9 +160,9 @@ Item { Text { text: root.notif?.appName ?? "Notification" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily width: parent.width - _timeText.implicitWidth - 4 elide: Text.ElideRight } @@ -169,9 +170,9 @@ Item { Text { id: _timeText text: root.notif?.timeStr ?? "" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } } @@ -182,9 +183,9 @@ Item { Text { text: root.notif?.summary ?? "" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true elide: Text.ElideRight width: parent.width - _inlineTime.implicitWidth - 4 @@ -193,9 +194,9 @@ Item { Text { id: _inlineTime text: root.notif?.timeStr ?? "" - color: S.Theme.base03 - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily anchors.verticalCenter: parent.verticalCenter } } @@ -204,9 +205,9 @@ Item { visible: root.showAppName width: parent.width text: root.notif?.summary ?? "" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily font.bold: true elide: Text.ElideRight wrapMode: Text.WordWrap @@ -216,9 +217,9 @@ Item { Text { width: parent.width text: root.notif?.body ?? "" - color: S.Theme.base04 - font.pixelSize: S.Theme.fontSize - 1 - font.family: S.Theme.fontFamily + color: NS.ThemeService.base04 + font.pixelSize: NS.ThemeService.fontSize - 1 + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight wrapMode: Text.WordWrap maximumLineCount: root.bodyMaxLines @@ -239,9 +240,9 @@ Item { required property var modelData width: _actText.implicitWidth + 12 height: _actText.implicitHeight + 6 - radius: S.Theme.radius - color: _actHover.hovered ? S.Theme.base03 : "transparent" - border.color: S.Theme.base03 + radius: NS.ThemeService.radius + color: _actHover.hovered ? NS.ThemeService.base03 : "transparent" + border.color: NS.ThemeService.base03 border.width: 1 Text { @@ -249,8 +250,8 @@ Item { anchors.centerIn: parent text: parent.modelData.text color: root.accentColor - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily } HoverHandler { diff --git a/shell/modules/NotifPopup.qml b/shell/modules/NotifPopup.qml index d6a1400..e12548a 100644 --- a/shell/modules/NotifPopup.qml +++ b/shell/modules/NotifPopup.qml @@ -4,6 +4,7 @@ import Quickshell.Wayland import Quickshell.Services.Notifications import "." as M import "../services" as S +import NovaStats as NS PanelWindow { id: root @@ -32,7 +33,7 @@ PanelWindow { property var _knownIds: ({}) Repeater { - model: S.NotifService.popups.slice(0, S.Modules.notifications.maxPopups || 4) + model: S.NotifService.popups.slice(0, NS.ModulesService.notificationsMaxPopups || 4) delegate: Item { id: popupItem @@ -66,7 +67,7 @@ PanelWindow { _heightScale = 1; } else { popupCol._knownIds[modelData.id] = true; - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { opacity = 1; x = 0; _heightScale = 1; @@ -86,7 +87,7 @@ PanelWindow { } Behavior on y { - enabled: popupItem._entered && !S.Theme.reducedMotion + enabled: popupItem._entered && !S.ThemeUtil.reducedMotion NumberAnimation { duration: 200 easing.type: Easing.OutCubic @@ -127,7 +128,7 @@ PanelWindow { popupItem.modelData.beginDismiss(); _fullDismiss = !!full; slideIn.stop(); - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { full ? S.NotifService.dismiss(modelData.id) : S.NotifService.dismissPopup(modelData.id); } else { slideOut.start(); diff --git a/shell/modules/NotificationsModule.qml b/shell/modules/NotificationsModule.qml index 16a097f..98cf126 100644 --- a/shell/modules/NotificationsModule.qml +++ b/shell/modules/NotificationsModule.qml @@ -4,11 +4,12 @@ import Quickshell.Services.Notifications import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.notifications.enable - spacing: S.Theme.moduleSpacing + active: NS.ModulesService.notificationsEnable + spacing: NS.ThemeService.moduleSpacing tooltip: S.NotifService.count > 0 ? "Notifications: " + S.NotifService.count + (S.NotifService.dnd ? " (DND)" : "") : (S.NotifService.dnd ? "Do not disturb" : "No notifications") panelNamespace: "nova-notifications" panelContentWidth: 350 @@ -28,13 +29,13 @@ M.BarModule { return S.NotifService.count > 0 ? "\uDB80\uDCA0" : "\uDB82\uDE93"; return S.NotifService.count > 0 ? "\uDB84\uDD6B" : "\uDB80\uDC9C"; } - color: S.NotifService.dnd ? S.Theme.base04 : root.accentColor + color: S.NotifService.dnd ? NS.ThemeService.base04 : root.accentColor anchors.verticalCenter: parent.verticalCenter } M.BarLabel { id: countLabel label: S.NotifService.count > 0 ? String(S.NotifService.count) + (root.hasUrgent ? "!" : "") : "" - color: root.hasUrgent ? S.Theme.base08 : root.accentColor + color: root.hasUrgent ? NS.ThemeService.base08 : root.accentColor anchors.verticalCenter: parent.verticalCenter transform: Scale { @@ -67,7 +68,7 @@ M.BarModule { Connections { target: S.NotifService function onCountChanged() { - if (S.NotifService.count > 0 && !S.Theme.reducedMotion) + if (S.NotifService.count > 0 && !S.ThemeUtil.reducedMotion) popAnim.start(); } } diff --git a/shell/modules/PopupBackground.qml b/shell/modules/PopupBackground.qml index 1abc050..c668cf4 100644 --- a/shell/modules/PopupBackground.qml +++ b/shell/modules/PopupBackground.qml @@ -1,13 +1,14 @@ import QtQuick import "." as M import "../services" as S +import NovaStats as NS Rectangle { - property color accentColor: S.Theme.base05 + property color accentColor: NS.ThemeService.base05 - color: S.Theme.base01 - opacity: Math.max(S.Theme.barOpacity, 0.85) - radius: S.Theme.radius + color: NS.ThemeService.base01 + opacity: Math.max(NS.ThemeService.barOpacity, 0.85) + radius: NS.ThemeService.radius border.color: accentColor border.width: 1 } diff --git a/shell/modules/PowerModule.qml b/shell/modules/PowerModule.qml index 0030520..f0b3676 100644 --- a/shell/modules/PowerModule.qml +++ b/shell/modules/PowerModule.qml @@ -4,10 +4,11 @@ import Quickshell.Io import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.power.enable + active: NS.ModulesService.powerEnable tooltip: "Power menu" panelNamespace: "nova-power" panelContentWidth: 180 diff --git a/shell/modules/PowerProfileModule.qml b/shell/modules/PowerProfileModule.qml index b2d359e..64a38a6 100644 --- a/shell/modules/PowerProfileModule.qml +++ b/shell/modules/PowerProfileModule.qml @@ -1,10 +1,11 @@ import QtQuick import "." as M import "../services" as S +import NovaStats as NS M.BarModule { id: root - active: S.Modules.powerProfile.enable + active: NS.ModulesService.powerProfileEnable tooltip: "Power profile: " + (S.PowerProfileService.profile || "unknown") onTapped: { const cycle = ["performance", "balanced", "power-saver"]; @@ -13,7 +14,7 @@ M.BarModule { } M.BarIcon { - color: S.PowerProfileService.profile === "performance" ? S.Theme.base09 : S.PowerProfileService.profile === "power-saver" ? S.Theme.base0B : root.accentColor + color: S.PowerProfileService.profile === "performance" ? NS.ThemeService.base09 : S.PowerProfileService.profile === "power-saver" ? NS.ThemeService.base0B : root.accentColor icon: { if (S.PowerProfileService.profile === "performance") return "\uF0E7"; diff --git a/shell/modules/PrivacyModule.qml b/shell/modules/PrivacyModule.qml index a0b703c..960ce7d 100644 --- a/shell/modules/PrivacyModule.qml +++ b/shell/modules/PrivacyModule.qml @@ -3,10 +3,11 @@ import QtQuick.Effects import Quickshell.Services.Pipewire import "." as M import "../services" as S +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing + spacing: NS.ThemeService.moduleSpacing cursorShape: Qt.ArrowCursor readonly property bool _videoCapture: { @@ -35,21 +36,21 @@ M.BarModule { return false; } - active: S.Modules.privacy.enable && (root._videoCapture || root._audioIn) + active: NS.ModulesService.privacyEnable && (root._videoCapture || root._audioIn) // Screenshare indicator Text { visible: root._videoCapture text: "\uF03D" - color: S.Theme.base08 - font.pixelSize: S.Theme.fontSize + 2 - font.family: S.Theme.iconFontFamily + color: NS.ThemeService.base08 + font.pixelSize: NS.ThemeService.fontSize + 2 + font.family: NS.ThemeService.iconFontFamily anchors.verticalCenter: parent.verticalCenter layer.enabled: true layer.effect: MultiEffect { shadowEnabled: true - shadowColor: S.Theme.base08 + shadowColor: NS.ThemeService.base08 shadowBlur: 0.8 shadowVerticalOffset: 0 shadowHorizontalOffset: 0 @@ -65,15 +66,15 @@ M.BarModule { Text { visible: root._audioIn text: "\uF130" - color: S.Theme.base0B - font.pixelSize: S.Theme.fontSize + 2 - font.family: S.Theme.iconFontFamily + color: NS.ThemeService.base0B + font.pixelSize: NS.ThemeService.fontSize + 2 + font.family: NS.ThemeService.iconFontFamily anchors.verticalCenter: parent.verticalCenter layer.enabled: true layer.effect: MultiEffect { shadowEnabled: true - shadowColor: S.Theme.base0B + shadowColor: NS.ThemeService.base0B shadowBlur: 0.8 shadowVerticalOffset: 0 shadowHorizontalOffset: 0 diff --git a/shell/modules/PulseAnimation.qml b/shell/modules/PulseAnimation.qml index 555a258..3bf3802 100644 --- a/shell/modules/PulseAnimation.qml +++ b/shell/modules/PulseAnimation.qml @@ -3,7 +3,7 @@ import "../services" as S SequentialAnimation { id: root - loops: S.Theme.reducedMotion ? 1 : Animation.Infinite + loops: S.ThemeUtil.reducedMotion ? 1 : Animation.Infinite property real minOpacity: 0.4 property int halfDuration: 400 diff --git a/shell/modules/ScreenCorners.qml b/shell/modules/ScreenCorners.qml index 3be436c..562dc9b 100644 --- a/shell/modules/ScreenCorners.qml +++ b/shell/modules/ScreenCorners.qml @@ -3,6 +3,7 @@ import Quickshell import Quickshell.Wayland import "." as M import "../services" as S +import NovaStats as NS // Draws rounded black corners at the edges of each screen. // Disabled when screenRadius is 0. @@ -11,7 +12,7 @@ Item { required property var screen - readonly property int _r: S.Theme.screenRadius + readonly property int _r: NS.ThemeService.screenRadius component Corner: PanelWindow { id: win diff --git a/shell/modules/SystemdModule.qml b/shell/modules/SystemdModule.qml index 64cbed3..b558592 100644 --- a/shell/modules/SystemdModule.qml +++ b/shell/modules/SystemdModule.qml @@ -5,10 +5,11 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.systemd.enable + active: NS.ModulesService.systemdEnable tooltip: { const sys = S.SystemdService.systemState; const fc = S.SystemdService.totalFailedCount; @@ -40,8 +41,8 @@ M.BarModule { if (st === "running") return root.accentColor; if (st === "degraded") - return S.Theme.base0A; - return S.Theme.base08; + return NS.ThemeService.base0A; + return NS.ThemeService.base08; } M.BarIcon { diff --git a/shell/modules/TemperatureModule.qml b/shell/modules/TemperatureModule.qml index 9c27429..30f0f79 100644 --- a/shell/modules/TemperatureModule.qml +++ b/shell/modules/TemperatureModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.temperature.enable - spacing: Math.max(1, S.Theme.moduleSpacing - 2) + active: NS.ModulesService.temperatureEnable + spacing: Math.max(1, NS.ThemeService.moduleSpacing - 2) tooltip: "Temperature: " + _temp + "\u00B0C" panelNamespace: "nova-temperature" panelContentWidth: 220 @@ -25,9 +26,9 @@ M.BarModule { } } - readonly property int _warm: S.Modules.temperature.warm || 80 - readonly property int _hot: S.Modules.temperature.hot || 90 - readonly property string _deviceFilter: S.Modules.temperature.device || "" + readonly property int _warm: NS.ModulesService.temperatureWarm || 80 + readonly property int _hot: NS.ModulesService.temperatureHot || 90 + readonly property string _deviceFilter: NS.ModulesService.temperatureDevice || "" readonly property int _temp: { if (_deviceFilter !== "") { @@ -38,7 +39,7 @@ M.BarModule { return S.SystemStats.tempCelsius; } - property color _stateColor: _temp > _hot ? S.Theme.base08 : _temp > _warm ? S.Theme.base0A : root.accentColor + property color _stateColor: _temp > _hot ? NS.ThemeService.base08 : _temp > _warm ? NS.ThemeService.base0A : root.accentColor Behavior on _stateColor { ColorAnimation { duration: 300 diff --git a/shell/modules/Tooltip.qml b/shell/modules/Tooltip.qml index 7122069..01643f3 100644 --- a/shell/modules/Tooltip.qml +++ b/shell/modules/Tooltip.qml @@ -3,6 +3,7 @@ import Quickshell import Quickshell.Wayland import "." as M import "../services" as S +import NovaStats as NS PanelWindow { id: root @@ -17,7 +18,7 @@ PanelWindow { if (_shown) { _winVisible = true; hideAnim.stop(); - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { content.opacity = 1; content.y = 0; } else { @@ -25,7 +26,7 @@ PanelWindow { } } else { showAnim.stop(); - if (S.Theme.reducedMotion) { + if (S.ThemeUtil.reducedMotion) { _winVisible = false; } else { hideAnim.start(); @@ -43,8 +44,8 @@ PanelWindow { margins.top: 0 margins.left: Math.max(0, Math.min(Math.round(M.TooltipState.itemX - implicitWidth / 2), screen.width - implicitWidth)) - implicitWidth: label.implicitWidth + S.Theme.barPadding * 2 - implicitHeight: label.implicitHeight + S.Theme.barPadding * 2 + implicitWidth: label.implicitWidth + NS.ThemeService.barPadding * 2 + implicitHeight: label.implicitHeight + NS.ThemeService.barPadding * 2 ParallelAnimation { id: showAnim @@ -101,9 +102,9 @@ PanelWindow { anchors.centerIn: parent text: M.TooltipState.text.replace(/\n/g, "
") textFormat: Text.RichText - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } } } diff --git a/shell/modules/TooltipState.qml b/shell/modules/TooltipState.qml index ca1a5d5..f6eb246 100644 --- a/shell/modules/TooltipState.qml +++ b/shell/modules/TooltipState.qml @@ -2,11 +2,12 @@ pragma Singleton import QtQuick import "." as M import "../services" as S +import NovaStats as NS QtObject { property bool visible: false property string text: "" property real itemX: 0 property var screen: null - property color accentColor: S.Theme.base05 + property color accentColor: NS.ThemeService.base05 } diff --git a/shell/modules/TrayMenu.qml b/shell/modules/TrayMenu.qml index 6f1f390..5741bb5 100644 --- a/shell/modules/TrayMenu.qml +++ b/shell/modules/TrayMenu.qml @@ -2,6 +2,7 @@ import QtQuick import Quickshell import "." as M import "../services" as S +import NovaStats as NS M.HoverPanel { id: menuWindow @@ -29,8 +30,8 @@ M.HoverPanel { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: backArea.containsMouse ? S.Theme.base02 : "transparent" - radius: S.Theme.radius + color: backArea.containsMouse ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius } Text { @@ -38,9 +39,9 @@ M.HoverPanel { anchors.left: parent.left anchors.leftMargin: 12 text: "\u2039 Back" - color: S.Theme.base05 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: NS.ThemeService.base05 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } MouseArea { @@ -74,7 +75,7 @@ M.HoverPanel { anchors.leftMargin: 8 anchors.rightMargin: 8 height: 1 - color: S.Theme.base03 + color: NS.ThemeService.base03 } Rectangle { @@ -82,8 +83,8 @@ M.HoverPanel { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 - color: rowArea.containsMouse && entryItem.modelData.enabled ? S.Theme.base02 : "transparent" - radius: S.Theme.radius + color: rowArea.containsMouse && entryItem.modelData.enabled ? NS.ThemeService.base02 : "transparent" + radius: NS.ThemeService.radius } M.ThemedIcon { @@ -92,8 +93,8 @@ M.HoverPanel { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 12 - width: S.Theme.fontSize - height: S.Theme.fontSize + width: NS.ThemeService.fontSize + height: NS.ThemeService.fontSize source: entryItem.modelData.icon tint: menuWindow.accentColor fillMode: Image.PreserveAspectFit @@ -107,9 +108,9 @@ M.HoverPanel { anchors.right: entryChevron.visible ? entryChevron.left : parent.right anchors.rightMargin: entryChevron.visible ? 4 : 12 text: entryItem.modelData.text - color: entryItem.modelData.enabled ? S.Theme.base05 : S.Theme.base03 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: entryItem.modelData.enabled ? NS.ThemeService.base05 : NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily elide: Text.ElideRight } @@ -120,9 +121,9 @@ M.HoverPanel { anchors.right: parent.right anchors.rightMargin: 12 text: "\u203A" - color: entryItem.modelData.enabled ? S.Theme.base05 : S.Theme.base03 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.fontFamily + color: entryItem.modelData.enabled ? NS.ThemeService.base05 : NS.ThemeService.base03 + font.pixelSize: NS.ThemeService.fontSize + font.family: NS.ThemeService.fontFamily } MouseArea { diff --git a/shell/modules/TrayModule.qml b/shell/modules/TrayModule.qml index 81c8af8..9e5a550 100644 --- a/shell/modules/TrayModule.qml +++ b/shell/modules/TrayModule.qml @@ -6,13 +6,14 @@ import Quickshell.Services.SystemTray import "." as M import "../services" as S +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing + 2 + spacing: NS.ThemeService.moduleSpacing + 2 cursorShape: Qt.ArrowCursor - active: S.Modules.tray.enable && _trayRepeater.count > 0 + active: NS.ModulesService.trayEnable && _trayRepeater.count > 0 property var _activeMenu: null @@ -28,8 +29,8 @@ M.BarModule { property bool _hovered: false property real _pulseOpacity: 1 - implicitWidth: S.Theme.fontSize + 4 - implicitHeight: S.Theme.fontSize + 4 + implicitWidth: NS.ThemeService.fontSize + 4 + implicitHeight: NS.ThemeService.fontSize + 4 M.PulseAnimation on _pulseOpacity { running: iconItem._needsAttention @@ -43,7 +44,7 @@ M.BarModule { layer.enabled: iconItem._needsAttention || iconItem._hovered layer.effect: MultiEffect { shadowEnabled: true - shadowColor: iconItem._needsAttention ? S.Theme.base08 : S.Theme.base05 + shadowColor: iconItem._needsAttention ? NS.ThemeService.base08 : NS.ThemeService.base05 shadowBlur: iconItem._needsAttention ? 0.8 : 0.5 shadowVerticalOffset: 0 shadowHorizontalOffset: 0 @@ -52,7 +53,7 @@ M.BarModule { M.ThemedIcon { anchors.fill: parent source: iconItem.modelData.icon - tint: iconItem._needsAttention ? S.Theme.base08 : root.accentColor + tint: iconItem._needsAttention ? NS.ThemeService.base08 : root.accentColor } } diff --git a/shell/modules/VolumeModule.qml b/shell/modules/VolumeModule.qml index 32f46a4..a069ead 100644 --- a/shell/modules/VolumeModule.qml +++ b/shell/modules/VolumeModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - active: S.Modules.volume.enable - spacing: S.Theme.moduleSpacing + active: NS.ModulesService.volumeEnable + spacing: NS.ThemeService.moduleSpacing tooltip: "Volume: " + Math.round(volume * 100) + "%" + (muted ? " (muted)" : "") panelNamespace: "nova-volume" panelContentWidth: 220 @@ -21,7 +22,7 @@ M.BarModule { readonly property real volume: S.PipewireService.volume readonly property bool muted: S.PipewireService.muted readonly property string _volumeIcon: muted ? "\uF026" : (volume > 0.5 ? "\uF028" : (volume > 0 ? "\uF027" : "\uF026")) - readonly property color _volumeColor: muted ? S.Theme.base04 : root.accentColor + readonly property color _volumeColor: muted ? NS.ThemeService.base04 : root.accentColor property bool _volumeInit: false property bool _mutedInit: false diff --git a/shell/modules/WeatherModule.qml b/shell/modules/WeatherModule.qml index a682025..819db0a 100644 --- a/shell/modules/WeatherModule.qml +++ b/shell/modules/WeatherModule.qml @@ -3,11 +3,12 @@ import Quickshell import "." as M import "../services" as S import "../applets" as C +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing - active: S.Modules.weather.enable && S.WeatherService.available + spacing: NS.ThemeService.moduleSpacing + active: NS.ModulesService.weatherEnable && S.WeatherService.available tooltip: "Weather" panelNamespace: "nova-weather" panelContentWidth: 280 diff --git a/shell/modules/WindowTitleModule.qml b/shell/modules/WindowTitleModule.qml index d089c57..61776b2 100644 --- a/shell/modules/WindowTitleModule.qml +++ b/shell/modules/WindowTitleModule.qml @@ -4,10 +4,11 @@ import Quickshell import Quickshell.Widgets import "." as M import "../services" as S +import NovaStats as NS M.BarModule { id: root - spacing: S.Theme.moduleSpacing + spacing: NS.ThemeService.moduleSpacing tooltip: S.NiriIpc.focusedAppId ? S.NiriIpc.focusedAppId + "\n" + S.NiriIpc.focusedTitle : S.NiriIpc.focusedTitle cursorShape: Qt.ArrowCursor @@ -27,7 +28,7 @@ M.BarModule { id: _icon visible: root._iconSource !== "" source: root._iconSource - implicitSize: S.Theme.fontSize + 2 + implicitSize: NS.ThemeService.fontSize + 2 anchors.verticalCenter: parent.verticalCenter layer.enabled: true layer.effect: MultiEffect { diff --git a/shell/modules/WorkspacesModule.qml b/shell/modules/WorkspacesModule.qml index ad099a4..cb2206e 100644 --- a/shell/modules/WorkspacesModule.qml +++ b/shell/modules/WorkspacesModule.qml @@ -3,10 +3,11 @@ import Quickshell import Quickshell.Io import "." as M import "../services" as S +import NovaStats as NS M.BarModule { id: root - active: S.Modules.workspaces.enable + active: NS.ModulesService.workspacesEnable spacing: 4 cursorShape: Qt.ArrowCursor @@ -79,10 +80,10 @@ M.BarModule { } } - width: S.Theme.fontSize + 4 - height: S.Theme.fontSize + 4 + width: NS.ThemeService.fontSize + 4 + height: NS.ThemeService.fontSize + 4 radius: width / 2 - color: pill.active ? root.accentColor : (pill._hovered ? S.Theme.base03 : S.Theme.base02) + color: pill.active ? root.accentColor : (pill._hovered ? NS.ThemeService.base03 : NS.ThemeService.base02) Behavior on color { ColorAnimation { duration: 150 @@ -92,9 +93,9 @@ M.BarModule { Text { anchors.centerIn: parent text: pill.modelData.idx - color: pill.active ? S.Theme.base00 : root.accentColor - font.pixelSize: S.Theme.fontSize - 2 - font.family: S.Theme.fontFamily + color: pill.active ? NS.ThemeService.base00 : root.accentColor + font.pixelSize: NS.ThemeService.fontSize - 2 + font.family: NS.ThemeService.fontFamily font.bold: pill.active } diff --git a/shell/services/BacklightService.qml b/shell/services/BacklightService.qml index caa4cd8..be3b0d4 100644 --- a/shell/services/BacklightService.qml +++ b/shell/services/BacklightService.qml @@ -4,6 +4,7 @@ import QtQuick import Quickshell import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root @@ -12,7 +13,7 @@ QtObject { readonly property bool available: _blDev !== "" function adjust(delta) { - const step = S.Modules.backlight.step || 5; + const step = NS.ModulesService.backlightStep || 5; _adjProc.cmd = delta > 0 ? "light -A " + step : "light -U " + step; _adjProc.running = true; } diff --git a/shell/services/BatteryService.qml b/shell/services/BatteryService.qml index e8e115f..5bf04a1 100644 --- a/shell/services/BatteryService.qml +++ b/shell/services/BatteryService.qml @@ -3,6 +3,7 @@ pragma Singleton import QtQuick import Quickshell.Services.UPower import "." as S +import NovaStats as NS QtObject { id: root @@ -20,11 +21,11 @@ QtObject { readonly property bool healthSupported: dev?.healthSupported ?? false readonly property real healthPercent: (dev?.healthPercentage ?? 1) * 100 - readonly property int critThresh: S.Modules.battery.critical || 15 - readonly property int warnThresh: S.Modules.battery.warning || 25 + readonly property int critThresh: NS.ModulesService.batteryCritical || 15 + readonly property int warnThresh: NS.ModulesService.batteryWarning || 25 readonly property bool critical: percent < critThresh && !charging - readonly property color stateColor: charging ? S.Theme.base0B : critical ? S.Theme.base09 : percent < warnThresh ? S.Theme.base0A : S.Theme.base05 + readonly property color stateColor: charging ? NS.ThemeService.base0B : critical ? NS.ThemeService.base09 : percent < warnThresh ? NS.ThemeService.base0A : NS.ThemeService.base05 // 24h history (1440 samples @ 60s) property var history: [] diff --git a/shell/services/BluetoothService.qml b/shell/services/BluetoothService.qml index 608f1c6..0f5b483 100644 --- a/shell/services/BluetoothService.qml +++ b/shell/services/BluetoothService.qml @@ -3,6 +3,7 @@ pragma Singleton import QtQuick import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root @@ -38,7 +39,7 @@ QtObject { // Status polling (bar icon state) property Process _statusProc: Process { - running: S.Modules.bluetooth.enable + running: NS.ModulesService.bluetoothEnable command: ["sh", "-c", "s=$(bluetoothctl show 2>/dev/null); " + "[ -z \"$s\" ] && echo unavailable && exit; " + "echo \"$s\" | grep -q 'Powered: yes' || { echo off:; exit; }; " + "info=$(bluetoothctl info 2>/dev/null); " + "d=$(echo \"$info\" | awk -F': ' '/\\tName:/{n=$2}/Connected: yes/{c=1}END{if(c)print n}'); " + "[ -n \"$d\" ] && echo \"connected:$d\" || { echo on:; exit; }; " + "bat=$(echo \"$info\" | awk -F': ' '/Battery Percentage.*\\(/{gsub(/[^0-9]/,\"\",$2);print $2}'); " + "[ -n \"$bat\" ] && echo \"bat:$bat\""] stdout: StdioCollector { onStreamFinished: { @@ -58,7 +59,7 @@ QtObject { // Event-driven: watch BlueZ DBus property changes property Process _monitor: Process { - running: S.Modules.bluetooth.enable + running: NS.ModulesService.bluetoothEnable command: ["sh", "-c", "dbus-monitor --system \"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path_namespace='/org/bluez'\" 2>/dev/null"] stdout: SplitParser { splitMarker: "\n" @@ -73,7 +74,7 @@ QtObject { property Timer _fallbackPoll: Timer { interval: 60000 - running: S.Modules.bluetooth.enable + running: NS.ModulesService.bluetoothEnable repeat: true onTriggered: root.refresh() } diff --git a/shell/services/CpuService.qml b/shell/services/CpuService.qml index 71bec6b..a0a4859 100644 --- a/shell/services/CpuService.qml +++ b/shell/services/CpuService.qml @@ -39,7 +39,7 @@ QtObject { property Timer _pollTimer: Timer { interval: { - const ms = S.Modules.statsDaemon.interval; + const ms = NS.ModulesService.statsDaemonInterval; return ms > 0 ? ms : 4000; } running: true diff --git a/shell/services/LockService.qml b/shell/services/LockService.qml index 6dadafc..9ce509d 100644 --- a/shell/services/LockService.qml +++ b/shell/services/LockService.qml @@ -3,11 +3,12 @@ pragma Singleton import QtQuick import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root - readonly property bool enabled: S.Modules.lock.enable + readonly property bool enabled: NS.ModulesService.lockEnable property bool locked: false property string sessionPath: "" diff --git a/shell/services/MachinectlService.qml b/shell/services/MachinectlService.qml index a954773..30a39f0 100644 --- a/shell/services/MachinectlService.qml +++ b/shell/services/MachinectlService.qml @@ -3,6 +3,7 @@ pragma Singleton import QtQuick import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root @@ -71,8 +72,8 @@ QtObject { } property Timer _poll: Timer { - interval: S.Modules.machinectl.interval ?? 15000 - running: S.Modules.machinectl.enable + interval: NS.ModulesService.machinectlInterval ?? 15000 + running: NS.ModulesService.machinectlEnable repeat: true triggeredOnStart: true onTriggered: if (!root._listProc.running) diff --git a/shell/services/Modules.qml b/shell/services/Modules.qml deleted file mode 100644 index be8b2bf..0000000 --- a/shell/services/Modules.qml +++ /dev/null @@ -1,180 +0,0 @@ -pragma Singleton - -import QtQuick -import Quickshell -import Quickshell.Io - -QtObject { - id: root - - property var workspaces: ({ - enable: true - }) - property var tray: ({ - enable: true - }) - property var windowTitle: ({ - enable: true - }) - property var clock: ({ - enable: true - }) - property var notifications: ({ - enable: true, - timeout: 3000, - maxPopups: 4, - maxVisible: 10, - maxHistory: -1 - }) - property var mpris: ({ - enable: true - }) - property var volume: ({ - enable: true - }) - property var bluetooth: ({ - enable: true - }) - property var backlight: ({ - enable: true, - step: 5 - }) - property var network: ({ - enable: true - }) - property var powerProfile: ({ - enable: true - }) - property var idleInhibitor: ({ - enable: true - }) - property var weather: ({ - enable: true, - args: ["--nerd"], - interval: 3600000 - }) - property var temperature: ({ - enable: true, - warm: 80, - hot: 90, - device: "" - }) - property var gpu: ({ - enable: true, - warm: 70, - hot: 85 - }) - property var cpu: ({ - enable: true - }) - property var memory: ({ - enable: true - }) - property var disk: ({ - enable: true, - interval: 30000, - warnThreshold: 85 - }) - property var battery: ({ - enable: true, - warning: 25, - critical: 15 - }) - property var privacy: ({ - enable: true - }) - property var screenCorners: ({ - enable: true - }) - property var power: ({ - enable: true - }) - property var backgroundOverlay: ({ - enable: true - }) - property var overviewBackdrop: ({ - enable: true - }) - property var lock: ({ - enable: true, - screenshot: true, - notifications: true, - mpris: true, - volume: true, - weather: true, - threatEffect: true - }) - property var dock: ({ - enable: true, - width: 300, - applets: { - clock: true, - cpu: true, - gpu: true, - memory: true, - temperature: true, - disk: true, - battery: true, - network: true, - bluetooth: true, - volume: true, - backlight: true, - weather: true, - mpris: true, - notifications: true, - power: true - } - }) - property var systemd: ({ - enable: true, - interval: 15000 - }) - property var machinectl: ({ - enable: true, - interval: 15000 - }) - property var statsDaemon: ({ - interval: -1 - }) - - // All module keys that have an enable flag — used to default-enable anything - // not explicitly mentioned in modules.json - readonly property var _moduleKeys: ["workspaces", "tray", "windowTitle", "clock", "notifications", "mpris", "volume", "bluetooth", "backlight", "network", "powerProfile", "idleInhibitor", "weather", "temperature", "gpu", "cpu", "memory", "disk", "battery", "privacy", "screenCorners", "power", "backgroundOverlay", "overviewBackdrop", "lock", "dock", "systemd", "machinectl"] - - // Fallback: if modules.json doesn't exist, enable everything - Component.onCompleted: _apply("{}") - - property FileView _file: FileView { - path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/modules.json" - watchChanges: true - onFileChanged: reload() - onLoaded: root._apply(text()) - } - - function _apply(raw) { - let data = {}; - try { - data = JSON.parse(raw); - } catch (e) {} - // Enable all modules that aren't explicitly mentioned in the JSON - for (const k of _moduleKeys) { - if (!(k in data)) - root[k] = Object.assign({}, root[k], { - enable: true - }); - } - - // Apply JSON overrides - for (const k of Object.keys(data)) { - if (!(k in root)) - continue; - const v = data[k]; - if (typeof v === "object" && v !== null) - root[k] = Object.assign({}, root[k], v); - else if (typeof v === "boolean") - root[k] = Object.assign({}, root[k], { - enable: v - }); - } - } -} diff --git a/shell/services/NetworkService.qml b/shell/services/NetworkService.qml index 2c2b531..f84fbfb 100644 --- a/shell/services/NetworkService.qml +++ b/shell/services/NetworkService.qml @@ -3,6 +3,7 @@ pragma Singleton import QtQuick import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root @@ -40,7 +41,7 @@ QtObject { // Status polling property Process _statusProc: Process { - running: S.Modules.network.enable + running: NS.ModulesService.networkEnable command: ["sh", "-c", "line=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>/dev/null | head -1); if [ -z \"$line\" ]; then dev=$(nmcli -t -f DEVICE,STATE device 2>/dev/null | grep ':connected' | grep -v ':unmanaged\\|:unavailable\\|:disconnected\\|:connecting' | head -1 | cut -d: -f1); [ -n \"$dev\" ] && line=\"linked:linked:$dev\"; fi; [ -z \"$line\" ] && exit 0; echo \"$line\"; dev=$(echo \"$line\" | cut -d: -f3); ip=$(nmcli -t -f IP4.ADDRESS device show \"$dev\" 2>/dev/null | head -1 | cut -d: -f2); echo \"ip:${ip:-}\"; sig=$(nmcli -t -f GENERAL.SIGNAL device show \"$dev\" 2>/dev/null | head -1 | cut -d: -f2); echo \"sig:${sig:-}\""] stdout: StdioCollector { onStreamFinished: { @@ -76,7 +77,7 @@ QtObject { // Event-driven monitor property Process _monitor: Process { - running: S.Modules.network.enable + running: NS.ModulesService.networkEnable command: ["nmcli", "monitor"] stdout: SplitParser { splitMarker: "\n" @@ -92,7 +93,7 @@ QtObject { // Fallback poll property Timer _fallbackPoll: Timer { interval: 60000 - running: S.Modules.network.enable + running: NS.ModulesService.networkEnable repeat: true onTriggered: root.refresh() } diff --git a/shell/services/NotifService.qml b/shell/services/NotifService.qml index e3dde41..5a7ca23 100644 --- a/shell/services/NotifService.qml +++ b/shell/services/NotifService.qml @@ -4,6 +4,7 @@ import QtQuick import Quickshell import Quickshell.Services.Notifications import "." as S +import NovaStats as NS QtObject { id: root @@ -96,7 +97,7 @@ QtObject { }); // Trim excess popups - const max = S.Modules.notifications.maxPopups || 4; + const max = NS.ModulesService.notificationsMaxPopups || 4; const currentPopups = root.list.filter(n => n.popup); if (currentPopups.length > max) { for (let i = max; i < currentPopups.length; i++) @@ -106,13 +107,13 @@ QtObject { // Auto-expire popup (skip for critical) if (item.popup && !isCritical) { - const timeout = notif.expireTimeout > 0 ? notif.expireTimeout : (S.Modules.notifications.timeout || 3000); + const timeout = notif.expireTimeout > 0 ? notif.expireTimeout : (NS.ModulesService.notificationsTimeout || 3000); item._expireTimer.interval = timeout; item._expireTimer.running = true; } // Trim history (-1 = unlimited) - const maxHistory = S.Modules.notifications.maxHistory ?? -1; + const maxHistory = NS.ModulesService.notificationsMaxHistory ?? -1; while (maxHistory > 0 && root.list.length > maxHistory) { const old = root.list.pop(); old.finishDismiss(); diff --git a/shell/services/PowerProfileService.qml b/shell/services/PowerProfileService.qml index 834be8f..abc6ccc 100644 --- a/shell/services/PowerProfileService.qml +++ b/shell/services/PowerProfileService.qml @@ -3,6 +3,7 @@ pragma Singleton import QtQuick import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root @@ -11,7 +12,7 @@ QtObject { readonly property bool powerSaver: profile === "power-saver" property var _proc: Process { - running: S.Modules.powerProfile.enable + running: NS.ModulesService.powerProfileEnable command: ["powerprofilesctl", "get"] stdout: StdioCollector { onStreamFinished: root.profile = text.trim() @@ -19,7 +20,7 @@ QtObject { } property var _monitor: Process { - running: S.Modules.powerProfile.enable + running: NS.ModulesService.powerProfileEnable command: ["sh", "-c", "dbus-monitor --system \"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/net/hadess/PowerProfiles'\" 2>/dev/null"] stdout: SplitParser { splitMarker: "\n" @@ -34,7 +35,7 @@ QtObject { property var _poll: Timer { interval: 60000 - running: S.Modules.powerProfile.enable + running: NS.ModulesService.powerProfileEnable repeat: true onTriggered: root._proc.running = true } diff --git a/shell/services/SystemStats.qml b/shell/services/SystemStats.qml index 76dd9cd..197805c 100644 --- a/shell/services/SystemStats.qml +++ b/shell/services/SystemStats.qml @@ -52,7 +52,7 @@ QtObject { // Drive the Rust service from QML timers; both intervals read from Modules config. property Timer _statsTimer: Timer { interval: { - const ms = M.Modules.statsDaemon.interval; + const ms = NS.ModulesService.statsDaemonInterval; return ms > 0 ? ms : 4000; } running: true @@ -62,7 +62,7 @@ QtObject { } property Timer _diskTimer: Timer { - interval: M.Modules.disk.interval || 30000 + interval: NS.ModulesService.diskInterval || 30000 running: true repeat: true triggeredOnStart: true diff --git a/shell/services/SystemdService.qml b/shell/services/SystemdService.qml index ae974ac..afe9fcc 100644 --- a/shell/services/SystemdService.qml +++ b/shell/services/SystemdService.qml @@ -3,6 +3,7 @@ pragma Singleton import QtQuick import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root @@ -64,8 +65,8 @@ QtObject { } property Timer _poll: Timer { - interval: S.Modules.systemd.interval ?? 15000 - running: S.Modules.systemd.enable + interval: NS.ModulesService.systemdInterval ?? 15000 + running: NS.ModulesService.systemdEnable repeat: true triggeredOnStart: true onTriggered: if (!root._pollProc.running) diff --git a/shell/services/Theme.qml b/shell/services/Theme.qml deleted file mode 100644 index 05311e2..0000000 --- a/shell/services/Theme.qml +++ /dev/null @@ -1,95 +0,0 @@ -pragma Singleton - -import QtQuick -import Quickshell -import Quickshell.Io - -QtObject { - id: root - - // base16 palette, overwritten from ~/.config/nova-shell/theme.json - property color base00: "#1e1e2e" - property color base01: "#181825" - property color base02: "#313244" - property color base03: "#45475a" - property color base04: "#585b70" - property color base05: "#cdd6f4" - property color base06: "#f5e0dc" - property color base07: "#b4befe" - property color base08: "#f38ba8" - property color base09: "#fab387" - property color base0A: "#f9e2af" - property color base0B: "#a6e3a1" - property color base0C: "#94e2d5" - property color base0D: "#89b4fa" - property color base0E: "#cba6f7" - property color base0F: "#f2cdcd" - - property string fontFamily: "sans-serif" - property string iconFontFamily: "Symbols Nerd Font" - property int fontSize: 12 - property real barOpacity: 0.9 - property int barHeight: 32 - property int barPadding: 8 - property int moduleSpacing: 4 - property int groupSpacing: 6 - property int groupPadding: 8 - property int radius: 4 - property int screenRadius: 15 - property bool _reducedMotionConfig: false - readonly property bool reducedMotion: _reducedMotionConfig || PowerProfileService.powerSaver - - // Green -> yellow -> red gradient for 0-100% load/usage values - function loadColor(pct) { - const t = Math.max(0, Math.min(100, pct)) / 100; - const a = t < 0.5 ? root.base0B : root.base0A; - const b = t < 0.5 ? root.base0A : root.base08; - const u = t < 0.5 ? t * 2 : (t - 0.5) * 2; - return Qt.rgba(a.r + (b.r - a.r) * u, a.g + (b.g - a.g) * u, a.b + (b.b - a.b) * u, 1); - } - - property FileView _themeFile: FileView { - path: (Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")) + "/nova-shell/theme.json" - watchChanges: true - onFileChanged: reload() - onLoaded: root._apply(text()) - } - - function _apply(raw) { - let data; - try { - data = JSON.parse(raw); - } catch (e) { - return; - } - const c = data.colors || {}; - for (const k of Object.keys(c)) { - if (k in root) - root[k] = c[k]; - } - if (data.fontFamily) - root.fontFamily = data.fontFamily; - if (data.iconFontFamily) - root.iconFontFamily = data.iconFontFamily; - if (data.fontSize) - root.fontSize = data.fontSize; - if (data.barOpacity !== undefined) - root.barOpacity = data.barOpacity; - if (data.barHeight !== undefined) - root.barHeight = data.barHeight; - if (data.barPadding !== undefined) - root.barPadding = data.barPadding; - if (data.moduleSpacing !== undefined) - root.moduleSpacing = data.moduleSpacing; - if (data.groupSpacing !== undefined) - root.groupSpacing = data.groupSpacing; - if (data.groupPadding !== undefined) - root.groupPadding = data.groupPadding; - if (data.radius !== undefined) - root.radius = data.radius; - if (data.screenRadius !== undefined) - root.screenRadius = data.screenRadius; - if (data.reducedMotion !== undefined) - root._reducedMotionConfig = data.reducedMotion; - } -} diff --git a/shell/services/ThemeUtil.qml b/shell/services/ThemeUtil.qml new file mode 100644 index 0000000..4201854 --- /dev/null +++ b/shell/services/ThemeUtil.qml @@ -0,0 +1,23 @@ +pragma Singleton + +import QtQuick +import NovaStats as NS +import "." as S + +// Helpers that need QML-side state (PowerProfileService) or compute over the +// raw theme primitives. Plain values come straight from NS.ThemeService. +QtObject { + id: root + + // Effective reduced-motion: configured value OR power-saver active. + readonly property bool reducedMotion: NS.ThemeService.reducedMotionConfig || S.PowerProfileService.powerSaver + + // Green -> yellow -> red gradient for 0-100% load/usage values. + function loadColor(pct) { + const t = Math.max(0, Math.min(100, pct)) / 100; + const a = t < 0.5 ? NS.ThemeService.base0B : NS.ThemeService.base0A; + const b = t < 0.5 ? NS.ThemeService.base0A : NS.ThemeService.base08; + const u = t < 0.5 ? t * 2 : (t - 0.5) * 2; + return Qt.rgba(a.r + (b.r - a.r) * u, a.g + (b.g - a.g) * u, a.b + (b.b - a.b) * u, 1); + } +} diff --git a/shell/services/WeatherService.qml b/shell/services/WeatherService.qml index ed89fac..65ab4cf 100644 --- a/shell/services/WeatherService.qml +++ b/shell/services/WeatherService.qml @@ -4,6 +4,7 @@ import QtQuick import Quickshell import Quickshell.Io import "." as S +import NovaStats as NS QtObject { id: root @@ -13,8 +14,8 @@ QtObject { readonly property bool available: icon !== "" property Process _proc: Process { - running: S.Modules.weather.enable - command: ["wttrbar"].concat(S.Modules.weather.args) + running: NS.ModulesService.weatherEnable + command: ["wttrbar"].concat(NS.ModulesService.weatherArgs) stdout: StdioCollector { onStreamFinished: { try { @@ -30,8 +31,8 @@ QtObject { } property Timer _poll: Timer { - interval: S.Modules.weather.interval || 3600000 - running: S.Modules.weather.enable + interval: NS.ModulesService.weatherInterval || 3600000 + running: NS.ModulesService.weatherEnable repeat: true onTriggered: root._proc.running = true } diff --git a/shell/services/qmldir b/shell/services/qmldir index e435fe4..6169b75 100644 --- a/shell/services/qmldir +++ b/shell/services/qmldir @@ -9,7 +9,6 @@ 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 Modules 1.0 Modules.qml singleton MprisService 1.0 MprisService.qml singleton NetworkService 1.0 NetworkService.qml singleton NiriIpc 1.0 NiriIpc.qml @@ -20,6 +19,6 @@ singleton ScreenshotService 1.0 ScreenshotService.qml singleton SleepService 1.0 SleepService.qml singleton SystemStats 1.0 SystemStats.qml singleton SystemdService 1.0 SystemdService.qml -singleton Theme 1.0 Theme.qml +singleton ThemeUtil 1.0 ThemeUtil.qml singleton WeatherService 1.0 WeatherService.qml # keep-sorted end diff --git a/shell/shell.qml b/shell/shell.qml index 522a412..eff500c 100644 --- a/shell/shell.qml +++ b/shell/shell.qml @@ -5,6 +5,7 @@ import "modules" import "services" import "dock" as Dock import "lock" as Lock +import NovaStats as NS import Quickshell ShellRoot { @@ -35,49 +36,49 @@ ShellRoot { } LazyLoader { - active: (Modules.notifications.maxPopups ?? 4) > 0 + active: NS.ModulesService.notificationsMaxPopups > 0 NotifPopup { screen: scope.modelData } } LazyLoader { - active: Modules.backgroundOverlay.enable + active: NS.ModulesService.backgroundOverlayEnable BackgroundOverlay { screen: scope.modelData } } LazyLoader { - active: Modules.overviewBackdrop.enable && NiriIpc.available + active: NS.ModulesService.overviewBackdropEnable && NiriIpc.available OverviewBackdrop { screen: scope.modelData } } LazyLoader { - active: Modules.lock.enable && (Modules.lock.screenshot ?? true) + active: NS.ModulesService.lockEnable && NS.ModulesService.lockScreenshot ScreenCapture { screen: scope.modelData } } LazyLoader { - active: Modules.screenCorners.enable + active: NS.ModulesService.screenCornersEnable ScreenCorners { screen: scope.modelData } } LazyLoader { - active: Modules.dock.enable && scope._isRightmost + active: NS.ModulesService.dockEnable && scope._isRightmost Dock.AppletDock { screen: scope.modelData } } LazyLoader { - active: Modules.dock.enable && scope._isRightmost + active: NS.ModulesService.dockEnable && scope._isRightmost Dock.DockEdgeTrigger { screen: scope.modelData } diff --git a/test/qmllint-baseline.txt b/test/qmllint-baseline.txt index f3c778e..79d3638 100644 --- a/test/qmllint-baseline.txt +++ b/test/qmllint-baseline.txt @@ -1,57 +1,199 @@ +shell/applets/AppletActionBar.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/BacklightApplet.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/BacklightApplet.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/BatteryApplet.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/BatteryApplet.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/BatteryApplet.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/BatteryApplet.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/applets/BatteryApplet.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/BatteryApplet.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/BluetoothApplet.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/BluetoothApplet.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/BluetoothApplet.qml: Unqualified access [unqualified] +shell/applets/ClockApplet.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/ClockApplet.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/ClockApplet.qml: Unqualified access [unqualified] shell/applets/CpuApplet.qml: Member "_barColor" not found on type "QQuickItem" [missing-property] shell/applets/CpuApplet.qml: Member "_f" not found on type "QQuickItem" [missing-property] shell/applets/CpuApplet.qml: Member "_throttled" not found on type "QQuickItem" [missing-property] +shell/applets/CpuApplet.qml: Member "_u" not found on type "QQuickItem" [missing-property] shell/applets/CpuApplet.qml: Member "index" not found on type "QQuickItem" [missing-property] +shell/applets/CpuApplet.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/CpuApplet.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/CpuApplet.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/CpuApplet.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/CpuApplet.qml: Unqualified access [unqualified] +shell/applets/DiskApplet.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/DiskApplet.qml: Unqualified access [unqualified] +shell/applets/GpuApplet.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/GpuApplet.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/GpuApplet.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/GpuApplet.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/GpuApplet.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/HexWaveBackground.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/applets/HexWaveBackground.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/applets/HexWaveBackground.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/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] shell/applets/MemoryApplet.qml: Unqualified access [unqualified] shell/applets/MprisApplet.qml: Member "frac" not found on type "QQuickItem" [missing-property] shell/applets/MprisApplet.qml: Member "join" not found on type "QString" [missing-property] +shell/applets/MprisApplet.qml: Member "spacing" not found on type "Repeater" [missing-property] +shell/applets/MprisApplet.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/MprisApplet.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/MprisApplet.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/MprisApplet.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/MprisApplet.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/MprisApplet.qml: Unqualified access [unqualified] +shell/applets/NetworkApplet.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/NetworkApplet.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/NetworkApplet.qml: Unqualified access [unqualified] shell/applets/NotifApplet.qml: Member "_notif" not found on type "QQuickItem" [missing-property] shell/applets/NotifApplet.qml: Member "_type" not found on type "QQuickItem" [missing-property] +shell/applets/NotifApplet.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/NotifApplet.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/NotifApplet.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/NotifApplet.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/applets/NotifApplet.qml: Unqualified access [unqualified] +shell/applets/PowerApplet.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/PowerApplet.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/PowerApplet.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/PowerApplet.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/PowerApplet.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/PowerApplet.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/applets/PowerApplet.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/PowerApplet.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] +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/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] +shell/applets/SystemdUnitRow.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/TemperatureApplet.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/TemperatureApplet.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/TemperatureApplet.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/TemperatureApplet.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/TemperatureApplet.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/TemperatureApplet.qml: Unqualified access [unqualified] +shell/applets/VolumeApplet.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/VolumeApplet.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/VolumeApplet.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/VolumeApplet.qml: Unqualified access [unqualified] +shell/applets/WeatherApplet.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/dock/AppletDock.qml: Could not find property "top". [missing-property] +shell/dock/AppletDock.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/dock/AppletDock.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/dock/AppletDock.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/dock/AppletDock.qml: Type PanelWindow is not creatable. [uncreatable-type] shell/dock/AppletDock.qml: Type margins is used but it is not resolved [unresolved-type] shell/dock/AppletDock.qml: unknown grouped property scope margins. [unqualified] +shell/dock/DockCard.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/dock/DockCard.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/dock/DockCard.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/dock/DockCard.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/dock/DockCard.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/dock/DockEdgeTrigger.qml: Type PanelWindow is not creatable. [uncreatable-type] shell/lock/Lock.qml: Unqualified access [unqualified] shell/lock/LockAuth.qml: Unqualified access [unqualified] +shell/lock/LockClock.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/lock/LockClock.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/lock/LockClock.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/lock/LockClock.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/lock/LockInput.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/lock/LockInput.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/lock/LockInput.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/lock/LockInput.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/lock/LockInput.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/lock/LockInput.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] +shell/lock/LockNotifPills.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/lock/LockNotifPills.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/lock/LockNotifPills.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/lock/LockSurface.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/lock/LockSurface.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/lock/LockSurface.qml: Unqualified access [unqualified] +shell/lock/LockWidgets.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/lock/LockWidgets.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/lock/LockWidgets.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/lock/LockWidgets.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/lock/LockWidgets.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] +shell/lock/LockWidgets.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/modules/BackgroundOverlay.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/modules/BackgroundOverlay.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/modules/BackgroundOverlay.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/BackgroundOverlay.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/BackgroundOverlay.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/BackgroundOverlay.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/modules/BackgroundOverlay.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/modules/BackgroundOverlay.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] +shell/modules/BackgroundOverlay.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/modules/BackgroundOverlay.qml: Type PanelWindow is not creatable. [uncreatable-type] shell/modules/BacklightModule.qml: Unqualified access [unqualified] shell/modules/Bar.qml: Could not find property "right". [missing-property] +shell/modules/Bar.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/modules/Bar.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/Bar.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/modules/Bar.qml: Type PanelWindow is not creatable. [uncreatable-type] shell/modules/Bar.qml: Type margins is used but it is not resolved [unresolved-type] shell/modules/Bar.qml: unknown grouped property scope margins. [unqualified] shell/modules/BarGroup.qml: Member "screen" not found on type "QObject" [missing-property] +shell/modules/BarGroup.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/modules/BarGroup.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/BarGroup.qml: Type "QColor" of property "base0C" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/modules/BarGroup.qml: Unqualified access [unqualified] shell/modules/BarIcon.qml: Member "accentColor" not found on type "QQuickItem" [missing-property] +shell/modules/BarIcon.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/modules/BarLabel.qml: Member "accentColor" not found on type "QQuickItem" [missing-property] +shell/modules/BarLabel.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/modules/BarModule.qml: Member "accentColor" not found on type "QQuickItem" [missing-property] shell/modules/BarModule.qml: Member "keepOpen" not found on type "QObject" [missing-property] shell/modules/BarModule.qml: Member "screen" not found on type "QObject" [missing-property] +shell/modules/BarModule.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/modules/BarModule.qml: Unqualified access [unqualified] shell/modules/BatteryModule.qml: Unqualified access [unqualified] +shell/modules/BluetoothModule.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/modules/BluetoothModule.qml: Unqualified access [unqualified] shell/modules/ClockModule.qml: Unqualified access [unqualified] shell/modules/CpuModule.qml: Unqualified access [unqualified] +shell/modules/DiskModule.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/DiskModule.qml: Unqualified access [unqualified] shell/modules/GpuModule.qml: Unqualified access [unqualified] shell/modules/HoverPanel.qml: Could not find property "top". [missing-property] shell/modules/HoverPanel.qml: Type PanelWindow is not creatable. [uncreatable-type] 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] shell/modules/NetworkModule.qml: Unqualified access [unqualified] +shell/modules/NotifCard.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/modules/NotifCard.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/modules/NotifCard.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/modules/NotifCard.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/modules/NotifCard.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/modules/NotifCard.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/NotifCard.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] shell/modules/NotifCard.qml: Unqualified access [unqualified] shell/modules/NotifPopup.qml: Could not find property "right". [missing-property] shell/modules/NotifPopup.qml: Could not find property "top". [missing-property] @@ -59,34 +201,75 @@ shell/modules/NotifPopup.qml: Type PanelWindow is not creatable. [uncreatable-ty shell/modules/NotifPopup.qml: Type margins is used but it is not resolved [unresolved-type] shell/modules/NotifPopup.qml: Unqualified access [unqualified] shell/modules/NotifPopup.qml: unknown grouped property scope margins. [unqualified] +shell/modules/NotificationsModule.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/modules/NotificationsModule.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/NotificationsModule.qml: Unqualified access [unqualified] shell/modules/OverviewBackdrop.qml: Type PanelWindow is not creatable. [uncreatable-type] +shell/modules/PopupBackground.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/modules/PopupBackground.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/modules/PowerModule.qml: Unqualified access [unqualified] +shell/modules/PowerProfileModule.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/PowerProfileModule.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/modules/PrivacyModule.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/PrivacyModule.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/modules/ScreenCapture.qml: Type PanelWindow is not creatable. [uncreatable-type] shell/modules/ScreenCorners.qml: Could not find property "right". [missing-property] shell/modules/ScreenCorners.qml: Type PanelWindow is not creatable. [uncreatable-type] 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] shell/modules/TemperatureModule.qml: Unqualified access [unqualified] shell/modules/ThemedIcon.qml: Unqualified access [unqualified] shell/modules/Tooltip.qml: Could not find property "left". [missing-property] shell/modules/Tooltip.qml: Could not find property "top". [missing-property] +shell/modules/Tooltip.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/modules/Tooltip.qml: Type PanelWindow is not creatable. [uncreatable-type] shell/modules/Tooltip.qml: Type margins is used but it is not resolved [unresolved-type] shell/modules/Tooltip.qml: unknown grouped property scope margins. [unqualified] +shell/modules/TooltipState.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/modules/TrayMenu.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/modules/TrayMenu.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/modules/TrayMenu.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/modules/TrayMenu.qml: Unqualified access [unqualified] shell/modules/TrayModule.qml: Member "screen" not found on type "QObject" [missing-property] +shell/modules/TrayModule.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/modules/TrayModule.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/TrayModule.qml: Type "qs::dbus::dbusmenu::DBusMenuHandle" of property "menu" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/modules/TrayModule.qml: Unqualified access [unqualified] +shell/modules/VolumeModule.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/modules/VolumeModule.qml: Unqualified access [unqualified] shell/modules/WeatherModule.qml: Unqualified access [unqualified] shell/modules/WindowTitleModule.qml: Unqualified access [unqualified] shell/modules/WorkspacesModule.qml: Member "screen" not found on type "QObject" [missing-property] +shell/modules/WorkspacesModule.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/modules/WorkspacesModule.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/modules/WorkspacesModule.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/modules/WorkspacesModule.qml: Unqualified access [unqualified] +shell/services/BatteryService.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/services/BatteryService.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/services/BatteryService.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/services/BatteryService.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/services/BluetoothService.qml: Unqualified access [unqualified] +shell/services/CpuService.qml: Type "QList_QString" of property "coreTypes" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/CpuService.qml: Type "QList_QString" of property "cores" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/CpuService.qml: Type "QList_f64" of property "coreMaxFreq" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/CpuService.qml: Type "QList_i32" of property "history" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/services/LockService.qml: Type QProcess::ExitStatus of parameter exitStatus in signal called exited was not found, but is required to compile onExited. Did you add all imports and dependencies? [signal-handler-parameters] shell/services/LockService.qml: Unqualified access [unqualified] shell/services/MprisService.qml: Unqualified access [unqualified] +shell/services/NetworkService.qml: Unqualified access [unqualified] shell/services/PowerProfileService.qml: Unqualified access [unqualified] +shell/services/SystemStats.qml: Type "QList_QString" of property "diskMounts" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/SystemStats.qml: Type "QList_QString" of property "tempDevices" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/SystemStats.qml: Type "QList_i32" of property "gpuHistory" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/SystemStats.qml: Type "QList_i32" of property "memHistory" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/SystemStats.qml: Type "QList_i32" of property "tempHistory" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] +shell/services/ThemeUtil.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/services/ThemeUtil.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/services/ThemeUtil.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/services/WeatherService.qml: Type "QList_QString" of property "weatherArgs" not found. This is likely due to a missing dependency entry or a type not being exposed declaratively. [unresolved-type] shell/shell.qml: Unqualified access [unqualified]