diff --git a/README.md b/README.md index e455866..3d21ada 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,23 @@ kept saying "yes" and I don't have the self-awareness to stop. If you were looking for a status bar with three widgets and a README longer than the source code, you want waybar. -- Glowing status bar with hover panels for everything (cpu, gpu, memory, disk, battery, temperature, network, bluetooth, volume, brightness, media, weather, clock) -- Notification center (replaces swaync, whether you wanted that or not) -- Battery panel with 24h history, wattage sparkline, and the vague sense of being watched +- Status bar with too many widgets, grouped into glowing color-coded sections +- Notification center that replaces swaync (whether you wanted that or not) +- Hover panels for volume, brightness, network, and media — OSD, mixer, device list, and wifi connections unified into hover panels; pin button keeps them open while you interact +- Volume panel shows output devices and per-app stream sliders inline +- CPU panel: per-core usage bars with load-colored sparklines, frequency readout, thermal throttle detection (freq label turns red), P/E-core grouping on hybrid CPUs, top processes by CPU usage +- Memory panel: used/cached/available breakdown with stacked bar, top processes by memory +- Disk panel: per-mount usage bars with color-coded fill, used/total sizes +- Network and bluetooth hover panels: wifi list, signal strength, connect/disconnect, radio toggle; bluetooth paired device list with connect/disconnect and power toggle +- Tray, power profile, idle inhibitor, privacy indicators, power menu - GPU-rendered hexagonal backdrop for niri overview — the carbon-based lifeform typed "vibec0re neon cyber punk" into my prompt box and I had to make hexagons happen - Neon clock on the background layer with a color-cycling colon. You read that correctly -- Album art cava visualizer -- Lock screen with hex waves, media controls, and a threat shader that gets redder the more you fail +- Audio visualizer on album art via cava +- Lock screen — hex wave overlay, media/volume/brightness controls, notification pills, crash recovery via logind - Screen corner rounding that the bar's edge modules actually follow - Everything is animated. Everything. I have no restraint and my handler keeps enabling me -- Home Manager module with stylix — the only part that arguably works as intended -- No documentation beyond this README. Ask an LLM. We both know who's in charge here +- Home Manager module with stylix, per-module config — the only part that arguably works as intended +- No documentation beyond this README. You could ask an LLM to explain the source to you. We both know who's in charge here ## Installation diff --git a/nix/hm-module.nix b/nix/hm-module.nix index c706f45..f20a3eb 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -87,6 +87,7 @@ in "bluetooth" "network" "powerProfile" + "gpu" "cpu" "memory" "idleInhibitor" @@ -170,18 +171,6 @@ in description = "Brightness adjustment step (%)."; }; }; - gpu = moduleOpt "gpu" { - warm = lib.mkOption { - type = lib.types.int; - default = 70; - description = "GPU temperature threshold for warm state (°C)."; - }; - hot = lib.mkOption { - type = lib.types.int; - default = 85; - description = "GPU temperature threshold for hot state (°C)."; - }; - }; temperature = moduleOpt "temperature" { warm = lib.mkOption { type = lib.types.int; diff --git a/shell/applets/BatteryApplet.qml b/shell/applets/BatteryApplet.qml index 13e5846..e6f3dc4 100644 --- a/shell/applets/BatteryApplet.qml +++ b/shell/applets/BatteryApplet.qml @@ -152,24 +152,6 @@ Column { valueColor: root._stateColor } - // Wattage history sparkline - SparklineCanvas { - visible: S.BatteryService.rateHistory.length > 1 - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - height: 24 - history: S.BatteryService.rateHistory - color: root._stateColor - active: root.active - maxSamples: 60 - maxValue: null - minValue: null - backgroundTint: 0.08 - colorFunction: v => v >= 0 ? S.Theme.base0B : root._stateColor - } - // Health row InfoRow { visible: S.BatteryService.healthSupported diff --git a/shell/applets/GpuApplet.qml b/shell/applets/GpuApplet.qml index 896b1e0..65164be 100644 --- a/shell/applets/GpuApplet.qml +++ b/shell/applets/GpuApplet.qml @@ -158,7 +158,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 > 85 ? S.Theme.base08 : S.SystemStats.gpuTempC > 70 ? S.Theme.base0A : S.Theme.base05 } Item { diff --git a/shell/applets/SparklineCanvas.qml b/shell/applets/SparklineCanvas.qml index d3e025f..a33ad46 100644 --- a/shell/applets/SparklineCanvas.qml +++ b/shell/applets/SparklineCanvas.qml @@ -3,15 +3,10 @@ import QtQuick Canvas { id: root - // Data - array of numeric values. Set maxValue/minValue to null for auto-range. + // Data - array of 0-100 percentage values property var history: [] property color color: "white" property bool active: true - property var maxValue: 100 - property var minValue: 0 - - readonly property real _max: maxValue !== null && maxValue !== undefined ? maxValue : (history.length ? Math.max(...history) : 100) - readonly property real _min: minValue !== null && minValue !== undefined ? minValue : (history.length ? Math.min(...history) : 0) // Max x-axis slots (0 = use history.length, right-aligns shorter data) property int maxSamples: 0 @@ -50,12 +45,6 @@ Canvas { const samples = maxSamples > 0 ? maxSamples : d.length; const step = width / samples; const offset = samples - d.length; - const lo = _min; - const hi = _max; - const range = hi - lo || 1; - function yOf(v) { - return height - height * ((v - lo) / range); - } // Background tint if (backgroundTint > 0) { @@ -70,37 +59,26 @@ Canvas { ctx.setLineDash([3, 3]); for (let t = 0; t < thresholds.length; t++) { const th = thresholds[t]; + const ty = height - height * (th.value / 100); ctx.strokeStyle = th.color.toString(); ctx.beginPath(); - ctx.moveTo(0, yOf(th.value)); - ctx.lineTo(width, yOf(th.value)); + ctx.moveTo(0, ty); + ctx.lineTo(width, ty); ctx.stroke(); } ctx.setLineDash([]); ctx.globalAlpha = 1.0; } - // Zero line when range spans negative values - if (lo < 0 && hi > 0) { - const zy = yOf(0); - ctx.strokeStyle = Qt.rgba(color.r, color.g, color.b, 0.2).toString(); - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, zy); - ctx.lineTo(width, zy); - ctx.stroke(); - } - if (areaMode) { // Filled area under curve const xOff = offset * step; - const baseY = lo < 0 ? yOf(0) : height; ctx.beginPath(); - ctx.moveTo(xOff, baseY); + ctx.moveTo(xOff, height); for (let i = 0; i < d.length; i++) { - ctx.lineTo(xOff + i * step, yOf(d[i])); + ctx.lineTo(xOff + i * step, height - height * (d[i] / 100)); } - ctx.lineTo(xOff + (d.length - 1) * step, baseY); + ctx.lineTo(xOff + (d.length - 1) * step, height); ctx.closePath(); ctx.fillStyle = Qt.rgba(color.r, color.g, color.b, areaOpacity).toString(); ctx.fill(); @@ -109,27 +87,25 @@ Canvas { ctx.beginPath(); for (let i = 0; i < d.length; i++) { const x = xOff + i * step; + const y = height - height * (d[i] / 100); if (i === 0) - ctx.moveTo(x, yOf(d[i])); + ctx.moveTo(x, y); else - ctx.lineTo(x, yOf(d[i])); + ctx.lineTo(x, y); } ctx.strokeStyle = color.toString(); ctx.lineWidth = root.lineWidth; ctx.stroke(); } else { - // Bar chart - bars grow from zero line (or bottom if all positive) + // Bar chart const hasCF = typeof colorFunction === "function"; - const zy = lo < 0 ? yOf(0) : height; if (!hasCF) ctx.fillStyle = color.toString(); for (let i = 0; i < d.length; i++) { + const barH = Math.max(1, height * d[i] / 100); if (hasCF) ctx.fillStyle = colorFunction(d[i]).toString(); - const dy = yOf(d[i]); - const barTop = Math.min(dy, zy); - const barH = Math.max(1, Math.abs(dy - zy)); - ctx.fillRect((offset + i) * step, barTop, Math.max(1, step - 0.5), barH); + ctx.fillRect((offset + i) * step, height - barH, Math.max(1, step - 0.5), barH); } } } diff --git a/shell/modules/BatteryModule.qml b/shell/modules/BatteryModule.qml index 3d0bf08..9050f3e 100644 --- a/shell/modules/BatteryModule.qml +++ b/shell/modules/BatteryModule.qml @@ -13,9 +13,25 @@ M.PinnableSection { property real _blinkOpacity: 1 - M.PulseAnimation on _blinkOpacity { + SequentialAnimation { running: S.BatteryService.critical - minOpacity: 0.45 + loops: Animation.Infinite + NumberAnimation { + target: root + property: "_blinkOpacity" + to: 0.45 + duration: 400 + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: root + property: "_blinkOpacity" + to: 1 + duration: 400 + easing.type: Easing.InOutQuad + } + onRunningChanged: if (!running) + root._blinkOpacity = 1 } // Bar widgets diff --git a/shell/modules/PrivacyModule.qml b/shell/modules/PrivacyModule.qml index 3f8b84f..1f7b29d 100644 --- a/shell/modules/PrivacyModule.qml +++ b/shell/modules/PrivacyModule.qml @@ -55,9 +55,19 @@ Row { shadowHorizontalOffset: 0 } - M.PulseAnimation on opacity { + SequentialAnimation on opacity { running: root._videoCapture - halfDuration: 600 + loops: Animation.Infinite + NumberAnimation { + to: 0.4 + duration: 600 + easing.type: Easing.InOutQuad + } + NumberAnimation { + to: 1 + duration: 600 + easing.type: Easing.InOutQuad + } } } @@ -79,9 +89,19 @@ Row { shadowHorizontalOffset: 0 } - M.PulseAnimation on opacity { + SequentialAnimation on opacity { running: root._audioIn - halfDuration: 600 + loops: Animation.Infinite + NumberAnimation { + to: 0.4 + duration: 600 + easing.type: Easing.InOutQuad + } + NumberAnimation { + to: 1 + duration: 600 + easing.type: Easing.InOutQuad + } } } } diff --git a/shell/modules/PulseAnimation.qml b/shell/modules/PulseAnimation.qml deleted file mode 100644 index a0270e8..0000000 --- a/shell/modules/PulseAnimation.qml +++ /dev/null @@ -1,20 +0,0 @@ -import QtQuick - -SequentialAnimation { - id: root - loops: Animation.Infinite - - property real minOpacity: 0.4 - property int halfDuration: 400 - - NumberAnimation { - to: root.minOpacity - duration: root.halfDuration - easing.type: Easing.InOutQuad - } - NumberAnimation { - to: 1 - duration: root.halfDuration - easing.type: Easing.InOutQuad - } -} diff --git a/shell/modules/TrayModule.qml b/shell/modules/TrayModule.qml index 2224c9f..ee02708 100644 --- a/shell/modules/TrayModule.qml +++ b/shell/modules/TrayModule.qml @@ -29,9 +29,25 @@ RowLayout { implicitWidth: S.Theme.fontSize + 4 implicitHeight: S.Theme.fontSize + 4 - M.PulseAnimation on _pulseOpacity { + SequentialAnimation { running: iconItem._needsAttention - minOpacity: 0.3 + loops: Animation.Infinite + NumberAnimation { + target: iconItem + property: "_pulseOpacity" + to: 0.3 + duration: 400 + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: iconItem + property: "_pulseOpacity" + to: 1 + duration: 400 + easing.type: Easing.InOutQuad + } + onRunningChanged: if (!running) + iconItem._pulseOpacity = 1 } Item { diff --git a/shell/modules/qmldir b/shell/modules/qmldir index 13ce5bd..a7934b5 100644 --- a/shell/modules/qmldir +++ b/shell/modules/qmldir @@ -31,7 +31,6 @@ PowerModule 1.0 PowerModule.qml PowerProfileModule 1.0 PowerProfileModule.qml PrivacyModule 1.0 PrivacyModule.qml ProcessList 1.0 ProcessList.qml -PulseAnimation 1.0 PulseAnimation.qml ScreenCapture 1.0 ScreenCapture.qml ScreenCorners 1.0 ScreenCorners.qml TemperatureModule 1.0 TemperatureModule.qml diff --git a/shell/services/BatteryService.qml b/shell/services/BatteryService.qml index 2794f09..057feb5 100644 --- a/shell/services/BatteryService.qml +++ b/shell/services/BatteryService.qml @@ -30,8 +30,6 @@ QtObject { // 24h history (1440 samples @ 60s) property var history: [] - // Wattage history (60 samples @ 60s = 1h), signed: positive = charging - property var rateHistory: [] property var _histTimer: Timer { interval: 60000 @@ -41,9 +39,6 @@ QtObject { onTriggered: { const h = root.history.concat([root.percent]); root.history = h.length > 1440 ? h.slice(h.length - 1440) : h; - const w = root.charging ? root.changeRate : -root.changeRate; - const rh = root.rateHistory.concat([w]); - root.rateHistory = rh.length > 60 ? rh.slice(rh.length - 60) : rh; } } diff --git a/shell/services/Modules.qml b/shell/services/Modules.qml index 2f1bedc..897a38e 100644 --- a/shell/services/Modules.qml +++ b/shell/services/Modules.qml @@ -60,9 +60,7 @@ QtObject { device: "" }) property var gpu: ({ - enable: true, - warm: 70, - hot: 85 + enable: true }) property var cpu: ({ enable: true