plugin: rust-side modules + theme services with serde-typed config

This commit is contained in:
Damocles 2026-05-04 22:58:12 +02:00
parent a86e90e927
commit f34f3f2f4e
95 changed files with 2477 additions and 1011 deletions

View file

@ -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,
}:
{

View file

@ -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 = {

258
plugin/Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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;
}

View file

@ -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)");
}

View file

@ -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<cxx_qt_lib::QString>;
}
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 `<group>.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<String> {
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<String>,
#[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<QString>,
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::<QString>::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::<serde_json::Value>(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;
}
}

414
plugin/src/theme_service.rs Normal file
View file

@ -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::<serde_json::Value>(&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;
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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, "<br>")
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
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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

View file

@ -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

View file

@ -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
}
}

View file

@ -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

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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
}
}

View file

@ -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()

View file

@ -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

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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: ""

View file

@ -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

View file

@ -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

View file

@ -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
}
}

View file

@ -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 {

View file

@ -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();

View file

@ -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();
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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";

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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, "<br>")
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
}
}
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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
}

View file

@ -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;
}

View file

@ -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: []

View file

@ -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()
}

View file

@ -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

View file

@ -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: ""

View file

@ -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)

View file

@ -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
});
}
}
}

View file

@ -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()
}

View file

@ -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();

View file

@ -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
}

View file

@ -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

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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]