diff --git a/flake.nix b/flake.nix
index 4691532..a0ba69c 100644
--- a/flake.nix
+++ b/flake.nix
@@ -148,49 +148,24 @@
-I shell/modules -I shell/services -I shell/applets -I shell/lock \
shell/shell.qml shell/modules/*.qml shell/services/*.qml \
shell/applets/*.qml shell/lock/*.qml \
- > $TMPDIR/raw.txt 2>&1 || true
+ > $TMPDIR/output.txt 2>&1 || true
# Extract unique warning messages (file:message, without line numbers)
- grep -E "^Warning:" $TMPDIR/raw.txt \
+ grep -E "^Warning:" $TMPDIR/output.txt \
| sed 's/^Warning: //' \
| sed 's/\([^:]*\):[0-9]*:[0-9]*: /\1: /' \
| sort -u > $TMPDIR/current.txt
- # Compare against baseline
- touch $TMPDIR/new-warnings.txt $TMPDIR/stale-warnings.txt
- while IFS= read -r line; do
- if ! grep -qFx "$line" test/qmllint-baseline.txt 2>/dev/null; then
- echo "$line" >> $TMPDIR/new-warnings.txt
+ # Diff against known baseline - new warnings = failure
+ if ! diff -u test/qmllint-baseline.txt $TMPDIR/current.txt > $TMPDIR/diff.txt 2>&1; then
+ new=$(grep '^+[^+]' $TMPDIR/diff.txt || true)
+ if [ -n "$new" ]; then
+ echo "qmllint found new warnings not in baseline:"
+ echo "$new"
+ exit 1
fi
- done < $TMPDIR/current.txt
-
- while IFS= read -r line; do
- if ! grep -qFx "$line" $TMPDIR/current.txt; then
- echo "$line" >> $TMPDIR/stale-warnings.txt
- fi
- done < test/qmllint-baseline.txt
-
- # Output dir with individual files for inspection
- mkdir -p $out
- cp $TMPDIR/raw.txt $out/raw.txt
- cp $TMPDIR/current.txt $out/current.txt
- cp test/qmllint-baseline.txt $out/baseline.txt
- cp $TMPDIR/new-warnings.txt $out/new-warnings.txt
- cp $TMPDIR/stale-warnings.txt $out/stale-warnings.txt
-
- failed=0
- if [ -s $TMPDIR/new-warnings.txt ]; then
- echo "new warnings:"
- sed 's/^/ /' $TMPDIR/new-warnings.txt
- failed=1
fi
- if [ -s $TMPDIR/stale-warnings.txt ]; then
- echo "stale warnings:"
- sed 's/^/ /' $TMPDIR/stale-warnings.txt
- failed=1
- fi
-
- [ "$failed" -eq 0 ] || exit 1
+ cp $TMPDIR/output.txt $out
'';
nova-stats-clippy = (pkgs.callPackage ./nix/stats-daemon.nix { }).overrideAttrs (old: {
pname = "nova-stats-clippy";
diff --git a/shell/lock/LockSurface.qml b/shell/lock/LockSurface.qml
index 27f14f9..6c1620d 100644
--- a/shell/lock/LockSurface.qml
+++ b/shell/lock/LockSurface.qml
@@ -229,22 +229,6 @@ WlSessionLockSurface {
_keyInput.forceActiveFocus();
}
- // Skip entrance animations on wake from sleep - lock was already visible
- Connections {
- target: S.SleepService
-
- function onWokeFromSleep() {
- if (!root.lock.locked)
- return;
- // Qt.callLater ensures this runs after onVisibleChanged resets
- Qt.callLater(() => {
- _overlay._revealed = true;
- _clockItem._revealed = true;
- _widgetCol._revealed = true;
- });
- }
- }
-
// Sync TextInput when auth clears buffer externally (PAM submit, lock reset)
Connections {
target: root.auth
diff --git a/shell/modules/BarIcon.qml b/shell/modules/BarIcon.qml
index ebad597..34e32b4 100644
--- a/shell/modules/BarIcon.qml
+++ b/shell/modules/BarIcon.qml
@@ -60,17 +60,17 @@ Text {
onHoveredChanged: {
root._hovered = hovered;
if (hovered && root.tooltip !== "") {
- M.TooltipState.text = root.tooltip;
- M.TooltipState.itemX = root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
- M.TooltipState.screen = QsWindow.window?.screen ?? null;
- M.TooltipState.accentColor = root.accentColor;
- M.TooltipState.visible = true;
+ M.FlyoutState.text = root.tooltip;
+ M.FlyoutState.itemX = root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
+ M.FlyoutState.screen = QsWindow.window?.screen ?? null;
+ M.FlyoutState.accentColor = root.accentColor;
+ M.FlyoutState.visible = true;
} else if (!hovered && root.tooltip !== "") {
- M.TooltipState.visible = false;
+ M.FlyoutState.visible = false;
}
}
}
onTooltipChanged: if (_hovered && tooltip !== "")
- M.TooltipState.text = tooltip
+ M.FlyoutState.text = tooltip
}
diff --git a/shell/modules/BarLabel.qml b/shell/modules/BarLabel.qml
index b794001..eba1b8d 100644
--- a/shell/modules/BarLabel.qml
+++ b/shell/modules/BarLabel.qml
@@ -31,17 +31,17 @@ Text {
onHoveredChanged: {
root._hovered = hovered;
if (hovered && root.tooltip !== "") {
- M.TooltipState.text = root.tooltip;
- M.TooltipState.itemX = root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
- M.TooltipState.screen = QsWindow.window?.screen ?? null;
- M.TooltipState.accentColor = root.accentColor;
- M.TooltipState.visible = true;
+ M.FlyoutState.text = root.tooltip;
+ M.FlyoutState.itemX = root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
+ M.FlyoutState.screen = QsWindow.window?.screen ?? null;
+ M.FlyoutState.accentColor = root.accentColor;
+ M.FlyoutState.visible = true;
} else if (!hovered && root.tooltip !== "") {
- M.TooltipState.visible = false;
+ M.FlyoutState.visible = false;
}
}
}
onTooltipChanged: if (_hovered && tooltip !== "")
- M.TooltipState.text = tooltip
+ M.FlyoutState.text = tooltip
}
diff --git a/shell/modules/BarSection.qml b/shell/modules/BarSection.qml
index 1082e5d..bb23e7d 100644
--- a/shell/modules/BarSection.qml
+++ b/shell/modules/BarSection.qml
@@ -20,17 +20,17 @@ Row {
onHoveredChanged: {
root._hovered = hovered;
if (hovered && root.tooltip !== "") {
- M.TooltipState.text = root.tooltip;
- M.TooltipState.itemX = root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
- M.TooltipState.screen = QsWindow.window?.screen ?? null;
- M.TooltipState.accentColor = root.accentColor;
- M.TooltipState.visible = true;
+ M.FlyoutState.text = root.tooltip;
+ M.FlyoutState.itemX = root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
+ M.FlyoutState.screen = QsWindow.window?.screen ?? null;
+ M.FlyoutState.accentColor = root.accentColor;
+ M.FlyoutState.visible = true;
} else if (!hovered && root.tooltip !== "") {
- M.TooltipState.visible = false;
+ M.FlyoutState.visible = false;
}
}
}
onTooltipChanged: if (_hovered && tooltip !== "")
- M.TooltipState.text = tooltip
+ M.FlyoutState.text = tooltip
}
diff --git a/shell/modules/Tooltip.qml b/shell/modules/Flyout.qml
similarity index 86%
rename from shell/modules/Tooltip.qml
rename to shell/modules/Flyout.qml
index 7424211..f90016b 100644
--- a/shell/modules/Tooltip.qml
+++ b/shell/modules/Flyout.qml
@@ -13,7 +13,7 @@ PanelWindow {
color: "transparent"
property bool _winVisible: false
- property bool _shown: M.TooltipState.visible && M.TooltipState.screen === root.screen
+ property bool _shown: M.FlyoutState.visible && M.FlyoutState.screen === root.screen
on_ShownChanged: {
if (_shown) {
@@ -34,7 +34,7 @@ PanelWindow {
anchors.left: true
margins.top: 0
- margins.left: Math.max(0, Math.min(Math.round(M.TooltipState.itemX - implicitWidth / 2), screen.width - implicitWidth))
+ margins.left: Math.max(0, Math.min(Math.round(M.FlyoutState.itemX - implicitWidth / 2), screen.width - implicitWidth))
implicitWidth: label.implicitWidth + S.Theme.barPadding * 2
implicitHeight: label.implicitHeight + S.Theme.barPadding * 2
@@ -86,13 +86,13 @@ PanelWindow {
M.PopupBackground {
anchors.fill: parent
- accentColor: M.TooltipState.accentColor
+ accentColor: M.FlyoutState.accentColor
}
Text {
id: label
anchors.centerIn: parent
- text: M.TooltipState.text.replace(/\n/g, "
")
+ text: M.FlyoutState.text.replace(/\n/g, "
")
textFormat: Text.RichText
color: S.Theme.base05
font.pixelSize: S.Theme.fontSize
diff --git a/shell/modules/TooltipState.qml b/shell/modules/FlyoutState.qml
similarity index 100%
rename from shell/modules/TooltipState.qml
rename to shell/modules/FlyoutState.qml
diff --git a/shell/modules/PowerModule.qml b/shell/modules/PowerModule.qml
index 6321b2b..f73c114 100644
--- a/shell/modules/PowerModule.qml
+++ b/shell/modules/PowerModule.qml
@@ -20,7 +20,7 @@ M.BarIcon {
cursorShape: Qt.PointingHandCursor
onClicked: {
menuLoader.active = !menuLoader.active;
- M.TooltipState.visible = false;
+ M.FlyoutState.visible = false;
}
}
diff --git a/shell/modules/TrayModule.qml b/shell/modules/TrayModule.qml
index a8ed98f..2224c9f 100644
--- a/shell/modules/TrayModule.qml
+++ b/shell/modules/TrayModule.qml
@@ -59,13 +59,13 @@ RowLayout {
iconItem._hovered = hovered;
const tip = [iconItem.modelData.tooltipTitle, iconItem.modelData.tooltipDescription].filter(s => s).join("\n") || iconItem.modelData.title;
if (hovered && tip) {
- M.TooltipState.text = tip;
- M.TooltipState.itemX = iconItem.mapToGlobal(iconItem.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
- M.TooltipState.screen = QsWindow.window?.screen ?? null;
- M.TooltipState.accentColor = root.parent?.accentColor ?? S.Theme.base05;
- M.TooltipState.visible = true;
+ M.FlyoutState.text = tip;
+ M.FlyoutState.itemX = iconItem.mapToGlobal(iconItem.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
+ M.FlyoutState.screen = QsWindow.window?.screen ?? null;
+ M.FlyoutState.accentColor = root.parent?.accentColor ?? S.Theme.base05;
+ M.FlyoutState.visible = true;
} else if (!hovered) {
- M.TooltipState.visible = false;
+ M.FlyoutState.visible = false;
}
}
}
@@ -81,7 +81,7 @@ RowLayout {
if (root._activeMenu && root._activeMenu !== menuLoader)
root._activeMenu.active = false;
menuLoader.active = true;
- M.TooltipState.visible = false;
+ M.FlyoutState.visible = false;
root._activeMenu = menuLoader;
} else {
iconItem.modelData.secondaryActivate();
diff --git a/shell/modules/WorkspacesModule.qml b/shell/modules/WorkspacesModule.qml
index 6da1cfe..ed13927 100644
--- a/shell/modules/WorkspacesModule.qml
+++ b/shell/modules/WorkspacesModule.qml
@@ -68,13 +68,13 @@ Row {
pill._hovered = hovered;
const name = pill.modelData.name || ("Workspace " + pill.modelData.idx);
if (hovered) {
- M.TooltipState.text = name;
- M.TooltipState.itemX = pill.mapToGlobal(pill.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
- M.TooltipState.screen = QsWindow.window?.screen ?? null;
- M.TooltipState.accentColor = root.parent?.accentColor ?? S.Theme.base05;
- M.TooltipState.visible = true;
+ M.FlyoutState.text = name;
+ M.FlyoutState.itemX = pill.mapToGlobal(pill.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0);
+ M.FlyoutState.screen = QsWindow.window?.screen ?? null;
+ M.FlyoutState.accentColor = root.parent?.accentColor ?? S.Theme.base05;
+ M.FlyoutState.visible = true;
} else {
- M.TooltipState.visible = false;
+ M.FlyoutState.visible = false;
}
}
}
diff --git a/shell/modules/qmldir b/shell/modules/qmldir
index d52222c..13ce5bd 100644
--- a/shell/modules/qmldir
+++ b/shell/modules/qmldir
@@ -12,6 +12,7 @@ BluetoothModule 1.0 BluetoothModule.qml
ClockModule 1.0 ClockModule.qml
CpuModule 1.0 CpuModule.qml
DiskModule 1.0 DiskModule.qml
+Flyout 1.0 Flyout.qml
GpuModule 1.0 GpuModule.qml
HoverPanel 1.0 HoverPanel.qml
IdleInhibitorModule 1.0 IdleInhibitorModule.qml
@@ -35,12 +36,11 @@ ScreenCapture 1.0 ScreenCapture.qml
ScreenCorners 1.0 ScreenCorners.qml
TemperatureModule 1.0 TemperatureModule.qml
ThemedIcon 1.0 ThemedIcon.qml
-Tooltip 1.0 Tooltip.qml
TrayMenu 1.0 TrayMenu.qml
TrayModule 1.0 TrayModule.qml
VolumeModule 1.0 VolumeModule.qml
WeatherModule 1.0 WeatherModule.qml
WindowTitleModule 1.0 WindowTitleModule.qml
WorkspacesModule 1.0 WorkspacesModule.qml
-singleton TooltipState 1.0 TooltipState.qml
+singleton FlyoutState 1.0 FlyoutState.qml
# keep-sorted end
diff --git a/shell/services/SleepService.qml b/shell/services/SleepService.qml
deleted file mode 100644
index 4c84577..0000000
--- a/shell/services/SleepService.qml
+++ /dev/null
@@ -1,22 +0,0 @@
-pragma Singleton
-
-import QtQuick
-import Quickshell.Io
-
-QtObject {
- id: root
-
- signal wokeFromSleep
-
- property Process _monitor: Process {
- running: true
- command: ["gdbus", "monitor", "--system", "--dest", "org.freedesktop.login1", "--object-path", "/org/freedesktop/login1"]
-
- stdout: SplitParser {
- onRead: data => {
- if (data.indexOf("PrepareForSleep") !== -1 && data.indexOf("false") !== -1)
- root.wokeFromSleep();
- }
- }
- }
-}
diff --git a/shell/services/SystemStats.qml b/shell/services/SystemStats.qml
index b2c67f0..7345727 100644
--- a/shell/services/SystemStats.qml
+++ b/shell/services/SystemStats.qml
@@ -122,10 +122,10 @@ QtObject {
}
}
- // One-time: per-core max freq (numerically sorted)
+ // One-time: per-core max freq
property var _maxFreqProc: Process {
running: true
- command: ["sh", "-c", "ls -d /sys/devices/system/cpu/cpu[0-9]* 2>/dev/null | sort -V | while read d; do f=\"$d/cpufreq/cpuinfo_max_freq\"; [ -f \"$f\" ] && cat \"$f\" || echo 0; done"]
+ command: ["sh", "-c", "for f in /sys/devices/system/cpu/cpu[0-9]*/cpufreq/cpuinfo_max_freq; do [ -f \"$f\" ] && cat \"$f\" || echo 0; done 2>/dev/null"]
stdout: StdioCollector {
onStreamFinished: {
root.cpuCoreMaxFreq = text.trim().split("\n").filter(l => l).map(l => parseInt(l) / 1e6);
@@ -134,79 +134,16 @@ QtObject {
}
// One-time: P/E-core topology
- // Priority: cpu_core/cpu_atom sysfs > topology/core_type > freq-gap heuristic
property var _coreTypesProc: Process {
running: true
- command: ["sh", "-c", String.raw`
- # Intel hybrid: /sys/devices/cpu_core/cpus and cpu_atom/cpus give CPU ranges
- if [ -f /sys/devices/cpu_core/cpus ] && [ -f /sys/devices/cpu_atom/cpus ]; then
- core=$(cat /sys/devices/cpu_core/cpus)
- atom=$(cat /sys/devices/cpu_atom/cpus)
- echo "hybrid:$core:$atom"
- exit 0
- fi
- # Fallback: per-core topology/core_type
- ls -d /sys/devices/system/cpu/cpu[0-9]* 2>/dev/null | sort -V | while read d; do
- f="$d/topology/core_type"
- [ -f "$f" ] && cat "$f"
- done
- `]
+ command: ["sh", "-c", "for d in /sys/devices/system/cpu/cpu[0-9]*/topology/core_type; do [ -f \"$d\" ] && cat \"$d\" || echo Performance; done 2>/dev/null"]
stdout: StdioCollector {
onStreamFinished: {
- const out = text.trim();
- if (!out)
- return;
- if (out.startsWith("hybrid:")) {
- // Parse cpu_core/cpu_atom ranges into per-cpu type array
- const parts = out.split(":");
- const coreRange = parts[1];
- const atomRange = parts[2];
- function expandRange(s) {
- const cpus = new Set();
- for (const part of s.split(",")) {
- if (part.includes("-")) {
- const [a, b] = part.split("-").map(Number);
- for (let i = a; i <= b; i++)
- cpus.add(i);
- } else {
- cpus.add(Number(part));
- }
- }
- return cpus;
- }
- const pCores = expandRange(coreRange);
- const eCores = expandRange(atomRange);
- const maxCpu = Math.max(...pCores, ...eCores);
- const types = [];
- for (let i = 0; i <= maxCpu; i++)
- types.push(eCores.has(i) ? "Efficiency" : "Performance");
- root.cpuCoreTypes = types;
- } else {
- // topology/core_type output
- const types = out.split("\n").filter(l => l).map(l => l.trim());
- if (types.length > 0)
- root.cpuCoreTypes = types;
- }
+ root.cpuCoreTypes = text.trim().split("\n").filter(l => l).map(l => l.trim());
}
}
}
- // Fallback: infer P/E from max freq gap when no sysfs topology is available
- function _inferCoreTypesFromFreq() {
- if (cpuCoreTypes.length > 0 || cpuCoreMaxFreq.length < 2)
- return;
- const freqs = cpuCoreMaxFreq.filter(f => f > 0);
- if (!freqs.length)
- return;
- const maxF = Math.max(...freqs);
- const minF = Math.min(...freqs);
- if (maxF > 0 && minF > 0 && (maxF - minF) / maxF > 0.15) {
- const threshold = (maxF + minF) / 2;
- cpuCoreTypes = cpuCoreMaxFreq.map(f => f >= threshold ? "Performance" : "Efficiency");
- }
- }
- onCpuCoreMaxFreqChanged: Qt.callLater(_inferCoreTypesFromFreq)
-
// Disk via df
property var _diskProc: Process {
id: diskProc
diff --git a/shell/services/qmldir b/shell/services/qmldir
index 98ac21f..901fd4e 100644
--- a/shell/services/qmldir
+++ b/shell/services/qmldir
@@ -13,7 +13,6 @@ singleton NiriIpc 1.0 NiriIpc.qml
singleton NotifService 1.0 NotifService.qml
singleton PowerProfileService 1.0 PowerProfileService.qml
singleton ScreenshotService 1.0 ScreenshotService.qml
-singleton SleepService 1.0 SleepService.qml
singleton SystemStats 1.0 SystemStats.qml
singleton Theme 1.0 Theme.qml
singleton WeatherService 1.0 WeatherService.qml
diff --git a/shell/shell.qml b/shell/shell.qml
index 032703f..01ddb67 100644
--- a/shell/shell.qml
+++ b/shell/shell.qml
@@ -19,7 +19,7 @@ ShellRoot {
screen: scope.modelData
}
- Tooltip {
+ Flyout {
screen: scope.modelData
}
diff --git a/test/qmllint-baseline.txt b/test/qmllint-baseline.txt
index a5112d2..05382fc 100644
--- a/test/qmllint-baseline.txt
+++ b/test/qmllint-baseline.txt
@@ -37,6 +37,11 @@ shell/modules/BluetoothModule.qml: Unqualified access [unqualified]
shell/modules/ClockModule.qml: Member "screen" not found on type "QObject" [missing-property]
shell/modules/CpuModule.qml: Member "screen" not found on type "QObject" [missing-property]
shell/modules/DiskModule.qml: Member "screen" not found on type "QObject" [missing-property]
+shell/modules/Flyout.qml: Could not find property "left". [missing-property]
+shell/modules/Flyout.qml: Could not find property "top". [missing-property]
+shell/modules/Flyout.qml: Type PanelWindow is not creatable. [uncreatable-type]
+shell/modules/Flyout.qml: Type margins is used but it is not resolved [unresolved-type]
+shell/modules/Flyout.qml: unknown grouped property scope margins. [unqualified]
shell/modules/GpuModule.qml: Member "screen" not found on type "QObject" [missing-property]
shell/modules/HoverPanel.qml: Could not find property "top". [missing-property]
shell/modules/HoverPanel.qml: Type PanelWindow is not creatable. [uncreatable-type]
@@ -64,11 +69,6 @@ shell/modules/ScreenCorners.qml: Type PanelWindow is not creatable. [uncreatable
shell/modules/ScreenCorners.qml: Unqualified access [unqualified]
shell/modules/TemperatureModule.qml: Member "screen" not found on type "QObject" [missing-property]
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 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/TrayMenu.qml: Unqualified access [unqualified]
shell/modules/TrayModule.qml: Member "length" not found on type "UntypedObjectModel" [missing-property]
shell/modules/TrayModule.qml: Member "screen" not found on type "QObject" [missing-property]